aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/parser
diff options
context:
space:
mode:
authorSami Shalayel <sami.shalayel@qt.io>2023-11-17 14:23:20 +0100
committerSami Shalayel <sami.shalayel@qt.io>2023-11-28 12:33:27 +0100
commitb9bf657c0194942c3051381e6c4db7e00ceb10f9 (patch)
treeccd9173a241da322cce66895d716ff80d5f23118 /src/qml/parser
parentda693f932e948c960526140d5701698a53155f23 (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.g48
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;
}