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