1 /*
2     SPDX-FileCopyrightText: 2013 Andrea Scarpino <scarpino@kde.org>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "usebuilder.h"
8 #include "expressionvisitor.h"
9 
10 #include "helper.h"
11 #include "parsesession.h"
12 
13 using namespace KDevelop;
14 
UseBuilder(ParseSession * session)15 UseBuilder::UseBuilder(ParseSession* session)
16 : UseBuilderBase()
17 {
18     m_session = session;
19     m_nodesThatOpenedContexts.push(nullptr);    // One push here, a thousand isEmpty() calls avoided later
20 }
21 
preVisit(QmlJS::AST::Node * node)22 bool UseBuilder::preVisit(QmlJS::AST::Node* node)
23 {
24     DUContext* ctx = contextFromNode(node);
25 
26     if (ctx && currentContext() != ctx) {
27         openContext(ctx);
28         m_nodesThatOpenedContexts.push(node);
29     }
30 
31     return true;
32 }
33 
postVisit(QmlJS::AST::Node * node)34 void UseBuilder::postVisit(QmlJS::AST::Node* node)
35 {
36     if (m_nodesThatOpenedContexts.top() == node) {
37         closeContext();
38         m_nodesThatOpenedContexts.pop();
39     }
40 }
41 
visit(QmlJS::AST::FieldMemberExpression * node)42 bool UseBuilder::visit(QmlJS::AST::FieldMemberExpression* node)
43 {
44     useForExpression(node, m_session->locationToRange(node->identifierToken));
45     return UseBuilderBase::visit(node);
46 }
47 
visit(QmlJS::AST::IdentifierExpression * node)48 bool UseBuilder::visit(QmlJS::AST::IdentifierExpression* node)
49 {
50     useForExpression(node);
51     return UseBuilderBase::visit(node);
52 }
53 
visit(QmlJS::AST::UiQualifiedId * node)54 bool UseBuilder::visit(QmlJS::AST::UiQualifiedId* node)
55 {
56     useForExpression(node);
57     return false;
58 }
59 
visit(QmlJS::AST::UiImport * node)60 bool UseBuilder::visit(QmlJS::AST::UiImport* node)
61 {
62     Q_UNUSED(node);
63     return false;   // Don't highlight the identifiers that appear in import statements
64 }
65 
visit(QmlJS::AST::UiScriptBinding * node)66 bool UseBuilder::visit(QmlJS::AST::UiScriptBinding* node)
67 {
68     QString propertyName = node->qualifiedId->name.toString();
69 
70     if (propertyName == QLatin1String("name") ||
71         propertyName == QLatin1String("type") ||
72         propertyName == QLatin1String("exports") ||
73         propertyName == QLatin1String("prototype")) {
74         // Ignore plugin.qmltypes-specific property names. They appear a huge
75         // number of time and are never declared anywhere.
76         return false;
77     }
78 
79     return true;
80 }
81 
visit(QmlJS::AST::UiPublicMember * node)82 bool UseBuilder::visit(QmlJS::AST::UiPublicMember* node)
83 {
84     // node->memberType can contain a type name (if node is a property), use it
85     DeclarationPointer decl = QmlJS::getDeclaration(
86         QualifiedIdentifier(node->memberTypeName().toString()),
87         currentContext()
88     );
89 
90     newUse(
91         m_session->locationToRange(node->typeToken),
92         decl
93     );
94 
95     return true;
96 }
97 
useForExpression(QmlJS::AST::Node * node,const KDevelop::RangeInRevision & range)98 void UseBuilder::useForExpression(QmlJS::AST::Node* node, const KDevelop::RangeInRevision &range)
99 {
100     // ExpressionVisitor can find the type and corresponding declaration of many
101     // kinds of expressions (identifiers, field members, special identifiers like
102     // this or parent, etc).
103     ExpressionVisitor visitor(currentContext());
104 
105     node->accept(&visitor);
106 
107     if (visitor.lastDeclaration()) {
108         newUse(
109             range.isValid() ? range : m_session->locationsToRange(
110                 node->firstSourceLocation(),
111                 node->lastSourceLocation()
112             ),
113             visitor.lastDeclaration()
114         );
115     }
116 }
117