1 /*
2 SPDX-FileCopyrightText: 2007 Hamish Rodda <rodda@kde.org>
3 SPDX-FileCopyrightText: 2007-2008 David Nolden <david.nolden.kdevelop@art-master.de>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8 #include "duchainutils.h"
9
10 #include <algorithm>
11
12 #include <interfaces/icore.h>
13 #include <interfaces/ilanguagecontroller.h>
14
15 #include "../interfaces/ilanguagesupport.h"
16 #include "../assistant/staticassistantsmanager.h"
17 #include <debug.h>
18
19 #include "declaration.h"
20 #include "classfunctiondeclaration.h"
21 #include "ducontext.h"
22 #include "duchain.h"
23 #include "use.h"
24 #include "duchainlock.h"
25 #include "classmemberdeclaration.h"
26 #include "functiondefinition.h"
27 #include "specializationstore.h"
28 #include "persistentsymboltable.h"
29 #include "classdeclaration.h"
30 #include "parsingenvironment.h"
31
32 #include <QStandardPaths>
33
34 using namespace KDevelop;
35 using namespace KTextEditor;
36
completionProperties(const Declaration * dec)37 CodeCompletionModel::CompletionProperties DUChainUtils::completionProperties(const Declaration* dec)
38 {
39 CodeCompletionModel::CompletionProperties p;
40
41 if(dec->context()->type() == DUContext::Class) {
42 if (const auto* member = dynamic_cast<const ClassMemberDeclaration*>(dec)) {
43 switch (member->accessPolicy()) {
44 case Declaration::Public:
45 p |= CodeCompletionModel::Public;
46 break;
47 case Declaration::Protected:
48 p |= CodeCompletionModel::Protected;
49 break;
50 case Declaration::Private:
51 p |= CodeCompletionModel::Private;
52 break;
53 default:
54 break;
55 }
56
57 if (member->isStatic())
58 p |= CodeCompletionModel::Static;
59 if (member->isAuto())
60 {}//TODO
61 if (member->isFriend())
62 p |= CodeCompletionModel::Friend;
63 if (member->isRegister())
64 {}//TODO
65 if (member->isExtern())
66 {}//TODO
67 if (member->isMutable())
68 {}//TODO
69 }
70 }
71
72 if (const auto* function = dynamic_cast<const AbstractFunctionDeclaration*>(dec)) {
73 p |= CodeCompletionModel::Function;
74 if (function->isVirtual())
75 p |= CodeCompletionModel::Virtual;
76 if (function->isInline())
77 p |= CodeCompletionModel::Inline;
78 if (function->isExplicit())
79 {}//TODO
80 }
81
82 if( dec->isTypeAlias() )
83 p |= CodeCompletionModel::TypeAlias;
84
85 if (dec->abstractType()) {
86 switch (dec->abstractType()->whichType()) {
87 case AbstractType::TypeIntegral:
88 p |= CodeCompletionModel::Variable;
89 break;
90 case AbstractType::TypePointer:
91 p |= CodeCompletionModel::Variable;
92 break;
93 case AbstractType::TypeReference:
94 p |= CodeCompletionModel::Variable;
95 break;
96 case AbstractType::TypeFunction:
97 p |= CodeCompletionModel::Function;
98 break;
99 case AbstractType::TypeStructure:
100 p |= CodeCompletionModel::Class;
101 break;
102 case AbstractType::TypeArray:
103 p |= CodeCompletionModel::Variable;
104 break;
105 case AbstractType::TypeEnumeration:
106 p |= CodeCompletionModel::Enum;
107 break;
108 case AbstractType::TypeEnumerator:
109 p |= CodeCompletionModel::Variable;
110 break;
111 case AbstractType::TypeAbstract:
112 case AbstractType::TypeDelayed:
113 case AbstractType::TypeUnsure:
114 case AbstractType::TypeAlias:
115 // TODO
116 break;
117 }
118
119 if( dec->abstractType()->modifiers() & AbstractType::ConstModifier )
120 p |= CodeCompletionModel::Const;
121
122 if( dec->kind() == Declaration::Instance && !dec->isFunctionDeclaration() )
123 p |= CodeCompletionModel::Variable;
124 }
125
126 if (dec->context()) {
127 if( dec->context()->type() == DUContext::Global )
128 p |= CodeCompletionModel::GlobalScope;
129 else if( dec->context()->type() == DUContext::Namespace )
130 p |= CodeCompletionModel::NamespaceScope;
131 else if( dec->context()->type() != DUContext::Class && dec->context()->type() != DUContext::Enum )
132 p |= CodeCompletionModel::LocalScope;
133 }
134
135 return p;
136 }
137 /**We have to construct the item from the pixmap, else the icon will be marked as "load on demand",
138 * and for some reason will be loaded every time it's used(this function returns a QIcon marked "load on demand"
139 * each time this is called). And the loading is very slow. Seems like a bug somewhere, it cannot be ment to be that slow.
140 */
141 #define RETURN_CACHED_ICON(name) {static QIcon icon(QIcon( \
142 QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kdevelop/pics/" name ".png"))\
143 ).pixmap(QSize(16, 16)));\
144 return icon;}
145
iconForProperties(KTextEditor::CodeCompletionModel::CompletionProperties p)146 QIcon DUChainUtils::iconForProperties(KTextEditor::CodeCompletionModel::CompletionProperties p)
147 {
148
149 if( (p & CodeCompletionModel::Variable) )
150 if( (p & CodeCompletionModel::Protected) )
151 RETURN_CACHED_ICON("CVprotected_var")
152 else if( p & CodeCompletionModel::Private )
153 RETURN_CACHED_ICON("CVprivate_var")
154 else
155 RETURN_CACHED_ICON("CVpublic_var")
156 else
157 if( (p & CodeCompletionModel::Union) && (p & CodeCompletionModel::Protected) )
158 RETURN_CACHED_ICON("protected_union")
159
160 else if( p & CodeCompletionModel::Enum )
161 if( p & CodeCompletionModel::Protected )
162 RETURN_CACHED_ICON("protected_enum")
163 else if( p & CodeCompletionModel::Private )
164 RETURN_CACHED_ICON("private_enum")
165 else
166 RETURN_CACHED_ICON("enum")
167
168 else if( p & CodeCompletionModel::Struct )
169 if( p & CodeCompletionModel::Private )
170 RETURN_CACHED_ICON("private_struct")
171 else
172 RETURN_CACHED_ICON("struct")
173
174 else if( p & CodeCompletionModel::Slot )
175 if( p & CodeCompletionModel::Protected )
176 RETURN_CACHED_ICON("CVprotected_slot")
177 else if( p & CodeCompletionModel::Private )
178 RETURN_CACHED_ICON("CVprivate_slot")
179 else if(p & CodeCompletionModel::Public )
180 RETURN_CACHED_ICON("CVpublic_slot")
181 else RETURN_CACHED_ICON("slot")
182 else if( p & CodeCompletionModel::Signal )
183 if( p & CodeCompletionModel::Protected )
184 RETURN_CACHED_ICON("CVprotected_signal")
185 else
186 RETURN_CACHED_ICON("signal")
187
188 else if( p & CodeCompletionModel::Class )
189 if( (p & CodeCompletionModel::Class) && (p & CodeCompletionModel::Protected) )
190 RETURN_CACHED_ICON("protected_class")
191 else if( (p & CodeCompletionModel::Class) && (p & CodeCompletionModel::Private) )
192 RETURN_CACHED_ICON("private_class")
193 else
194 RETURN_CACHED_ICON("code-class")
195
196 else if( p & CodeCompletionModel::Union )
197 if( p & CodeCompletionModel::Private )
198 RETURN_CACHED_ICON("private_union")
199 else
200 RETURN_CACHED_ICON("union")
201
202 else if( p & CodeCompletionModel::TypeAlias )
203 if ((p & CodeCompletionModel::Const) /*|| (p & CodeCompletionModel::Volatile)*/)
204 RETURN_CACHED_ICON("CVtypedef")
205 else
206 RETURN_CACHED_ICON("typedef")
207
208 else if( p & CodeCompletionModel::Function ) {
209 if( p & CodeCompletionModel::Protected )
210 RETURN_CACHED_ICON("protected_function")
211 else if( p & CodeCompletionModel::Private )
212 RETURN_CACHED_ICON("private_function")
213 else
214 RETURN_CACHED_ICON("code-function")
215 }
216
217 if( p & CodeCompletionModel::Protected )
218 RETURN_CACHED_ICON("protected_field")
219 else if( p & CodeCompletionModel::Private )
220 RETURN_CACHED_ICON("private_field")
221 else
222 RETURN_CACHED_ICON("field")
223
224 return QIcon();
225 }
226
iconForDeclaration(const Declaration * dec)227 QIcon DUChainUtils::iconForDeclaration(const Declaration* dec)
228 {
229 return iconForProperties(completionProperties(dec));
230 }
231
contentContextFromProxyContext(TopDUContext * top)232 TopDUContext* DUChainUtils::contentContextFromProxyContext(TopDUContext* top)
233 {
234 if(!top)
235 return nullptr;
236 if(top->parsingEnvironmentFile() && top->parsingEnvironmentFile()->isProxyContext()) {
237 if(!top->importedParentContexts().isEmpty())
238 {
239 DUContext* ctx = top->importedParentContexts().at(0).context(nullptr);
240 if(!ctx)
241 return nullptr;
242 TopDUContext* ret = ctx->topContext();
243 if(!ret)
244 return nullptr;
245 if(ret->url() != top->url())
246 qCDebug(LANGUAGE) << "url-mismatch between content and proxy:" << top->url().toUrl() << ret->url().toUrl();
247 if(ret->url() == top->url() && !ret->parsingEnvironmentFile()->isProxyContext())
248 return ret;
249 }
250 else {
251 qCDebug(LANGUAGE) << "Proxy-context imports no content-context";
252 }
253 } else
254 return top;
255 return nullptr;
256 }
257
standardContextForUrl(const QUrl & url,bool preferProxyContext)258 TopDUContext* DUChainUtils::standardContextForUrl(const QUrl& url, bool preferProxyContext) {
259 KDevelop::TopDUContext* chosen = nullptr;
260
261 const auto languages = ICore::self()->languageController()->languagesForUrl(url);
262
263 for (const auto language : languages) {
264 if(!chosen)
265 {
266 chosen = language->standardContext(url, preferProxyContext);
267 }
268 }
269
270 if(!chosen)
271 chosen = DUChain::self()->chainForDocument(IndexedString(url), preferProxyContext);
272
273 if(!chosen && preferProxyContext)
274 return standardContextForUrl(url, false); // Fall back to a normal context
275
276 return chosen;
277 }
278
279 struct ItemUnderCursorInternal
280 {
281 Declaration* declaration;
282 DUContext* context;
283 RangeInRevision range;
284 };
285
itemUnderCursorInternal(const CursorInRevision & c,DUContext * ctx,RangeInRevision::ContainsBehavior behavior)286 ItemUnderCursorInternal itemUnderCursorInternal(const CursorInRevision& c, DUContext* ctx, RangeInRevision::ContainsBehavior behavior)
287 {
288 //Search all collapsed sub-contexts. In C++, those can contain declarations that have ranges out of the context
289 const auto childContexts = ctx->childContexts();
290 for (DUContext* subCtx : childContexts) {
291 //This is a little hacky, but we need it in case of foreach macros and similar stuff
292 if(subCtx->range().contains(c, behavior) || subCtx->range().isEmpty() || subCtx->range().start.line == c.line || subCtx->range().end.line == c.line) {
293 ItemUnderCursorInternal sub = itemUnderCursorInternal(c, subCtx, behavior);
294 if(sub.declaration) {
295 return sub;
296 }
297 }
298 }
299
300 const auto localDeclarations = ctx->localDeclarations();
301 for (Declaration* decl : localDeclarations) {
302 if(decl->range().contains(c, behavior)) {
303 return {decl, ctx, decl->range()};
304 }
305 }
306
307 //Try finding a use under the cursor
308 for(int a = 0; a < ctx->usesCount(); ++a) {
309 if(ctx->uses()[a].m_range.contains(c, behavior)) {
310 return {ctx->topContext()->usedDeclarationForIndex(ctx->uses()[a].m_declarationIndex), ctx, ctx->uses()[a].m_range};
311 }
312 }
313
314 return {nullptr, nullptr, RangeInRevision()};
315 }
316
itemUnderCursor(const QUrl & url,const KTextEditor::Cursor & cursor)317 DUChainUtils::ItemUnderCursor DUChainUtils::itemUnderCursor(const QUrl& url, const KTextEditor::Cursor& cursor)
318 {
319 KDevelop::TopDUContext* top = standardContextForUrl(url.adjusted(QUrl::NormalizePathSegments));
320
321 if(!top) {
322 return {nullptr, nullptr, KTextEditor::Range()};
323 }
324
325 ItemUnderCursorInternal decl = itemUnderCursorInternal(top->transformToLocalRevision(cursor), top, RangeInRevision::Default);
326 if (decl.declaration == nullptr)
327 {
328 decl = itemUnderCursorInternal(top->transformToLocalRevision(cursor), top, RangeInRevision::IncludeBackEdge);
329 }
330 return {decl.declaration, decl.context, top->transformFromLocalRevision(decl.range)};
331 }
332
declarationForDefinition(Declaration * definition,TopDUContext * topContext)333 Declaration* DUChainUtils::declarationForDefinition(Declaration* definition, TopDUContext* topContext)
334 {
335 if(!definition)
336 return nullptr;
337
338 if(!topContext)
339 topContext = definition->topContext();
340
341 if(dynamic_cast<FunctionDefinition*>(definition)) {
342 Declaration* ret = static_cast<FunctionDefinition*>(definition)->declaration();
343 if(ret)
344 return ret;
345 }
346
347 return definition;
348 }
349
declarationInLine(const KTextEditor::Cursor & _cursor,DUContext * ctx)350 Declaration* DUChainUtils::declarationInLine(const KTextEditor::Cursor& _cursor, DUContext* ctx) {
351 if(!ctx)
352 return nullptr;
353
354 CursorInRevision cursor = ctx->transformToLocalRevision(_cursor);
355
356 const auto localDeclarations = ctx->localDeclarations();
357 for (Declaration* decl : localDeclarations) {
358 if(decl->range().start.line == cursor.line)
359 return decl;
360 DUContext* funCtx = functionContext(decl);
361 if(funCtx && funCtx->range().contains(cursor))
362 return decl;
363 }
364
365 const auto childContexts = ctx->childContexts();
366 for (DUContext* child : childContexts){
367 Declaration* decl = declarationInLine(_cursor, child);
368 if(decl)
369 return decl;
370 }
371
372 return nullptr;
373 }
374
~DUChainItemFilter()375 DUChainUtils::DUChainItemFilter::~DUChainItemFilter() {
376 }
377
collectItems(DUContext * context,DUChainItemFilter & filter)378 void DUChainUtils::collectItems( DUContext* context, DUChainItemFilter& filter ) {
379
380 QVector<DUContext*> children = context->childContexts();
381 QVector<Declaration*> localDeclarations = context->localDeclarations();
382
383 QVector<DUContext*>::const_iterator childIt = children.constBegin();
384 QVector<Declaration*>::const_iterator declIt = localDeclarations.constBegin();
385
386 while(childIt != children.constEnd() || declIt != localDeclarations.constEnd()) {
387
388 DUContext* child = nullptr;
389 if(childIt != children.constEnd())
390 child = *childIt;
391
392 Declaration* decl = nullptr;
393 if(declIt != localDeclarations.constEnd())
394 decl = *declIt;
395
396 if(decl) {
397 if(child && child->range().start.line >= decl->range().start.line)
398 child = nullptr;
399 }
400
401 if(child) {
402 if(decl && decl->range().start >= child->range().start)
403 decl = nullptr;
404 }
405
406 if(decl) {
407 if( filter.accept(decl) ) {
408 //Action is done in the filter
409 }
410
411 ++declIt;
412 continue;
413 }
414
415 if(child) {
416 if( filter.accept(child) )
417 collectItems(child, filter);
418 ++childIt;
419 continue;
420 }
421 }
422 }
423
argumentContext(KDevelop::Declaration * decl)424 KDevelop::DUContext* DUChainUtils::argumentContext(KDevelop::Declaration* decl) {
425 DUContext* internal = decl->internalContext();
426 if( !internal )
427 return nullptr;
428 if( internal->type() == DUContext::Function )
429 return internal;
430 const auto importedParentContexts = internal->importedParentContexts();
431 for (const DUContext::Import& ctx : importedParentContexts) {
432 if( ctx.context(decl->topContext()) )
433 if( ctx.context(decl->topContext())->type() == DUContext::Function )
434 return ctx.context(decl->topContext());
435 }
436 return nullptr;
437 }
438
collectAllVersions(Declaration * decl)439 QList<IndexedDeclaration> DUChainUtils::collectAllVersions(Declaration* decl) {
440 QList<IndexedDeclaration> ret;
441 ret << IndexedDeclaration(decl);
442
443 if(decl->inSymbolTable())
444 {
445 uint count;
446 const IndexedDeclaration* allDeclarations;
447 PersistentSymbolTable::self().declarations(decl->qualifiedIdentifier(), count, allDeclarations);
448 for(uint a = 0; a < count; ++a)
449 if(!(allDeclarations[a] == IndexedDeclaration(decl)))
450 ret << allDeclarations[a];
451 }
452
453 return ret;
454 }
455
inheritersInternal(const Declaration * decl,uint & maxAllowedSteps,bool collectVersions)456 static QList<Declaration*> inheritersInternal(const Declaration* decl, uint& maxAllowedSteps, bool collectVersions)
457 {
458 QList<Declaration*> ret;
459
460 if(!dynamic_cast<const ClassDeclaration*>(decl))
461 return ret;
462
463 if(maxAllowedSteps == 0)
464 return ret;
465
466 if(decl->internalContext() && decl->internalContext()->type() == DUContext::Class) {
467 const auto indexedImporters = decl->internalContext()->indexedImporters();
468 for (const IndexedDUContext importer : indexedImporters) {
469
470 DUContext* imp = importer.data();
471
472 if(!imp)
473 continue;
474
475 if(imp->type() == DUContext::Class && imp->owner())
476 ret << imp->owner();
477
478 --maxAllowedSteps;
479
480 if(maxAllowedSteps == 0)
481 return ret;
482 }
483 }
484
485 if(collectVersions && decl->inSymbolTable()) {
486 uint count;
487 const IndexedDeclaration* allDeclarations;
488 PersistentSymbolTable::self().declarations(decl->qualifiedIdentifier(), count, allDeclarations);
489 for(uint a = 0; a < count; ++a) {
490 ++maxAllowedSteps;
491
492 if(allDeclarations[a].data() && allDeclarations[a].data() != decl) {
493 ret += inheritersInternal(allDeclarations[a].data(), maxAllowedSteps, false);
494 }
495
496 if(maxAllowedSteps == 0)
497 return ret;
498 }
499 }
500
501 return ret;
502 }
503
inheriters(const Declaration * decl,uint & maxAllowedSteps,bool collectVersions)504 QList<Declaration*> DUChainUtils::inheriters(const Declaration* decl, uint& maxAllowedSteps, bool collectVersions)
505 {
506 auto inheriters = inheritersInternal(decl, maxAllowedSteps, collectVersions);
507
508 // remove duplicates
509 std::sort(inheriters.begin(), inheriters.end());
510 inheriters.erase(std::unique(inheriters.begin(), inheriters.end()), inheriters.end());
511
512 return inheriters;
513 }
514
overriders(const Declaration * currentClass,const Declaration * overriddenDeclaration,uint & maxAllowedSteps)515 QList<Declaration*> DUChainUtils::overriders(const Declaration* currentClass, const Declaration* overriddenDeclaration, uint& maxAllowedSteps) {
516 QList<Declaration*> ret;
517
518 if(maxAllowedSteps == 0)
519 return ret;
520
521 if(currentClass != overriddenDeclaration->context()->owner() && currentClass->internalContext())
522 ret += currentClass->internalContext()->findLocalDeclarations(overriddenDeclaration->identifier(), CursorInRevision::invalid(), currentClass->topContext(), overriddenDeclaration->abstractType());
523
524 const auto inheriters = DUChainUtils::inheriters(currentClass, maxAllowedSteps);
525 for (Declaration* inheriter : inheriters) {
526 ret += overriders(inheriter, overriddenDeclaration, maxAllowedSteps);
527 }
528
529 return ret;
530 }
531
hasUse(DUContext * context,int usedDeclarationIndex)532 static bool hasUse(DUContext* context, int usedDeclarationIndex) {
533 if(usedDeclarationIndex == std::numeric_limits<int>::max())
534 return false;
535
536 for(int a = 0; a < context->usesCount(); ++a)
537 if(context->uses()[a].m_declarationIndex == usedDeclarationIndex)
538 return true;
539
540 const auto childContexts = context->childContexts();
541 return std::any_of(childContexts.begin(), childContexts.end(), [&](DUContext* child) {
542 return hasUse(child, usedDeclarationIndex);
543 });
544 }
545
contextHasUse(DUContext * context,Declaration * declaration)546 bool DUChainUtils::contextHasUse(DUContext* context, Declaration* declaration) {
547 return hasUse(context, context->topContext()->indexForUsedDeclaration(declaration, false));
548 }
549
countUses(DUContext * context,int usedDeclarationIndex)550 static uint countUses(DUContext* context, int usedDeclarationIndex) {
551 if(usedDeclarationIndex == std::numeric_limits<int>::max())
552 return 0;
553
554 uint ret = 0;
555
556 for(int a = 0; a < context->usesCount(); ++a)
557 if(context->uses()[a].m_declarationIndex == usedDeclarationIndex)
558 ++ret;
559
560 const auto childContexts = context->childContexts();
561 for (DUContext* child : childContexts) {
562 ret += countUses(child, usedDeclarationIndex);
563 }
564
565 return ret;
566 }
567
contextCountUses(DUContext * context,Declaration * declaration)568 uint DUChainUtils::contextCountUses(DUContext* context, Declaration* declaration) {
569 return countUses(context, context->topContext()->indexForUsedDeclaration(declaration, false));
570 }
571
overridden(const Declaration * decl)572 Declaration* DUChainUtils::overridden(const Declaration* decl) {
573 const auto* classFunDecl = dynamic_cast<const ClassFunctionDeclaration*>(decl);
574 if(!classFunDecl || !classFunDecl->isVirtual())
575 return nullptr;
576
577 QList<Declaration*> decls;
578
579 const auto importedParentContexts = decl->context()->importedParentContexts();
580 for (const DUContext::Import &import : importedParentContexts) {
581 DUContext* ctx = import.context(decl->topContext());
582 if(ctx)
583 decls += ctx->findDeclarations(QualifiedIdentifier(decl->identifier()),
584 CursorInRevision::invalid(), decl->abstractType(), decl->topContext(), DUContext::DontSearchInParent);
585 }
586
587 auto it = std::find_if(decls.constBegin(), decls.constEnd(), [&](Declaration* found) {
588 const auto* foundClassFunDecl = dynamic_cast<const ClassFunctionDeclaration*>(found);
589 return (foundClassFunDecl && foundClassFunDecl->isVirtual());
590 });
591
592 return (it != decls.constEnd()) ? *it : nullptr;
593 }
594
functionContext(Declaration * decl)595 DUContext* DUChainUtils::functionContext(Declaration* decl) {
596 DUContext* functionContext = decl->internalContext();
597 if(functionContext && functionContext->type() != DUContext::Function) {
598 const auto importedParentContexts = functionContext->importedParentContexts();
599 for (const DUContext::Import& import : importedParentContexts) {
600 DUContext* ctx = import.context(decl->topContext());
601 if(ctx && ctx->type() == DUContext::Function)
602 functionContext = ctx;
603 }
604 }
605
606 if(functionContext && functionContext->type() == DUContext::Function)
607 return functionContext;
608 return nullptr;
609 }
610
allProblemsForContext(const KDevelop::ReferencedTopDUContext & top)611 QVector<KDevelop::Problem::Ptr> KDevelop::DUChainUtils::allProblemsForContext(const KDevelop::ReferencedTopDUContext& top)
612 {
613 QVector<KDevelop::Problem::Ptr> ret;
614
615 const auto problems = top->problems();
616 const auto contextProblems = ICore::self()->languageController()->staticAssistantsManager()->problemsForContext(top);
617 ret.reserve(problems.size() + contextProblems.size());
618
619 for (const auto& p : problems) {
620 ret << p;
621 }
622 for (const auto& p : contextProblems) {
623 ret << p;
624 }
625
626 return ret;
627 }
628
629