"Fossies" - the Fresh Open Source Software Archive

Member "qt-creator-opensource-src-4.15.1/src/plugins/cpptools/symbolfinder.cpp" (8 Jun 2021, 18269 Bytes) of package /linux/misc/qt-creator-opensource-src-4.15.1.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "symbolfinder.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: opensource-src-4.15.0_vs_opensource-src-4.15.1.

    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 #if defined(_MSC_VER)
   27 #pragma warning(disable:4996)
   28 #endif
   29 
   30 #include "symbolfinder.h"
   31 
   32 #include "cppmodelmanager.h"
   33 
   34 #include <cplusplus/LookupContext.h>
   35 
   36 #include <utils/qtcassert.h>
   37 
   38 #include <QDebug>
   39 #include <QPair>
   40 
   41 #include <algorithm>
   42 #include <utility>
   43 
   44 using namespace CPlusPlus;
   45 using namespace CppTools;
   46 
   47 namespace {
   48 
   49 struct Hit {
   50     Hit(Function *func, bool exact) : func(func), exact(exact) {}
   51     Hit() = default;
   52 
   53     Function *func = nullptr;
   54     bool exact = false;
   55 };
   56 
   57 class FindMatchingDefinition: public SymbolVisitor
   58 {
   59     Symbol *_declaration = nullptr;
   60     const OperatorNameId *_oper = nullptr;
   61     const ConversionNameId *_conv = nullptr;
   62     const bool _strict;
   63     QList<Hit> _result;
   64 
   65 public:
   66     explicit FindMatchingDefinition(Symbol *declaration, bool strict)
   67         : _declaration(declaration), _strict(strict)
   68     {
   69         if (_declaration->name()) {
   70             _oper = _declaration->name()->asOperatorNameId();
   71             _conv = _declaration->name()->asConversionNameId();
   72         }
   73     }
   74 
   75     const QList<Hit> result() const { return _result; }
   76 
   77     using SymbolVisitor::visit;
   78 
   79     bool visit(Function *fun) override
   80     {
   81         if (_oper || _conv) {
   82             if (const Name *name = fun->unqualifiedName()) {
   83                 if ((_oper && _oper->match(name)) || (_conv && _conv->match(name)))
   84                     _result.append({fun, true});
   85             }
   86         } else if (Function *decl = _declaration->type()->asFunctionType()) {
   87             if (fun->match(decl)) {
   88                 _result.prepend({fun, true});
   89             } else if (!_strict
   90                        && Matcher::match(fun->unqualifiedName(), decl->unqualifiedName())) {
   91                 _result.append({fun, false});
   92             }
   93         }
   94 
   95         return false;
   96     }
   97 
   98     bool visit(Block *) override
   99     {
  100         return false;
  101     }
  102 };
  103 
  104 class FindMatchingVarDefinition: public SymbolVisitor
  105 {
  106     Symbol *_declaration = nullptr;
  107     QList<Declaration *> _result;
  108     const Identifier *_className = nullptr;
  109 
  110 public:
  111     explicit FindMatchingVarDefinition(Symbol *declaration)
  112         : _declaration(declaration)
  113     {
  114         if (declaration->isStatic() && declaration->enclosingScope()->asClass()
  115                 && declaration->enclosingClass()->asClass()->name()) {
  116             _className = declaration->enclosingScope()->name()->identifier();
  117         }
  118     }
  119 
  120     const QList<Declaration *> result() const { return _result; }
  121 
  122     using SymbolVisitor::visit;
  123 
  124     bool visit(Declaration *decl) override
  125     {
  126         if (!decl->type()->match(_declaration->type().type()))
  127             return false;
  128         if (!_declaration->identifier()->equalTo(decl->identifier()))
  129             return false;
  130         if (_className) {
  131             const QualifiedNameId * const qualName = decl->name()->asQualifiedNameId();
  132             if (!qualName)
  133                 return false;
  134             if (!qualName->base() || !qualName->base()->identifier()->equalTo(_className))
  135                 return false;
  136         }
  137         _result.append(decl);
  138         return false;
  139     }
  140 
  141     bool visit(Block *) override { return false; }
  142 };
  143 
  144 } // end of anonymous namespace
  145 
  146 static const int kMaxCacheSize = 10;
  147 
  148 SymbolFinder::SymbolFinder() = default;
  149 
  150 // strict means the returned symbol has to match exactly,
  151 // including argument count, argument types, constness and volatileness.
  152 Function *SymbolFinder::findMatchingDefinition(Symbol *declaration,
  153                                              const Snapshot &snapshot,
  154                                              bool strict)
  155 {
  156     if (!declaration)
  157         return nullptr;
  158 
  159     QString declFile = QString::fromUtf8(declaration->fileName(), declaration->fileNameLength());
  160 
  161     Document::Ptr thisDocument = snapshot.document(declFile);
  162     if (!thisDocument) {
  163         qWarning() << "undefined document:" << declaration->fileName();
  164         return nullptr;
  165     }
  166 
  167     Function *declarationTy = declaration->type()->asFunctionType();
  168     if (!declarationTy) {
  169         qWarning() << "not a function:" << declaration->fileName()
  170                    << declaration->line() << declaration->column();
  171         return nullptr;
  172     }
  173 
  174     Hit best;
  175     foreach (const QString &fileName, fileIterationOrder(declFile, snapshot)) {
  176         Document::Ptr doc = snapshot.document(fileName);
  177         if (!doc) {
  178             clearCache(declFile, fileName);
  179             continue;
  180         }
  181 
  182         const Identifier *id = declaration->identifier();
  183         if (id && !doc->control()->findIdentifier(id->chars(), id->size()))
  184             continue;
  185 
  186         if (!id) {
  187             const Name * const name = declaration->name();
  188             if (!name)
  189                 continue;
  190             if (const OperatorNameId * const oper = name->asOperatorNameId()) {
  191                 if (!doc->control()->findOperatorNameId(oper->kind()))
  192                     continue;
  193             } else if (const ConversionNameId * const conv = name->asConversionNameId()) {
  194                 if (!doc->control()->findConversionNameId(conv->type()))
  195                     continue;
  196             } else {
  197                 continue;
  198             }
  199         }
  200 
  201         FindMatchingDefinition candidates(declaration, strict);
  202         candidates.accept(doc->globalNamespace());
  203 
  204         const QList<Hit> result = candidates.result();
  205         if (result.isEmpty())
  206             continue;
  207 
  208         LookupContext context(doc, snapshot);
  209         ClassOrNamespace *enclosingType = context.lookupType(declaration);
  210         if (!enclosingType)
  211             continue; // nothing to do
  212 
  213         for (const Hit &hit : result) {
  214             QTC_CHECK(!strict || hit.exact);
  215 
  216             const QList<LookupItem> declarations = context.lookup(hit.func->name(),
  217                                                                   hit.func->enclosingScope());
  218             if (declarations.isEmpty())
  219                 continue;
  220             if (enclosingType != context.lookupType(declarations.first().declaration()))
  221                 continue;
  222 
  223             if (hit.exact)
  224                 return hit.func;
  225 
  226             if (!best.func || hit.func->argumentCount() == declarationTy->argumentCount())
  227                 best = hit;
  228         }
  229     }
  230 
  231     QTC_CHECK(!best.exact);
  232     return strict ? nullptr : best.func;
  233 }
  234 
  235 Symbol *SymbolFinder::findMatchingVarDefinition(Symbol *declaration, const Snapshot &snapshot)
  236 {
  237     if (!declaration)
  238         return nullptr;
  239     for (const Scope *s = declaration->enclosingScope(); s; s = s->enclosingScope()) {
  240         if (s->asBlock())
  241             return nullptr;
  242     }
  243 
  244     QString declFile = QString::fromUtf8(declaration->fileName(), declaration->fileNameLength());
  245     const Document::Ptr thisDocument = snapshot.document(declFile);
  246     if (!thisDocument) {
  247         qWarning() << "undefined document:" << declaration->fileName();
  248         return nullptr;
  249     }
  250 
  251     using SymbolWithPriority = QPair<Symbol *, bool>;
  252     QList<SymbolWithPriority> candidates;
  253     QList<SymbolWithPriority> fallbacks;
  254     foreach (const QString &fileName, fileIterationOrder(declFile, snapshot)) {
  255         Document::Ptr doc = snapshot.document(fileName);
  256         if (!doc) {
  257             clearCache(declFile, fileName);
  258             continue;
  259         }
  260 
  261         const Identifier *id = declaration->identifier();
  262         if (id && !doc->control()->findIdentifier(id->chars(), id->size()))
  263             continue;
  264 
  265         FindMatchingVarDefinition finder(declaration);
  266         finder.accept(doc->globalNamespace());
  267         if (finder.result().isEmpty())
  268             continue;
  269 
  270         LookupContext context(doc, snapshot);
  271         ClassOrNamespace * const enclosingType = context.lookupType(declaration);
  272         for (Symbol * const symbol : finder.result()) {
  273             const QList<LookupItem> items = context.lookup(symbol->name(),
  274                                                            symbol->enclosingScope());
  275             bool addFallback = true;
  276             for (const LookupItem &item : items) {
  277                 if (item.declaration() == symbol)
  278                     addFallback = false;
  279                 candidates << qMakePair(item.declaration(),
  280                                         context.lookupType(item.declaration()) == enclosingType);
  281             }
  282             // TODO: This is a workaround for static member definitions not being found by
  283             //       the lookup() function.
  284             if (addFallback)
  285                 fallbacks << qMakePair(symbol, context.lookupType(symbol) == enclosingType);
  286         }
  287     }
  288 
  289     candidates << fallbacks;
  290     SymbolWithPriority best;
  291     for (const auto &candidate : qAsConst(candidates)) {
  292         if (candidate.first == declaration)
  293             continue;
  294         if (QLatin1String(candidate.first->fileName()) == declFile
  295                 && candidate.first->sourceLocation() == declaration->sourceLocation())
  296             continue;
  297         if (!candidate.first->asDeclaration())
  298             continue;
  299         if (declaration->isExtern() && candidate.first->isStatic())
  300             continue;
  301         if (!best.first) {
  302             best = candidate;
  303             continue;
  304         }
  305         if (!best.second && candidate.second) {
  306             best = candidate;
  307             continue;
  308         }
  309         if (best.first->isExtern() && !candidate.first->isExtern())
  310             best = candidate;
  311     }
  312 
  313     return best.first;
  314 }
  315 
  316 Class *SymbolFinder::findMatchingClassDeclaration(Symbol *declaration, const Snapshot &snapshot)
  317 {
  318     if (!declaration->identifier())
  319         return nullptr;
  320 
  321     QString declFile = QString::fromUtf8(declaration->fileName(), declaration->fileNameLength());
  322 
  323     foreach (const QString &file, fileIterationOrder(declFile, snapshot)) {
  324         Document::Ptr doc = snapshot.document(file);
  325         if (!doc) {
  326             clearCache(declFile, file);
  327             continue;
  328         }
  329 
  330         if (!doc->control()->findIdentifier(declaration->identifier()->chars(),
  331                                             declaration->identifier()->size()))
  332             continue;
  333 
  334         LookupContext context(doc, snapshot);
  335 
  336         ClassOrNamespace *type = context.lookupType(declaration);
  337         if (!type)
  338             continue;
  339 
  340         foreach (Symbol *s, type->symbols()) {
  341             if (Class *c = s->asClass())
  342                 return c;
  343         }
  344     }
  345 
  346     return nullptr;
  347 }
  348 
  349 static void findDeclarationOfSymbol(Symbol *s,
  350                                     Function *functionType,
  351                                     QList<Declaration *> *typeMatch,
  352                                     QList<Declaration *> *argumentCountMatch,
  353                                     QList<Declaration *> *nameMatch)
  354 {
  355     if (Declaration *decl = s->asDeclaration()) {
  356         if (Function *declFunTy = decl->type()->asFunctionType()) {
  357             if (functionType->match(declFunTy))
  358                 typeMatch->prepend(decl);
  359             else if (functionType->argumentCount() == declFunTy->argumentCount())
  360                 argumentCountMatch->prepend(decl);
  361             else
  362                 nameMatch->append(decl);
  363         }
  364     }
  365 }
  366 
  367 void SymbolFinder::findMatchingDeclaration(const LookupContext &context,
  368                                            Function *functionType,
  369                                            QList<Declaration *> *typeMatch,
  370                                            QList<Declaration *> *argumentCountMatch,
  371                                            QList<Declaration *> *nameMatch)
  372 {
  373     if (!functionType)
  374         return;
  375 
  376     Scope *enclosingScope = functionType->enclosingScope();
  377     while (!(enclosingScope->isNamespace() || enclosingScope->isClass()))
  378         enclosingScope = enclosingScope->enclosingScope();
  379     QTC_ASSERT(enclosingScope != nullptr, return);
  380 
  381     const Name *functionName = functionType->name();
  382     if (!functionName)
  383         return;
  384 
  385     ClassOrNamespace *binding = nullptr;
  386     const QualifiedNameId *qName = functionName->asQualifiedNameId();
  387     if (qName) {
  388         if (qName->base())
  389             binding = context.lookupType(qName->base(), enclosingScope);
  390         else
  391             binding = context.globalNamespace();
  392         functionName = qName->name();
  393     }
  394 
  395     if (!binding) { // declaration for a global function
  396         binding = context.lookupType(enclosingScope);
  397 
  398         if (!binding)
  399             return;
  400     }
  401 
  402     const Identifier *funcId = functionName->identifier();
  403     OperatorNameId::Kind operatorNameId = OperatorNameId::InvalidOp;
  404 
  405     if (!funcId) {
  406         if (!qName)
  407             return;
  408         const OperatorNameId * const onid = qName->name()->asOperatorNameId();
  409         if (!onid)
  410             return;
  411         operatorNameId = onid->kind();
  412     }
  413 
  414     foreach (Symbol *s, binding->symbols()) {
  415         Scope *scope = s->asScope();
  416         if (!scope)
  417             continue;
  418 
  419         if (funcId) {
  420             for (Symbol *s = scope->find(funcId); s; s = s->next()) {
  421                 if (!s->name() || !funcId->match(s->identifier()) || !s->type()->isFunctionType())
  422                     continue;
  423                 findDeclarationOfSymbol(s, functionType, typeMatch, argumentCountMatch, nameMatch);
  424             }
  425         } else {
  426             for (Symbol *s = scope->find(operatorNameId); s; s = s->next()) {
  427                 if (!s->name() || !s->type()->isFunctionType())
  428                     continue;
  429                 findDeclarationOfSymbol(s, functionType, typeMatch, argumentCountMatch, nameMatch);
  430             }
  431         }
  432     }
  433 }
  434 
  435 QList<Declaration *> SymbolFinder::findMatchingDeclaration(const LookupContext &context,
  436                                                            Function *functionType)
  437 {
  438     QList<Declaration *> result;
  439     if (!functionType)
  440         return result;
  441 
  442     QList<Declaration *> nameMatch, argumentCountMatch, typeMatch;
  443     findMatchingDeclaration(context, functionType, &typeMatch, &argumentCountMatch, &nameMatch);
  444     result.append(typeMatch);
  445 
  446     // For member functions not defined inline, add fuzzy matches as fallbacks. We cannot do
  447     // this for free functions, because there is no guarantee that there's a separate declaration.
  448     QList<Declaration *> fuzzyMatches = argumentCountMatch + nameMatch;
  449     if (!functionType->enclosingScope() || !functionType->enclosingScope()->isClass()) {
  450         for (Declaration * const d : fuzzyMatches) {
  451             if (d->enclosingScope() && d->enclosingScope()->isClass())
  452                 result.append(d);
  453         }
  454     }
  455     return result;
  456 }
  457 
  458 QStringList SymbolFinder::fileIterationOrder(const QString &referenceFile, const Snapshot &snapshot)
  459 {
  460     if (m_filePriorityCache.contains(referenceFile)) {
  461         checkCacheConsistency(referenceFile, snapshot);
  462     } else {
  463         foreach (Document::Ptr doc, snapshot)
  464             insertCache(referenceFile, doc->fileName());
  465     }
  466 
  467     QStringList files = m_filePriorityCache.value(referenceFile).toStringList();
  468 
  469     trackCacheUse(referenceFile);
  470 
  471     return files;
  472 }
  473 
  474 void SymbolFinder::clearCache()
  475 {
  476     m_filePriorityCache.clear();
  477     m_fileMetaCache.clear();
  478     m_recent.clear();
  479 }
  480 
  481 void SymbolFinder::checkCacheConsistency(const QString &referenceFile, const Snapshot &snapshot)
  482 {
  483     // We only check for "new" files, which which are in the snapshot but not in the cache.
  484     // The counterpart validation for "old" files is done when one tries to access the
  485     // corresponding document and notices it's now null.
  486     const QSet<QString> &meta = m_fileMetaCache.value(referenceFile);
  487     foreach (const Document::Ptr &doc, snapshot) {
  488         if (!meta.contains(doc->fileName()))
  489             insertCache(referenceFile, doc->fileName());
  490     }
  491 }
  492 
  493 const QString projectPartIdForFile(const QString &filePath)
  494 {
  495     const QList<ProjectPart::Ptr> parts = CppModelManager::instance()->projectPart(filePath);
  496     if (!parts.isEmpty())
  497         return parts.first()->id();
  498     return QString();
  499 }
  500 
  501 void SymbolFinder::clearCache(const QString &referenceFile, const QString &comparingFile)
  502 {
  503     m_filePriorityCache[referenceFile].remove(comparingFile, projectPartIdForFile(comparingFile));
  504     m_fileMetaCache[referenceFile].remove(comparingFile);
  505 }
  506 
  507 void SymbolFinder::insertCache(const QString &referenceFile, const QString &comparingFile)
  508 {
  509     FileIterationOrder &order = m_filePriorityCache[referenceFile];
  510     if (!order.isValid()) {
  511         const auto projectPartId = projectPartIdForFile(referenceFile);
  512         order.setReference(referenceFile, projectPartId);
  513     }
  514     order.insert(comparingFile, projectPartIdForFile(comparingFile));
  515 
  516     m_fileMetaCache[referenceFile].insert(comparingFile);
  517 }
  518 
  519 void SymbolFinder::trackCacheUse(const QString &referenceFile)
  520 {
  521     if (!m_recent.isEmpty()) {
  522         if (m_recent.last() == referenceFile)
  523             return;
  524         m_recent.removeOne(referenceFile);
  525     }
  526 
  527     m_recent.append(referenceFile);
  528 
  529     // We don't want this to grow too much.
  530     if (m_recent.size() > kMaxCacheSize) {
  531         const QString &oldest = m_recent.takeFirst();
  532         m_filePriorityCache.remove(oldest);
  533         m_fileMetaCache.remove(oldest);
  534     }
  535 }