mirror of
https://github.com/luau-lang/luau.git
synced 2025-01-08 12:29:09 +00:00
551a43c424
- Updated Roblox copyright to 2023 - Floor division operator `//` (implements #832) - Autocomplete now shows `end` within `do` blocks - Restore BraceType when using `Lexer::lookahead` (fixes #1019) # New typechecker - Subtyping tests between metatables and tables - Subtyping tests between string singletons and tables - Subtyping tests for class types # Native codegen - Fixed macOS test failure (wrong spill restore offset) - Fixed clobbering of non-volatile xmm registers on Windows - Fixed wrong storage location of SSA reg spills - Implemented A64 support for add/sub extended - Eliminated zextReg from A64 lowering - Remove identical table slot lookups - Propagate values from predecessor into the linear block - Disabled reuse slot optimization - Keep `LuaNode::val` check for nil when optimizing `CHECK_SLOT_MATCH` - Implemented IR translation of `table.insert` builtin - Fixed mmap error handling on macOS/Linux # Tooling - Used `|` as a column separator instead of `+` in `bench.py` - Added a `table.sort` micro-benchmark - Switched `libprotobuf-mutator` to a less problematic version
288 lines
8.7 KiB
C++
288 lines
8.7 KiB
C++
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
|
#include "Luau/Config.h"
|
|
|
|
#include "Luau/Lexer.h"
|
|
#include "Luau/StringUtils.h"
|
|
|
|
LUAU_FASTFLAG(LuauFloorDivision)
|
|
namespace Luau
|
|
{
|
|
|
|
using Error = std::optional<std::string>;
|
|
|
|
Config::Config()
|
|
{
|
|
enabledLint.setDefaults();
|
|
}
|
|
|
|
static Error parseBoolean(bool& result, const std::string& value)
|
|
{
|
|
if (value == "true")
|
|
result = true;
|
|
else if (value == "false")
|
|
result = false;
|
|
else
|
|
return Error{"Bad setting '" + value + "'. Valid options are true and false"};
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
Error parseModeString(Mode& mode, const std::string& modeString, bool compat)
|
|
{
|
|
if (modeString == "nocheck")
|
|
mode = Mode::NoCheck;
|
|
else if (modeString == "strict")
|
|
mode = Mode::Strict;
|
|
else if (modeString == "nonstrict")
|
|
mode = Mode::Nonstrict;
|
|
else if (modeString == "noinfer" && compat)
|
|
mode = Mode::NoCheck;
|
|
else
|
|
return Error{"Bad mode \"" + modeString + "\". Valid options are nocheck, nonstrict, and strict"};
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
static Error parseLintRuleStringForCode(
|
|
LintOptions& enabledLints, LintOptions& fatalLints, LintWarning::Code code, const std::string& value, bool compat)
|
|
{
|
|
if (value == "true")
|
|
{
|
|
enabledLints.enableWarning(code);
|
|
}
|
|
else if (value == "false")
|
|
{
|
|
enabledLints.disableWarning(code);
|
|
}
|
|
else if (compat)
|
|
{
|
|
if (value == "enabled")
|
|
{
|
|
enabledLints.enableWarning(code);
|
|
fatalLints.disableWarning(code);
|
|
}
|
|
else if (value == "disabled")
|
|
{
|
|
enabledLints.disableWarning(code);
|
|
fatalLints.disableWarning(code);
|
|
}
|
|
else if (value == "fatal")
|
|
{
|
|
enabledLints.enableWarning(code);
|
|
fatalLints.enableWarning(code);
|
|
}
|
|
else
|
|
{
|
|
return Error{"Bad setting '" + value + "'. Valid options are enabled, disabled, and fatal"};
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return Error{"Bad setting '" + value + "'. Valid options are true and false"};
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
Error parseLintRuleString(LintOptions& enabledLints, LintOptions& fatalLints, const std::string& warningName, const std::string& value, bool compat)
|
|
{
|
|
if (warningName == "*")
|
|
{
|
|
for (int code = LintWarning::Code_Unknown; code < LintWarning::Code__Count; ++code)
|
|
{
|
|
if (auto err = parseLintRuleStringForCode(enabledLints, fatalLints, LintWarning::Code(code), value, compat))
|
|
return Error{"In key " + warningName + ": " + *err};
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LintWarning::Code code = LintWarning::parseName(warningName.c_str());
|
|
|
|
if (code == LintWarning::Code_Unknown)
|
|
return Error{"Unknown lint " + warningName};
|
|
|
|
if (auto err = parseLintRuleStringForCode(enabledLints, fatalLints, code, value, compat))
|
|
return Error{"In key " + warningName + ": " + *err};
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
static void next(Lexer& lexer)
|
|
{
|
|
lexer.next();
|
|
|
|
// skip C-style comments as Lexer only understands Lua-style comments atm
|
|
|
|
if (FFlag::LuauFloorDivision)
|
|
{
|
|
while (lexer.current().type == Luau::Lexeme::FloorDiv)
|
|
lexer.nextline();
|
|
}
|
|
else
|
|
{
|
|
while (lexer.current().type == '/')
|
|
{
|
|
Lexeme peek = lexer.lookahead();
|
|
|
|
if (peek.type != '/' || peek.location.begin != lexer.current().location.end)
|
|
break;
|
|
|
|
lexer.nextline();
|
|
}
|
|
}
|
|
}
|
|
|
|
static Error fail(Lexer& lexer, const char* message)
|
|
{
|
|
Lexeme cur = lexer.current();
|
|
|
|
return format("Expected %s at line %d, got %s instead", message, cur.location.begin.line + 1, cur.toString().c_str());
|
|
}
|
|
|
|
template<typename Action>
|
|
static Error parseJson(const std::string& contents, Action action)
|
|
{
|
|
Allocator allocator;
|
|
AstNameTable names(allocator);
|
|
Lexer lexer(contents.data(), contents.size(), names);
|
|
next(lexer);
|
|
|
|
std::vector<std::string> keys;
|
|
bool arrayTop = false; // we don't support nested arrays
|
|
|
|
if (lexer.current().type != '{')
|
|
return fail(lexer, "'{'");
|
|
next(lexer);
|
|
|
|
for (;;)
|
|
{
|
|
if (arrayTop)
|
|
{
|
|
if (lexer.current().type == ']')
|
|
{
|
|
next(lexer);
|
|
arrayTop = false;
|
|
|
|
LUAU_ASSERT(!keys.empty());
|
|
keys.pop_back();
|
|
|
|
if (lexer.current().type == ',')
|
|
next(lexer);
|
|
else if (lexer.current().type != '}')
|
|
return fail(lexer, "',' or '}'");
|
|
}
|
|
else if (lexer.current().type == Lexeme::QuotedString)
|
|
{
|
|
std::string value(lexer.current().data, lexer.current().length);
|
|
next(lexer);
|
|
|
|
if (Error err = action(keys, value))
|
|
return err;
|
|
|
|
if (lexer.current().type == ',')
|
|
next(lexer);
|
|
else if (lexer.current().type != ']')
|
|
return fail(lexer, "',' or ']'");
|
|
}
|
|
else
|
|
return fail(lexer, "array element or ']'");
|
|
}
|
|
else
|
|
{
|
|
if (lexer.current().type == '}')
|
|
{
|
|
next(lexer);
|
|
|
|
if (keys.empty())
|
|
{
|
|
if (lexer.current().type != Lexeme::Eof)
|
|
return fail(lexer, "end of file");
|
|
|
|
return {};
|
|
}
|
|
else
|
|
keys.pop_back();
|
|
|
|
if (lexer.current().type == ',')
|
|
next(lexer);
|
|
else if (lexer.current().type != '}')
|
|
return fail(lexer, "',' or '}'");
|
|
}
|
|
else if (lexer.current().type == Lexeme::QuotedString)
|
|
{
|
|
std::string key(lexer.current().data, lexer.current().length);
|
|
next(lexer);
|
|
|
|
keys.push_back(key);
|
|
|
|
if (lexer.current().type != ':')
|
|
return fail(lexer, "':'");
|
|
next(lexer);
|
|
|
|
if (lexer.current().type == '{' || lexer.current().type == '[')
|
|
{
|
|
arrayTop = (lexer.current().type == '[');
|
|
next(lexer);
|
|
}
|
|
else if (lexer.current().type == Lexeme::QuotedString || lexer.current().type == Lexeme::ReservedTrue ||
|
|
lexer.current().type == Lexeme::ReservedFalse)
|
|
{
|
|
std::string value = lexer.current().type == Lexeme::QuotedString
|
|
? std::string(lexer.current().data, lexer.current().length)
|
|
: (lexer.current().type == Lexeme::ReservedTrue ? "true" : "false");
|
|
next(lexer);
|
|
|
|
if (Error err = action(keys, value))
|
|
return err;
|
|
|
|
keys.pop_back();
|
|
|
|
if (lexer.current().type == ',')
|
|
next(lexer);
|
|
else if (lexer.current().type != '}')
|
|
return fail(lexer, "',' or '}'");
|
|
}
|
|
else
|
|
return fail(lexer, "field value");
|
|
}
|
|
else
|
|
return fail(lexer, "field key");
|
|
}
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
Error parseConfig(const std::string& contents, Config& config, bool compat)
|
|
{
|
|
return parseJson(contents, [&](const std::vector<std::string>& keys, const std::string& value) -> Error {
|
|
if (keys.size() == 1 && keys[0] == "languageMode")
|
|
return parseModeString(config.mode, value, compat);
|
|
else if (keys.size() == 2 && keys[0] == "lint")
|
|
return parseLintRuleString(config.enabledLint, config.fatalLint, keys[1], value, compat);
|
|
else if (keys.size() == 1 && keys[0] == "lintErrors")
|
|
return parseBoolean(config.lintErrors, value);
|
|
else if (keys.size() == 1 && keys[0] == "typeErrors")
|
|
return parseBoolean(config.typeErrors, value);
|
|
else if (keys.size() == 1 && keys[0] == "globals")
|
|
{
|
|
config.globals.push_back(value);
|
|
return std::nullopt;
|
|
}
|
|
else if (compat && keys.size() == 2 && keys[0] == "language" && keys[1] == "mode")
|
|
return parseModeString(config.mode, value, compat);
|
|
else
|
|
{
|
|
std::vector<std::string_view> keysv(keys.begin(), keys.end());
|
|
return "Unknown key " + join(keysv, "/");
|
|
}
|
|
});
|
|
}
|
|
|
|
const Config& NullConfigResolver::getConfig(const ModuleName& name) const
|
|
{
|
|
return defaultConfig;
|
|
}
|
|
|
|
} // namespace Luau
|