Fix string interpolation autocomplete

This commit is contained in:
Kampfkarren 2022-11-15 13:12:39 -08:00
parent c32cdd5c42
commit eb53fa5bec
5 changed files with 36 additions and 37 deletions

View file

@ -1,6 +1,7 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details // This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/AstQuery.h" #include "Luau/AstQuery.h"
#include "Luau/Ast.h"
#include "Luau/Module.h" #include "Luau/Module.h"
#include "Luau/Scope.h" #include "Luau/Scope.h"
#include "Luau/TypeInfer.h" #include "Luau/TypeInfer.h"

View file

@ -1227,11 +1227,8 @@ static bool stringPartOfInterpString(const AstNode* node, Position position)
return false; return false;
} }
printf("expressions.count: %ld\n", interpString->expressions.size);
for (const AstExpr* expression : interpString->expressions) for (const AstExpr* expression : interpString->expressions)
{ {
printf("%s <= %s <= %s -- expression = %d\n", toString(expression->location.begin).c_str(), toString(position).c_str(), toString(expression->location.end).c_str(), expression->classIndex);
if (expression->location.containsClosed(position)) if (expression->location.containsClosed(position))
{ {
return false; return false;
@ -1239,7 +1236,6 @@ static bool stringPartOfInterpString(const AstNode* node, Position position)
} }
return true; return true;
// return node->is<AstExprInterpString>();
} }
static std::optional<AutocompleteEntryMap> autocompleteStringParams(const SourceModule& sourceModule, const ModulePtr& module, static std::optional<AutocompleteEntryMap> autocompleteStringParams(const SourceModule& sourceModule, const ModulePtr& module,

View file

@ -22,7 +22,7 @@ bool lua_telemetry_parsed_named_non_function_type = false;
LUAU_FASTFLAGVARIABLE(LuauErrorDoubleHexPrefix, false) LUAU_FASTFLAGVARIABLE(LuauErrorDoubleHexPrefix, false)
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuaReportParseIntegerIssues, false) LUAU_DYNAMIC_FASTFLAGVARIABLE(LuaReportParseIntegerIssues, false)
LUAU_FASTFLAGVARIABLE(LuauInterpolatedStringBaseSupport, false) LUAU_FASTFLAGVARIABLE(LuauInterpolatedStringBaseSupport, true)
LUAU_FASTFLAGVARIABLE(LuauCommaParenWarnings, false) LUAU_FASTFLAGVARIABLE(LuauCommaParenWarnings, false)
LUAU_FASTFLAGVARIABLE(LuauTableConstructorRecovery, false) LUAU_FASTFLAGVARIABLE(LuauTableConstructorRecovery, false)
@ -2661,6 +2661,7 @@ AstExpr* Parser::parseInterpString()
TempVector<AstExpr*> expressions(scratchExpr); TempVector<AstExpr*> expressions(scratchExpr);
Location startLocation = lexer.current().location; Location startLocation = lexer.current().location;
Location endLocation;
do do
{ {
@ -2668,17 +2669,16 @@ AstExpr* Parser::parseInterpString()
LUAU_ASSERT(currentLexeme.type == Lexeme::InterpStringBegin || currentLexeme.type == Lexeme::InterpStringMid || LUAU_ASSERT(currentLexeme.type == Lexeme::InterpStringBegin || currentLexeme.type == Lexeme::InterpStringMid ||
currentLexeme.type == Lexeme::InterpStringEnd || currentLexeme.type == Lexeme::InterpStringSimple); currentLexeme.type == Lexeme::InterpStringEnd || currentLexeme.type == Lexeme::InterpStringSimple);
Location location = currentLexeme.location; endLocation = currentLexeme.location;
Location startOfBrace = Location(location.end, 1); Location startOfBrace = Location(endLocation.end, 1);
scratchData.assign(currentLexeme.data, currentLexeme.length); scratchData.assign(currentLexeme.data, currentLexeme.length);
if (!Lexer::fixupQuotedString(scratchData)) if (!Lexer::fixupQuotedString(scratchData))
{ {
nextLexeme(); nextLexeme();
printf("FIXUP QUOTED STRING FAIL\n"); return reportExprError(Location{startLocation, endLocation}, {}, "Interpolated string literal contains malformed escape sequence");
return reportExprError(startLocation, {}, "Interpolated string literal contains malformed escape sequence");
} }
AstArray<char> chars = copy(scratchData); AstArray<char> chars = copy(scratchData);
@ -2689,10 +2689,7 @@ AstExpr* Parser::parseInterpString()
if (currentLexeme.type == Lexeme::InterpStringEnd || currentLexeme.type == Lexeme::InterpStringSimple) if (currentLexeme.type == Lexeme::InterpStringEnd || currentLexeme.type == Lexeme::InterpStringSimple)
{ {
AstArray<AstArray<char>> stringsArray = copy(strings); break;
AstArray<AstExpr*> expressionsArray = copy(expressions);
return allocator.alloc<AstExprInterpString>(startLocation, stringsArray, expressionsArray);
} }
switch (lexer.current().type) switch (lexer.current().type)
@ -2701,20 +2698,14 @@ AstExpr* Parser::parseInterpString()
case Lexeme::InterpStringEnd: case Lexeme::InterpStringEnd:
{ {
nextLexeme(); nextLexeme();
expressions.push_back(reportExprError(location, {}, "Malformed interpolated string, expected expression inside '{}'")); expressions.push_back(reportExprError(endLocation, {}, "Malformed interpolated string, expected expression inside '{}'"));
AstArray<AstArray<char>> stringsArray = copy(strings); break;
AstArray<AstExpr*> expressionsArray = copy(expressions);
return allocator.alloc<AstExprInterpString>(startLocation, stringsArray, expressionsArray);
} }
case Lexeme::BrokenString: case Lexeme::BrokenString:
{ {
nextLexeme(); nextLexeme();
expressions.push_back(reportExprError(location, {}, "Malformed interpolated string, did you forget to add a '`'?")); expressions.push_back(reportExprError(endLocation, {}, "Malformed interpolated string, did you forget to add a '`'?"));
AstArray<AstArray<char>> stringsArray = copy(strings); break;
AstArray<AstExpr*> expressionsArray = copy(expressions);
return allocator.alloc<AstExprInterpString>(startLocation, stringsArray, expressionsArray);
} }
default: default:
expressions.push_back(parseExpr()); expressions.push_back(parseExpr());
@ -2728,17 +2719,18 @@ AstExpr* Parser::parseInterpString()
break; break;
case Lexeme::BrokenInterpDoubleBrace: case Lexeme::BrokenInterpDoubleBrace:
nextLexeme(); nextLexeme();
printf("BROKEN INTERP DOUBLE BRACE\n"); return reportExprError(endLocation, {}, ERROR_INVALID_INTERP_DOUBLE_BRACE);
return reportExprError(location, {}, ERROR_INVALID_INTERP_DOUBLE_BRACE);
case Lexeme::BrokenString: case Lexeme::BrokenString:
nextLexeme(); nextLexeme();
printf("BROKEN STRING\n"); return reportExprError(endLocation, {}, "Malformed interpolated string, did you forget to add a '}'?");
return reportExprError(location, {}, "Malformed interpolated string, did you forget to add a '}'?");
default: default:
printf("DEFAULT: %s\n", lexer.current().toString().c_str()); return reportExprError(endLocation, {}, "Malformed interpolated string, got %s", lexer.current().toString().c_str());
return reportExprError(location, {}, "Malformed interpolated string, got %s", lexer.current().toString().c_str());
} }
} while (true); } while (true);
AstArray<AstArray<char>> stringsArray = copy(strings);
AstArray<AstExpr*> expressionsArray = copy(expressions);
return allocator.alloc<AstExprInterpString>(Location{startLocation, endLocation}, stringsArray, expressionsArray);
} }
AstExpr* Parser::parseNumber() AstExpr* Parser::parseNumber()

