Lookup.cpp revision 292942
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"
16using namespace clang;
17using namespace clang::tooling;
18
19static 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
49static 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 "::".
76static 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
85std::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