]> granicus.if.org Git - clang/blob - lib/Tooling/Core/Lookup.cpp
84135f441d26f94a544573f5a4b58996acb810e8
[clang] / lib / Tooling / Core / Lookup.cpp
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 // Returns true if the context in which the type is used and the context in
20 // which the type is declared are the same semantical namespace but different
21 // lexical namespaces.
22 static bool
23 usingFromDifferentCanonicalNamespace(const DeclContext *FromContext,
24                                      const DeclContext *UseContext) {
25   while (true) {
26     // Look past non-namespaces and anonymous namespaces on FromContext.
27     // We can skip anonymous namespace because:
28     // 1. `FromContext` and `UseContext` must be in the same anonymous
29     // namespaces since referencing across anonymous namespaces is not possible.
30     // 2. If `FromContext` and `UseContext` are in the same anonymous namespace,
31     // the function will still return `false` as expected.
32     while (FromContext &&
33            (!isa<NamespaceDecl>(FromContext) ||
34             cast<NamespaceDecl>(FromContext)->isAnonymousNamespace()))
35       FromContext = FromContext->getParent();
36
37     // Look past non-namespaces and anonymous namespaces on UseContext.
38     while (UseContext &&
39            (!isa<NamespaceDecl>(UseContext) ||
40             cast<NamespaceDecl>(UseContext)->isAnonymousNamespace()))
41       UseContext = UseContext->getParent();
42
43     // We hit the root, no namespace collision.
44     if (!FromContext || !UseContext)
45       return false;
46
47     // Literally the same namespace, not a collision.
48     if (FromContext == UseContext)
49       return false;
50
51     // Now check the names. If they match we have a different namespace with the
52     // same name.
53     if (cast<NamespaceDecl>(FromContext)->getDeclName() ==
54         cast<NamespaceDecl>(UseContext)->getDeclName())
55       return true;
56
57     FromContext = FromContext->getParent();
58     UseContext = UseContext->getParent();
59   }
60 }
61
62 static StringRef getBestNamespaceSubstr(const DeclContext *DeclA,
63                                         StringRef NewName,
64                                         bool HadLeadingColonColon) {
65   while (true) {
66     while (DeclA && !isa<NamespaceDecl>(DeclA))
67       DeclA = DeclA->getParent();
68
69     // Fully qualified it is! Leave :: in place if it's there already.
70     if (!DeclA)
71       return HadLeadingColonColon ? NewName : NewName.substr(2);
72
73     // Otherwise strip off redundant namespace qualifications from the new name.
74     // We use the fully qualified name of the namespace and remove that part
75     // from NewName if it has an identical prefix.
76     std::string NS =
77         "::" + cast<NamespaceDecl>(DeclA)->getQualifiedNameAsString() + "::";
78     if (NewName.startswith(NS))
79       return NewName.substr(NS.size());
80
81     // No match yet. Strip of a namespace from the end of the chain and try
82     // again. This allows to get optimal qualifications even if the old and new
83     // decl only share common namespaces at a higher level.
84     DeclA = DeclA->getParent();
85   }
86 }
87
88 /// Check if the name specifier begins with a written "::".
89 static bool isFullyQualified(const NestedNameSpecifier *NNS) {
90   while (NNS) {
91     if (NNS->getKind() == NestedNameSpecifier::Global)
92       return true;
93     NNS = NNS->getPrefix();
94   }
95   return false;
96 }
97
98 std::string tooling::replaceNestedName(const NestedNameSpecifier *Use,
99                                        const DeclContext *UseContext,
100                                        const NamedDecl *FromDecl,
101                                        StringRef ReplacementString) {
102   assert(ReplacementString.startswith("::") &&
103          "Expected fully-qualified name!");
104
105   // We can do a raw name replacement when we are not inside the namespace for
106   // the original function and it is not in the global namespace.  The
107   // assumption is that outside the original namespace we must have a using
108   // statement that makes this work out and that other parts of this refactor
109   // will automatically fix using statements to point to the new function
110   const bool class_name_only = !Use;
111   const bool in_global_namespace =
112       isa<TranslationUnitDecl>(FromDecl->getDeclContext());
113   if (class_name_only && !in_global_namespace &&
114       !usingFromDifferentCanonicalNamespace(FromDecl->getDeclContext(),
115                                             UseContext)) {
116     auto Pos = ReplacementString.rfind("::");
117     return Pos != StringRef::npos ? ReplacementString.substr(Pos + 2)
118                                   : ReplacementString;
119   }
120   // We did not match this because of a using statement, so we will need to
121   // figure out how good a namespace match we have with our destination type.
122   // We work backwards (from most specific possible namespace to least
123   // specific).
124   return getBestNamespaceSubstr(UseContext, ReplacementString,
125                                 isFullyQualified(Use));
126 }