#include "clang/Tooling/Refactoring/Lookup.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclarationName.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "llvm/ADT/SmallVector.h"
using namespace clang;
using namespace clang::tooling;
static llvm::SmallVector<const NamespaceDecl *, 4>
getAllNamedNamespaces(const DeclContext *Context) {
llvm::SmallVector<const NamespaceDecl *, 4> Namespaces;
auto GetNextNamedNamespace = [](const DeclContext *Context) {
while (Context && (!isa<NamespaceDecl>(Context) ||
cast<NamespaceDecl>(Context)->isAnonymousNamespace()))
Context = Context->getParent();
return Context;
};
for (Context = GetNextNamedNamespace(Context); Context != nullptr;
Context = GetNextNamedNamespace(Context->getParent()))
Namespaces.push_back(cast<NamespaceDecl>(Context));
return Namespaces;
}
static bool
usingFromDifferentCanonicalNamespace(const DeclContext *FromContext,
const DeclContext *UseContext) {
llvm::SmallVector<const NamespaceDecl *, 4> FromNamespaces =
getAllNamedNamespaces(FromContext);
llvm::SmallVector<const NamespaceDecl *, 4> UseNamespaces =
getAllNamedNamespaces(UseContext);
if (UseNamespaces.size() < FromNamespaces.size())
return false;
unsigned Diff = UseNamespaces.size() - FromNamespaces.size();
auto FromIter = FromNamespaces.begin();
auto UseIter = UseNamespaces.begin() + Diff;
for (; FromIter != FromNamespaces.end() && UseIter != UseNamespaces.end();
++FromIter, ++UseIter) {
if (*FromIter == *UseIter)
return false;
if (cast<NamespaceDecl>(*FromIter)->getDeclName() ==
cast<NamespaceDecl>(*UseIter)->getDeclName())
return true;
}
assert(FromIter == FromNamespaces.end() && UseIter == UseNamespaces.end());
return false;
}
static StringRef getBestNamespaceSubstr(const DeclContext *DeclA,
StringRef NewName,
bool HadLeadingColonColon) {
while (true) {
while (DeclA && !isa<NamespaceDecl>(DeclA))
DeclA = DeclA->getParent();
if (!DeclA)
return HadLeadingColonColon ? NewName : NewName.substr(2);
std::string NS =
"::" + cast<NamespaceDecl>(DeclA)->getQualifiedNameAsString() + "::";
if (NewName.startswith(NS))
return NewName.substr(NS.size());
DeclA = DeclA->getParent();
}
}
static bool isFullyQualified(const NestedNameSpecifier *NNS) {
while (NNS) {
if (NNS->getKind() == NestedNameSpecifier::Global)
return true;
NNS = NNS->getPrefix();
}
return false;
}
static std::string disambiguateSpellingInScope(StringRef Spelling,
StringRef QName,
const DeclContext &UseContext,
SourceLocation UseLoc) {
assert(QName.startswith("::"));
assert(QName.endswith(Spelling));
if (Spelling.startswith("::"))
return std::string(Spelling);
auto UnspelledSpecifier = QName.drop_back(Spelling.size());
llvm::SmallVector<llvm::StringRef, 2> UnspelledScopes;
UnspelledSpecifier.split(UnspelledScopes, "::", -1,
false);
llvm::SmallVector<const NamespaceDecl *, 4> EnclosingNamespaces =
getAllNamedNamespaces(&UseContext);
auto &AST = UseContext.getParentASTContext();
StringRef TrimmedQName = QName.substr(2);
const auto &SM = UseContext.getParentASTContext().getSourceManager();
UseLoc = SM.getSpellingLoc(UseLoc);
auto IsAmbiguousSpelling = [&](const llvm::StringRef CurSpelling) {
if (CurSpelling.startswith("::"))
return false;
StringRef Head = CurSpelling.split("::").first;
for (const auto *NS : EnclosingNamespaces) {
auto LookupRes = NS->lookup(DeclarationName(&AST.Idents.get(Head)));
if (!LookupRes.empty()) {
for (const NamedDecl *Res : LookupRes)
if (!TrimmedQName.startswith(Res->getQualifiedNameAsString()) &&
SM.isBeforeInTranslationUnit(
SM.getSpellingLoc(Res->getLocation()), UseLoc))
return true;
}
}
return false;
};
std::string Disambiguated = std::string(Spelling);
while (IsAmbiguousSpelling(Disambiguated)) {
if (UnspelledScopes.empty()) {
Disambiguated = "::" + Disambiguated;
} else {
Disambiguated = (UnspelledScopes.back() + "::" + Disambiguated).str();
UnspelledScopes.pop_back();
}
}
return Disambiguated;
}
std::string tooling::replaceNestedName(const NestedNameSpecifier *Use,
SourceLocation UseLoc,
const DeclContext *UseContext,
const NamedDecl *FromDecl,
StringRef ReplacementString) {
assert(ReplacementString.startswith("::") &&
"Expected fully-qualified name!");
const bool class_name_only = !Use;
const bool in_global_namespace =
isa<TranslationUnitDecl>(FromDecl->getDeclContext());
const bool is_class_forward_decl =
isa<CXXRecordDecl>(FromDecl) &&
!cast<CXXRecordDecl>(FromDecl)->isCompleteDefinition();
if (class_name_only && !in_global_namespace && !is_class_forward_decl &&
!usingFromDifferentCanonicalNamespace(FromDecl->getDeclContext(),
UseContext)) {
auto Pos = ReplacementString.rfind("::");
return std::string(Pos != StringRef::npos
? ReplacementString.substr(Pos + 2)
: ReplacementString);
}
StringRef Suggested = getBestNamespaceSubstr(UseContext, ReplacementString,
isFullyQualified(Use));
return disambiguateSpellingInScope(Suggested, ReplacementString, *UseContext,
UseLoc);
}