1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "typehierarchybuilder.h"
27
28 #include <cplusplus/FindUsages.h>
29
30 using namespace CPlusPlus;
31 using namespace CppTools;
32
33 namespace {
34
unqualifyName(const QString & qualifiedName)35 QString unqualifyName(const QString &qualifiedName)
36 {
37 const int index = qualifiedName.lastIndexOf(QLatin1String("::"));
38 if (index == -1)
39 return qualifiedName;
40 return qualifiedName.right(qualifiedName.length() - index - 2);
41 }
42
43 class DerivedHierarchyVisitor : public SymbolVisitor
44 {
45 public:
DerivedHierarchyVisitor(const QString & qualifiedName,QHash<QString,QHash<QString,QString>> & cache)46 explicit DerivedHierarchyVisitor(const QString &qualifiedName, QHash<QString, QHash<QString, QString>> &cache)
47 : _qualifiedName(qualifiedName)
48 , _unqualifiedName(unqualifyName(qualifiedName))
49 , _cache(cache)
50 {}
51
52 void execute(const Document::Ptr &doc, const Snapshot &snapshot);
53
54 bool visit(Class *) override;
55
derived()56 const QList<Symbol *> &derived() { return _derived; }
otherBases()57 const QSet<QString> otherBases() { return _otherBases; }
58
59 private:
60 Symbol *lookup(const Name *symbolName, Scope *enclosingScope);
61
62 LookupContext _context;
63 QString _qualifiedName;
64 QString _unqualifiedName;
65 Overview _overview;
66 // full scope name to base symbol name to fully qualified base symbol name
67 QHash<QString, QHash<QString, QString>> &_cache;
68 QSet<QString> _otherBases;
69 QList<Symbol *> _derived;
70 };
71
execute(const Document::Ptr & doc,const Snapshot & snapshot)72 void DerivedHierarchyVisitor::execute(const Document::Ptr &doc,
73 const Snapshot &snapshot)
74 {
75 _derived.clear();
76 _otherBases.clear();
77 _context = LookupContext(doc, snapshot);
78
79 for (int i = 0; i < doc->globalSymbolCount(); ++i)
80 accept(doc->globalSymbolAt(i));
81 }
82
visit(Class * symbol)83 bool DerivedHierarchyVisitor::visit(Class *symbol)
84 {
85 const QList<const Name *> &fullScope
86 = LookupContext::fullyQualifiedName(symbol->enclosingScope());
87 const QString fullScopeName = _overview.prettyName(fullScope);
88
89 for (int i = 0; i < symbol->baseClassCount(); ++i) {
90 BaseClass *baseSymbol = symbol->baseClassAt(i);
91
92 const QString &baseName = _overview.prettyName(baseSymbol->name());
93 QString fullBaseName = _cache.value(fullScopeName).value(baseName);
94 if (fullBaseName.isEmpty()) {
95 Symbol *actualBaseSymbol = TypeHierarchyBuilder::followTypedef(_context,
96 baseSymbol->name(), symbol->enclosingScope()).declaration();
97 if (!actualBaseSymbol)
98 continue;
99
100 const QList<const Name *> &full
101 = LookupContext::fullyQualifiedName(actualBaseSymbol);
102 fullBaseName = _overview.prettyName(full);
103 _cache[fullScopeName].insert(baseName, fullBaseName);
104 }
105
106 if (_qualifiedName == fullBaseName)
107 _derived.append(symbol);
108 else
109 _otherBases.insert(fullBaseName);
110 }
111 return true;
112 }
113
114 } // namespace
115
116 TypeHierarchy::TypeHierarchy() = default;
117
TypeHierarchy(Symbol * symbol)118 TypeHierarchy::TypeHierarchy(Symbol *symbol) : _symbol(symbol)
119 {}
120
symbol() const121 Symbol *TypeHierarchy::symbol() const
122 {
123 return _symbol;
124 }
125
hierarchy() const126 const QList<TypeHierarchy> &TypeHierarchy::hierarchy() const
127 {
128 return _hierarchy;
129 }
130
buildDerivedTypeHierarchy(Symbol * symbol,const Snapshot & snapshot)131 TypeHierarchy TypeHierarchyBuilder::buildDerivedTypeHierarchy(Symbol *symbol,
132 const Snapshot &snapshot)
133 {
134 QFutureInterfaceBase dummy;
135 return TypeHierarchyBuilder::buildDerivedTypeHierarchy(dummy, symbol, snapshot);
136 }
137
buildDerivedTypeHierarchy(QFutureInterfaceBase & futureInterface,Symbol * symbol,const Snapshot & snapshot)138 TypeHierarchy TypeHierarchyBuilder::buildDerivedTypeHierarchy(QFutureInterfaceBase &futureInterface,
139 Symbol *symbol,
140 const Snapshot &snapshot)
141 {
142 TypeHierarchy hierarchy(symbol);
143 TypeHierarchyBuilder builder;
144 QHash<QString, QHash<QString, QString>> cache;
145 builder.buildDerived(futureInterface, &hierarchy, snapshot, cache);
146 return hierarchy;
147 }
148
followTypedef(const LookupContext & context,const Name * symbolName,Scope * enclosingScope,std::set<const Symbol * > typedefs)149 LookupItem TypeHierarchyBuilder::followTypedef(const LookupContext &context, const Name *symbolName,
150 Scope *enclosingScope,
151 std::set<const Symbol *> typedefs)
152 {
153 QList<LookupItem> items = context.lookup(symbolName, enclosingScope);
154
155 Symbol *actualBaseSymbol = nullptr;
156 LookupItem matchingItem;
157
158 for (const LookupItem &item : items) {
159 Symbol *s = item.declaration();
160 if (!s)
161 continue;
162 if (!s->isClass() && !s->isTemplate() && !s->isTypedef())
163 continue;
164 if (!typedefs.insert(s).second)
165 continue;
166 actualBaseSymbol = s;
167 matchingItem = item;
168 break;
169 }
170
171 if (!actualBaseSymbol)
172 return LookupItem();
173
174 if (actualBaseSymbol->isTypedef()) {
175 NamedType *namedType = actualBaseSymbol->type()->asNamedType();
176 if (!namedType) {
177 // Anonymous aggregate such as: typedef struct {} Empty;
178 return LookupItem();
179 }
180 return followTypedef(context, namedType->name(), actualBaseSymbol->enclosingScope(),
181 typedefs);
182 }
183
184 return matchingItem;
185 }
186
filesDependingOn(const Snapshot & snapshot,Symbol * symbol)187 static Utils::FilePaths filesDependingOn(const Snapshot &snapshot,
188 Symbol *symbol)
189 {
190 if (!symbol)
191 return Utils::FilePaths();
192
193 const Utils::FilePath file = Utils::FilePath::fromUtf8(symbol->fileName(), symbol->fileNameLength());
194 return Utils::FilePaths { file } + snapshot.filesDependingOn(file);
195 }
196
buildDerived(QFutureInterfaceBase & futureInterface,TypeHierarchy * typeHierarchy,const Snapshot & snapshot,QHash<QString,QHash<QString,QString>> & cache,int depth)197 void TypeHierarchyBuilder::buildDerived(QFutureInterfaceBase &futureInterface,
198 TypeHierarchy *typeHierarchy,
199 const Snapshot &snapshot,
200 QHash<QString, QHash<QString, QString>> &cache,
201 int depth)
202 {
203 Symbol *symbol = typeHierarchy->_symbol;
204 if (_visited.contains(symbol))
205 return;
206
207 _visited.insert(symbol);
208
209 const QString &symbolName = _overview.prettyName(LookupContext::fullyQualifiedName(symbol));
210 DerivedHierarchyVisitor visitor(symbolName, cache);
211
212 const Utils::FilePaths &dependingFiles = filesDependingOn(snapshot, symbol);
213 if (depth == 0)
214 futureInterface.setProgressRange(0, dependingFiles.size());
215
216 int i = -1;
217 for (const Utils::FilePath &fileName : dependingFiles) {
218 if (futureInterface.isCanceled())
219 return;
220 if (depth == 0)
221 futureInterface.setProgressValue(++i);
222 Document::Ptr doc = snapshot.document(fileName);
223 if ((_candidates.contains(fileName) && !_candidates.value(fileName).contains(symbolName))
224 || !doc->control()->findIdentifier(symbol->identifier()->chars(),
225 symbol->identifier()->size())) {
226 continue;
227 }
228
229 visitor.execute(doc, snapshot);
230 _candidates.insert(fileName, visitor.otherBases());
231
232 const QList<Symbol *> &derived = visitor.derived();
233 for (Symbol *s : derived) {
234 TypeHierarchy derivedHierarchy(s);
235 buildDerived(futureInterface, &derivedHierarchy, snapshot, cache, depth + 1);
236 if (futureInterface.isCanceled())
237 return;
238 typeHierarchy->_hierarchy.append(derivedHierarchy);
239 }
240 }
241 }
242