View file

@ -183,7 +183,7 @@ TEST_CASE_FIXTURE(JsonEncoderFixture, "encode_AstExprInterpString")
AstStat* statement = expectParseStatement("local a = `var = {x}`"); AstStat* statement = expectParseStatement("local a = `var = {x}`");
std::string_view expected = std::string_view expected =
R"({"type":"AstStatLocal","location":"0,0 - 0,18","vars":[{"luauType":null,"name":"a","type":"AstLocal","location":"0,6 - 0,7"}],"values":[{"type":"AstExprInterpString","location":"0,10 - 0,18","strings":["var = ",""],"expressions":[{"type":"AstExprGlobal","location":"0,18 - 0,19","global":"x"}]}]})"; R"({"type":"AstStatLocal","location":"0,0 - 0,21","vars":[{"luauType":null,"name":"a","type":"AstLocal","location":"0,6 - 0,7"}],"values":[{"type":"AstExprInterpString","location":"0,10 - 0,21","strings":["var = ",""],"expressions":[{"type":"AstExprGlobal","location":"0,18 - 0,19","global":"x"}]}]})";
CHECK(toJson(statement) == expected); CHECK(toJson(statement) == expected);
} }

View file

@ -2712,15 +2712,24 @@ a = if temp then even else abc@3
TEST_CASE_FIXTURE(ACFixture, "autocomplete_interpolated_string_constant") TEST_CASE_FIXTURE(ACFixture, "autocomplete_interpolated_string_constant")
{ {
ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true}; ScopedFastFlag sff{"LuauInterpolatedStringBaseSupport", true};
printf("======= autocomplete_interpolated_string_constant =======\n");
// check(R"(f(`@1`))"); check(R"(f(`@1`))");
// auto ac = autocomplete('1'); auto ac = autocomplete('1');
// CHECK(ac.entryMap.empty()); CHECK(ac.entryMap.empty());
// CHECK_EQ(ac.context, AutocompleteContext::String); CHECK_EQ(ac.context, AutocompleteContext::String);
check(R"(f(`@1 {"a"}`))");
ac = autocomplete('1');
CHECK(ac.entryMap.empty());
CHECK_EQ(ac.context, AutocompleteContext::String);
check(R"(f(`{"a"} @1`))"); check(R"(f(`{"a"} @1`))");
auto ac = autocomplete('1'); ac = autocomplete('1');
CHECK(ac.entryMap.empty());
CHECK_EQ(ac.context, AutocompleteContext::String);
check(R"(f(`{"a"} @1 {"b"}`))");
ac = autocomplete('1');
CHECK(ac.entryMap.empty()); CHECK(ac.entryMap.empty());
CHECK_EQ(ac.context, AutocompleteContext::String); CHECK_EQ(ac.context, AutocompleteContext::String);
} }
@ -2745,8 +2754,9 @@ TEST_CASE_FIXTURE(ACFixture, "autocomplete_interpolated_string_expression_with_c
CHECK(ac.entryMap.count("table")); CHECK(ac.entryMap.count("table"));
CHECK_EQ(ac.context, AutocompleteContext::Expression); CHECK_EQ(ac.context, AutocompleteContext::Expression);
check(R"(f(`expression = {@1--[[ bla bla bla ]]`))"); check(R"(f(`expression = {@1 --[[ bla bla bla ]]`))");
ac = autocomplete('1'); ac = autocomplete('1');
CHECK(!ac.entryMap.empty());
CHECK(ac.entryMap.count("table")); CHECK(ac.entryMap.count("table"));
CHECK_EQ(ac.context, AutocompleteContext::Expression); CHECK_EQ(ac.context, AutocompleteContext::Expression);
} }