diff options
author | Sami Shalayel <sami.shalayel@qt.io> | 2023-11-29 16:48:04 +0100 |
---|---|---|
committer | Sami Shalayel <sami.shalayel@qt.io> | 2023-12-05 17:50:35 +0100 |
commit | 99c8a040f6e2a8a8733e94fbd07986b4f5982211 (patch) | |
tree | e3d56d3a59c041e65a504bf36b18856dfcace7e7 /src/qml/parser | |
parent | 2313dc93733b6fa46dd0de5711b9f4053768552e (diff) |
qmlls: completions in variable declarations
Add the equal token sourcelocation into a ScriptPattern. This is
somewhat cumbersome because the parser has no direct access to it.
Instead, create a new ExpressionNode type called InitializerExpression:
it contains an ExpressionNode and an equaltoken, and is populated in the
parser for Initializer and Initializer_In rules. It also implements some
pure virtual methods to not be abstract, and has its own
Kind_InitializerExpression.
The PatternElement constructor extracts the location of the equaltoken
from the InitializerExpression in its constructor, and saves it in its
new member equaltoken.
Later on, the Dom constructor will be able to add the location of the
equal token to the Dom, such that qmlls's completion can decide whether
or not completion is required in variable declaration statements.
With this commit, qmlls will provide completions only after the above
mentioned equal token. The explanation is in a comment in qqmllsutils,
but the rough idea is that everything before the '=' is a variable name
(so it should not be in use yet, to avoid shadowing and confusing QML
programs) and that everything behind a '=' is a default value that can
be any arbitrary expression in JS. This default value can be a method
name, a property name, etc, so provide completion at this place.
Also takes care of completions inside of deconstructions nested inside
variable declarations.
Task-number: QTBUG-117445
Change-Id: Ie58ffda4de9636796a9a690537affef85ede398d
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src/qml/parser')
-rw-r--r-- | src/qml/parser/qqmljs.g | 4 | ||||
-rw-r--r-- | src/qml/parser/qqmljsast.cpp | 5 | ||||
-rw-r--r-- | src/qml/parser/qqmljsast_p.h | 58 |
3 files changed, 65 insertions, 2 deletions
diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g index 6d9d11800c..bc5955245a 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -2167,7 +2167,9 @@ Initializer: T_EQ AssignmentExpression; Initializer_In: T_EQ AssignmentExpression_In; /. case $rule_number: { - sym(1) = sym(2); + auto node = new (pool) AST::InitializerExpression(sym(2).Expression); + node->equalToken = loc(1); + sym(1).Expression = node; } break; ./ diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp index 76448f6853..a45d4cbd1b 100644 --- a/src/qml/parser/qqmljsast.cpp +++ b/src/qml/parser/qqmljsast.cpp @@ -1394,6 +1394,11 @@ void TaggedTemplate::accept0(BaseVisitor *visitor) visitor->endVisit(this); } +void InitializerExpression::accept0(BaseVisitor *visitor) +{ + expression->accept0(visitor); +} + void PatternElement::accept0(BaseVisitor *visitor) { if (visitor->visit(this)) { diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h index 73b5c5cd5e..2a76c371d0 100644 --- a/src/qml/parser/qqmljsast_p.h +++ b/src/qml/parser/qqmljsast_p.h @@ -149,6 +149,7 @@ public: Kind_ClassDeclaration, Kind_IdentifierExpression, Kind_IdentifierPropertyName, + Kind_InitializerExpression, Kind_ComputedPropertyName, Kind_IfStatement, Kind_LabelledStatement, @@ -873,6 +874,39 @@ struct BoundNames : public QVector<BoundName> } }; +/*! +\internal +This class is needed to pass the information about the equalToken in the parser, and is only needed +during AST construction. It behaves exactly like the expression it contains: that avoids changing +all the usages in qqmljs.g from ExpressionNode to InitializerExpression for every rule expecting a +InitializerOpt_In or InitializerOpt. +*/ +class QML_PARSER_EXPORT InitializerExpression : public ExpressionNode +{ +public: + QQMLJS_DECLARE_AST_NODE(InitializerExpression) + + InitializerExpression(ExpressionNode *e) : expression(e) { kind = K; } + + void accept0(BaseVisitor *visitor) override; + + SourceLocation firstSourceLocation() const override + { return equalToken; } + + SourceLocation lastSourceLocation() const override { return expression->lastSourceLocation(); } + + FunctionExpression *asFunctionDefinition() override + { + return expression->asFunctionDefinition(); + } + + ClassExpression *asClassDefinition() override { return expression->asClassDefinition(); } + + // attributes + ExpressionNode *expression; + SourceLocation equalToken; +}; + class QML_PARSER_EXPORT PatternElement : public Node { public: @@ -893,9 +927,28 @@ public: Binding, }; +private: + /*! + \internal + Hide InitializerExpression from the AST. InitializerExpression is only needed during parsing for + the AST construction, and it is not possible for the parser to directly embed the location of + equal tokens inside the PatternElement without the InitializerExpression. + */ + void unwrapInitializer() + { + if (auto unwrapped = AST::cast<InitializerExpression *>(initializer)) { + equalToken = unwrapped->equalToken; + initializer = unwrapped->expression; + } + } +public: + PatternElement(ExpressionNode *i = nullptr, Type t = Literal) : initializer(i), type(t) - { kind = K; } + { + kind = K; + unwrapInitializer(); + } PatternElement(QStringView n, TypeAnnotation *typeAnnotation = nullptr, ExpressionNode *i = nullptr, Type t = Binding) : bindingIdentifier(n), initializer(i), type(t) @@ -903,6 +956,7 @@ public: { Q_ASSERT(t >= RestElement); kind = K; + unwrapInitializer(); } PatternElement(Pattern *pattern, ExpressionNode *i = nullptr, Type t = Binding) @@ -910,6 +964,7 @@ public: { Q_ASSERT(t >= RestElement); kind = K; + unwrapInitializer(); } void accept0(BaseVisitor *visitor) override; @@ -933,6 +988,7 @@ public: // attributes SourceLocation identifierToken; + SourceLocation equalToken; QStringView bindingIdentifier; ExpressionNode *bindingTarget = nullptr; ExpressionNode *initializer = nullptr; |