• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
isInsideDifferentNamespaceWithSameName(const DeclContext * DeclA,const DeclContext * DeclB)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 
getBestNamespaceSubstr(const DeclContext * DeclA,StringRef NewName,bool HadLeadingColonColon)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 "::".
isFullyQualified(const NestedNameSpecifier * NNS)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 
replaceNestedName(const NestedNameSpecifier * Use,const DeclContext * UseContext,const NamedDecl * FromDecl,StringRef ReplacementString)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