13 #include "clang/AST/Decl.h"
14 #include "clang/AST/DeclBase.h"
15 #include "clang/AST/DeclCXX.h"
16 #include "clang/AST/RecursiveASTVisitor.h"
17 #include "clang/Basic/SourceLocation.h"
18 #include "clang/Tooling/Core/Replacement.h"
19 #include "clang/Tooling/Refactoring/RecursiveSymbolVisitor.h"
20 #include "llvm/ADT/ScopeExit.h"
34 class RemoveUsingNamespace :
public Tweak {
36 const char *id()
const override;
38 bool prepare(
const Selection &
Inputs)
override;
39 Expected<Effect> apply(
const Selection &
Inputs)
override;
40 std::string title()
const override;
41 Intent intent()
const override {
return Refactor; }
44 const UsingDirectiveDecl *TargetDirective =
nullptr;
48 class FindSameUsings :
public RecursiveASTVisitor<FindSameUsings> {
50 FindSameUsings(
const UsingDirectiveDecl &Target,
51 std::vector<const UsingDirectiveDecl *> &
Results)
52 : TargetNS(Target.getNominatedNamespace()),
55 bool VisitUsingDirectiveDecl(UsingDirectiveDecl *D) {
56 if (D->getNominatedNamespace() != TargetNS ||
57 D->getDeclContext() != TargetCtx)
64 const NamespaceDecl *TargetNS;
65 const DeclContext *TargetCtx;
66 std::vector<const UsingDirectiveDecl *> &
Results;
70 llvm::Expected<tooling::Replacement>
71 removeUsingDirective(ASTContext &
Ctx,
const UsingDirectiveDecl *D) {
72 auto &SM =
Ctx.getSourceManager();
73 llvm::Optional<Token> NextTok =
74 Lexer::findNextToken(D->getEndLoc(), SM,
Ctx.getLangOpts());
75 if (!NextTok || NextTok->isNot(tok::semi))
76 return llvm::createStringError(llvm::inconvertibleErrorCode(),
77 "no semicolon after using-directive");
80 return tooling::Replacement(
82 CharSourceRange::getTokenRange(D->getBeginLoc(), NextTok->getLocation()),
83 "",
Ctx.getLangOpts());
87 bool isTopLevelDecl(
const SelectionTree::Node *Node) {
88 return Node->Parent && Node->Parent->ASTNode.get<TranslationUnitDecl>();
97 const DeclContext *visibleContext(
const DeclContext *D) {
98 while (D->isInlineNamespace() || D->isTransparentContext())
103 bool RemoveUsingNamespace::prepare(
const Selection &
Inputs) {
105 auto *CA =
Inputs.ASTSelection.commonAncestor();
108 TargetDirective = CA->ASTNode.get<UsingDirectiveDecl>();
109 if (!TargetDirective)
111 if (!dyn_cast<Decl>(TargetDirective->getDeclContext()))
123 if (!TargetDirective->getNominatedNamespace()->using_directives().empty())
125 return isTopLevelDecl(CA);
128 Expected<Tweak::Effect> RemoveUsingNamespace::apply(
const Selection &
Inputs) {
130 auto &SM =
Ctx.getSourceManager();
133 std::vector<const UsingDirectiveDecl *> AllDirectives;
134 FindSameUsings(*TargetDirective, AllDirectives).TraverseAST(
Ctx);
136 SourceLocation FirstUsingDirectiveLoc;
137 for (
auto *D : AllDirectives) {
138 if (FirstUsingDirectiveLoc.isInvalid() ||
139 SM.isBeforeInTranslationUnit(D->getBeginLoc(), FirstUsingDirectiveLoc))
140 FirstUsingDirectiveLoc = D->getBeginLoc();
145 std::vector<SourceLocation> IdentsToQualify;
146 for (
auto &D :
Inputs.AST->getLocalTopLevelDecls()) {
151 for (
auto *T : Ref.Targets) {
152 if (!visibleContext(T->getDeclContext())
153 ->Equals(TargetDirective->getNominatedNamespace()))
156 SourceLocation
Loc = Ref.NameLoc;
157 if (
Loc.isMacroID()) {
164 if (!SM.isMacroArgExpansion(
Loc))
166 Loc = SM.getFileLoc(Ref.NameLoc);
168 assert(
Loc.isFileID());
169 if (SM.getFileID(
Loc) != SM.getMainFileID())
171 if (SM.isBeforeInTranslationUnit(
Loc, FirstUsingDirectiveLoc))
173 IdentsToQualify.push_back(
Loc);
177 llvm::sort(IdentsToQualify);
178 IdentsToQualify.erase(
179 std::unique(IdentsToQualify.begin(), IdentsToQualify.end()),
180 IdentsToQualify.end());
183 tooling::Replacements R;
184 for (
auto *D : AllDirectives) {
185 auto RemoveUsing = removeUsingDirective(
Ctx, D);
187 return RemoveUsing.takeError();
188 if (
auto Err = R.add(*RemoveUsing))
189 return std::move(Err);
193 for (
auto Loc : IdentsToQualify) {
194 if (
auto Err = R.add(tooling::Replacement(
Ctx.getSourceManager(),
Loc,
196 return std::move(Err);
198 return Effect::mainFileEdit(SM, std::move(R));
201 std::string RemoveUsingNamespace::title()
const {
203 llvm::formatv(
"Remove using namespace, re-qualify names instead."));