Home | History | Annotate | Download | only in Core
      1 //===--- Lookup.cpp - Framework for clang refactoring tools ---------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 //  This file defines helper methods for clang tools performing name lookup.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #include "clang/Tooling/Core/Lookup.h"
     15 #include "clang/AST/Decl.h"
     16 using namespace clang;
     17 using namespace clang::tooling;
     18 
     19 static bool isInsideDifferentNamespaceWithSameName(const DeclContext *DeclA,
     20                                                    const DeclContext *DeclB) {
     21   while (true) {
     22     // Look past non-namespaces on DeclA.
     23     while (DeclA && !isa<NamespaceDecl>(DeclA))
     24       DeclA = DeclA->getParent();
     25 
     26     // Look past non-namespaces on DeclB.
     27     while (DeclB && !isa<NamespaceDecl>(DeclB))
     28       DeclB = DeclB->getParent();
     29 
     30     // We hit the root, no namespace collision.
     31     if (!DeclA || !DeclB)
     32       return false;
     33 
     34     // Literally the same namespace, not a collision.
     35     if (DeclA == DeclB)
     36       return false;
     37 
     38     // Now check the names. If they match we have a different namespace with the
     39     // same name.
     40     if (cast<NamespaceDecl>(DeclA)->getDeclName() ==
     41         cast<NamespaceDecl>(DeclB)->getDeclName())
     42       return true;
     43 
     44     DeclA = DeclA->getParent();
     45     DeclB = DeclB->getParent();
     46   }
     47 }
     48 
     49 static StringRef getBestNamespaceSubstr(const DeclContext *DeclA,
     50                                         StringRef NewName,
     51                                         bool HadLeadingColonColon) {
     52   while (true) {
     53     while (DeclA && !isa<NamespaceDecl>(DeclA))
     54       DeclA = DeclA->getParent();
     55 
     56     // Fully qualified it is! Leave :: in place if it's there already.
     57     if (!DeclA)
     58       return HadLeadingColonColon ? NewName : NewName.substr(2);
     59 
     60     // Otherwise strip off redundant namespace qualifications from the new name.
     61     // We use the fully qualified name of the namespace and remove that part
     62     // from NewName if it has an identical prefix.
     63     std::string NS =
     64         "::" + cast<NamespaceDecl>(DeclA)->getQualifiedNameAsString() + "::";
     65     if (NewName.startswith(NS))
     66       return NewName.substr(NS.size());
     67 
     68     // No match yet. Strip of a namespace from the end of the chain and try
     69     // again. This allows to get optimal qualifications even if the old and new
     70     // decl only share common namespaces at a higher level.
     71     DeclA = DeclA->getParent();
     72   }
     73 }
     74 
     75 /// Check if the name specifier begins with a written "::".
     76 static bool isFullyQualified(const NestedNameSpecifier *NNS) {
     77   while (NNS) {
     78     if (NNS->getKind() == NestedNameSpecifier::Global)
     79       return true;
     80     NNS = NNS->getPrefix();
     81   }
     82   return false;
     83 }
     84 
     85 std::string tooling::replaceNestedName(const NestedNameSpecifier *Use,
     86                                        const DeclContext *UseContext,
     87                                        const NamedDecl *FromDecl,
     88                                        StringRef ReplacementString) {
     89   assert(ReplacementString.startswith("::") &&
     90          "Expected fully-qualified name!");
     91 
     92   // We can do a raw name replacement when we are not inside the namespace for
     93   // the original function and it is not in the global namespace.  The
     94   // assumption is that outside the original namespace we must have a using
     95   // statement that makes this work out and that other parts of this refactor
     96   // will automatically fix using statements to point to the new function
     97   const bool class_name_only = !Use;
     98   const bool in_global_namespace =
     99       isa<TranslationUnitDecl>(FromDecl->getDeclContext());
    100   if (class_name_only && !in_global_namespace &&
    101       !isInsideDifferentNamespaceWithSameName(FromDecl->getDeclContext(),
    102                                               UseContext)) {
    103     auto Pos = ReplacementString.rfind("::");
    104     return Pos != StringRef::npos ? ReplacementString.substr(Pos + 2)
    105                                   : ReplacementString;
    106   }
    107   // We did not match this because of a using statement, so we will need to
    108   // figure out how good a namespace match we have with our destination type.
    109   // We work backwards (from most specific possible namespace to least
    110   // specific).
    111   return getBestNamespaceSubstr(UseContext, ReplacementString,
    112                                 isFullyQualified(Use));
    113 }
    114