diff options
author | Sami Shalayel <sami.shalayel@qt.io> | 2023-11-17 14:23:20 +0100 |
---|---|---|
committer | Sami Shalayel <sami.shalayel@qt.io> | 2023-11-28 12:33:27 +0100 |
commit | b9bf657c0194942c3051381e6c4db7e00ceb10f9 (patch) | |
tree | ccd9173a241da322cce66895d716ff80d5f23118 /src/qml/parser | |
parent | da693f932e948c960526140d5701698a53155f23 (diff) |
qqmljs.g: insert empty identifiers when missing
For the completion in qmlls, users usually expect to see a list of
completions after typing in a T_DOT ("."). Sadly, this T_DOT usually
makes the QML code invalid or ambiguous, such that the parser aborts
parsing.
For qmlls, this is quite bad: no completions can be proposed if the AST
cannot be computed. Therefore, this commit tries to make the parser a
little bit more resistant to missing T_IDENTIFIER behind T_DOT.
Add a yyprevtoken field in the parser to keep track of what was the
last successfully parsed token, and update it with yytoken before yytoken
changes.
Extract the pushTokenWithEmptyLocation() logic from the automatic
semicolon inserting code, and reuse it for the automatic insertion of
identifiers after dots.
Add some tests in tst_qmlls_utils and adapt the qmlls completion code to
work with missing right hand sides (RHS) of dotted expression
`a.b`. Create a new file missingRHS.qml because yyy.qml does not seem to
stop growing.
The fix of this commit does not take care of all possible cases: when
T_DOT is followed by an T_IDENTIFIER, then no T_IDENTIFIER is inserted
even if the parsing fails afterwards. This happens when a JS
statement is behind a T_DOT without identifier, for example. Add tests
for that too, QEXPECT_FAIL them and put them in a separate file
missingRHS.parserfail.qml. They need to be in a separate file because no
completions can be obtained when the parser fails, and that affects all
the completions of the entire file. This special file
missingRHS.parserfail.qml also needs to be ignored by tst_qmlformat.
Task-number: QTBUG-115836
Change-Id: If307430131a7df25ae9bd4ea0393d47c0641c8d3
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src/qml/parser')
-rw-r--r-- | src/qml/parser/qqmljs.g | 48 |
1 files changed, 26 insertions, 22 deletions
diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g index 80225a87dc..6d9d11800c 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -328,6 +328,7 @@ protected: AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr); void pushToken(int token); + void pushTokenWithEmptyLocation(int token); int lookaheadToken(Lexer *lexer); static DiagnosticMessage compileError(const SourceLocation &location, @@ -379,6 +380,7 @@ protected: QStringView yytokenraw; SourceLocation yylloc; SourceLocation yyprevlloc; + int yyprevtoken = -1; SavedToken token_buffer[TOKEN_BUFFER_SIZE]; SavedToken *first_token = nullptr; @@ -512,9 +514,19 @@ void Parser::pushToken(int token) yytoken = token; } +void Parser::pushTokenWithEmptyLocation(int token) +{ + pushToken(token); + yylloc = yyprevlloc; + yylloc.offset += yylloc.length; + yylloc.startColumn += yylloc.length; + yylloc.length = 0; +} + int Parser::lookaheadToken(Lexer *lexer) { if (yytoken < 0) { + yyprevtoken = yytoken; yytoken = lexer->lex(); yylval = lexer->tokenValue(); yytokenspell = lexer->tokenSpell(); @@ -610,6 +622,7 @@ bool Parser::parse(int startToken) #endif if (action > 0) { if (action != ACCEPT_STATE) { + yyprevtoken = yytoken; yytoken = -1; sym(1).dval = yylval; stringRef(1) = yytokenspell; @@ -4790,35 +4803,26 @@ ExportSpecifier: IdentifierName T_AS IdentifierName; if (first_token == last_token) { const int errorState = state_stack[tos]; + // automatic insertion of missing identifiers after dots + if (yytoken != -1 && m_enableIdentifierInsertion && t_action(errorState, T_IDENTIFIER) && yyprevtoken == T_DOT) { +#ifdef PARSER_DEBUG + qDebug() << "Inserting missing identifier between" << spell[yyprevtoken] << "and" + << spell[yytoken]; +#endif + pushTokenWithEmptyLocation(T_IDENTIFIER); + action = errorState; + goto _Lcheck_token; + } + + // automatic insertion of `;' if (yytoken != -1 && ((t_action(errorState, T_AUTOMATIC_SEMICOLON) && lexer->canInsertAutomaticSemicolon(yytoken)) || t_action(errorState, T_COMPATIBILITY_SEMICOLON))) { #ifdef PARSER_DEBUG qDebug() << "Inserting automatic semicolon."; #endif - SavedToken &tk = token_buffer[0]; - tk.token = yytoken; - tk.dval = yylval; - tk.spell = yytokenspell; - tk.raw = yytokenraw; - tk.loc = yylloc; - - yylloc = yyprevlloc; - yylloc.offset += yylloc.length; - yylloc.startColumn += yylloc.length; - yylloc.length = 0; - - //const QString msg = QCoreApplication::translate("QQmlParser", "Missing `;'"); - //diagnostic_messages.append(compileError(yyloc, msg, QtWarningMsg)); - - first_token = &token_buffer[0]; - last_token = &token_buffer[1]; - - yytoken = T_SEMICOLON; - yylval = 0; - + pushTokenWithEmptyLocation(T_SEMICOLON); action = errorState; - goto _Lcheck_token; } |