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