13 #include "clang/AST/Decl.h"
14 #include "clang/AST/RecursiveASTVisitor.h"
33 class AddUsing :
public Tweak {
35 const char *id()
const override;
37 bool prepare(
const Selection &
Inputs)
override;
38 Expected<Effect> apply(
const Selection &
Inputs)
override;
39 std::string title()
const override;
40 Intent intent()
const override {
return Refactor; }
44 NestedNameSpecifierLoc QualifierToRemove;
50 std::string AddUsing::title()
const {
51 return std::string(llvm::formatv(
52 "Add using-declaration for {0} and remove qualifier.", Name));
56 class UsingFinder :
public RecursiveASTVisitor<UsingFinder> {
58 UsingFinder(std::vector<const UsingDecl *> &
Results,
59 const DeclContext *SelectionDeclContext,
const SourceManager &SM)
60 :
Results(
Results), SelectionDeclContext(SelectionDeclContext), SM(SM) {}
62 bool VisitUsingDecl(UsingDecl *D) {
63 auto Loc = D->getUsingLoc();
64 if (SM.getFileID(
Loc) != SM.getMainFileID()) {
67 if (D->getDeclContext()->Encloses(SelectionDeclContext)) {
73 bool TraverseDecl(
Decl *Node) {
77 if (Node->getDeclContext()->Encloses(SelectionDeclContext)) {
78 return RecursiveASTVisitor<UsingFinder>::TraverseDecl(Node);
84 std::vector<const UsingDecl *> &
Results;
85 const DeclContext *SelectionDeclContext;
86 const SourceManager &SM;
89 struct InsertionPointData {
106 llvm::Expected<InsertionPointData>
107 findInsertionPoint(
const Tweak::Selection &
Inputs,
108 const NestedNameSpecifierLoc &QualifierToRemove,
109 const llvm::StringRef Name) {
110 auto &SM =
Inputs.AST->getSourceManager();
115 SourceLocation LastUsingLoc;
116 std::vector<const UsingDecl *> Usings;
117 UsingFinder(Usings, &
Inputs.ASTSelection.commonAncestor()->getDeclContext(),
119 .TraverseAST(
Inputs.AST->getASTContext());
121 for (
auto &U : Usings) {
122 if (SM.isBeforeInTranslationUnit(
Inputs.Cursor, U->getUsingLoc()))
125 if (U->getQualifier()->getAsNamespace()->getCanonicalDecl() ==
126 QualifierToRemove.getNestedNameSpecifier()
128 ->getCanonicalDecl() &&
129 U->getName() ==
Name) {
130 return InsertionPointData();
135 LastUsingLoc = U->getUsingLoc();
137 if (LastUsingLoc.isValid()) {
138 InsertionPointData
Out;
139 Out.Loc = LastUsingLoc;
144 const DeclContext *ParentDeclCtx =
145 &
Inputs.ASTSelection.commonAncestor()->getDeclContext();
146 while (ParentDeclCtx && !ParentDeclCtx->isFileContext()) {
147 ParentDeclCtx = ParentDeclCtx->getLexicalParent();
149 if (
auto *ND = llvm::dyn_cast_or_null<NamespaceDecl>(ParentDeclCtx)) {
150 auto Toks =
Inputs.AST->getTokens().expandedTokens(ND->getSourceRange());
151 const auto *Tok = llvm::find_if(Toks, [](
const syntax::Token &Tok) {
152 return Tok.kind() == tok::l_brace;
154 if (Tok == Toks.end() || Tok->endLocation().isInvalid()) {
155 return llvm::createStringError(llvm::inconvertibleErrorCode(),
156 "Namespace with no {");
158 if (!Tok->endLocation().isMacroID()) {
159 InsertionPointData
Out;
160 Out.Loc = Tok->endLocation();
167 auto TLDs =
Inputs.AST->getLocalTopLevelDecls();
169 return llvm::createStringError(llvm::inconvertibleErrorCode(),
170 "Cannot find place to insert \"using\"");
172 InsertionPointData
Out;
173 Out.Loc = SM.getExpansionLoc(TLDs[0]->getBeginLoc());
178 bool AddUsing::prepare(
const Selection &
Inputs) {
179 auto &SM =
Inputs.AST->getSourceManager();
182 if (
isHeaderFile(SM.getFileEntryForID(SM.getMainFileID())->getName(),
183 Inputs.AST->getLangOpts()))
186 auto *Node =
Inputs.ASTSelection.commonAncestor();
193 for (; Node->Parent; Node = Node->Parent) {
194 if (Node->ASTNode.get<NestedNameSpecifierLoc>()) {
196 }
else if (
auto *T = Node->ASTNode.get<TypeLoc>()) {
197 if (T->getAs<ElaboratedTypeLoc>()) {
199 }
else if (Node->Parent->ASTNode.get<TypeLoc>() ||
200 Node->Parent->ASTNode.get<NestedNameSpecifierLoc>()) {
212 if (
auto *D = Node->ASTNode.get<DeclRefExpr>()) {
213 if (
auto *II = D->getDecl()->getIdentifier()) {
214 QualifierToRemove = D->getQualifierLoc();
215 Name = II->getName();
217 }
else if (
auto *T = Node->ASTNode.get<TypeLoc>()) {
218 if (
auto E = T->getAs<ElaboratedTypeLoc>()) {
219 if (
auto *BaseTypeIdentifier =
220 E.getType().getUnqualifiedType().getBaseTypeIdentifier()) {
221 Name = BaseTypeIdentifier->getName();
222 QualifierToRemove =
E.getQualifierLoc();
230 if (!QualifierToRemove.hasQualifier() ||
231 !QualifierToRemove.getNestedNameSpecifier()->getAsNamespace() ||
241 if (SM.isMacroBodyExpansion(QualifierToRemove.getBeginLoc()) ||
242 !SM.isWrittenInSameFile(QualifierToRemove.getBeginLoc(),
243 QualifierToRemove.getEndLoc())) {
250 Expected<Tweak::Effect> AddUsing::apply(
const Selection &
Inputs) {
251 auto &SM =
Inputs.AST->getSourceManager();
252 auto &TB =
Inputs.AST->getTokens();
255 auto SpelledTokens = TB.spelledForExpanded(
256 TB.expandedTokens(QualifierToRemove.getSourceRange()));
257 if (!SpelledTokens) {
258 return llvm::createStringError(
259 llvm::inconvertibleErrorCode(),
260 "Could not determine length of the qualifier");
263 syntax::Token::range(SM, SpelledTokens->front(), SpelledTokens->back())
265 tooling::Replacements R;
266 if (
auto Err = R.add(tooling::Replacement(
267 SM, SpelledTokens->front().location(),
Length,
""))) {
268 return std::move(Err);
278 std::string UsingText;
279 llvm::raw_string_ostream UsingTextStream(UsingText);
280 UsingTextStream <<
"using ";
281 QualifierToRemove.getNestedNameSpecifier()->print(
282 UsingTextStream,
Inputs.AST->getASTContext().getPrintingPolicy());
286 if (
auto Err = R.add(tooling::Replacement(SM,
InsertionPoint->Loc, 0,
287 UsingTextStream.str()))) {
288 return std::move(Err);
292 return Effect::mainFileEdit(
Inputs.AST->getASTContext().getSourceManager(),