1 //===--- AddUsing.cpp --------------------------------------------*- C++-*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
11 #include "SourceCode.h"
12 #include "refactor/Tweak.h"
13 #include "support/Logger.h"
14 #include "clang/AST/Decl.h"
15 #include "clang/AST/Expr.h"
16 #include "clang/AST/NestedNameSpecifier.h"
17 #include "clang/AST/RecursiveASTVisitor.h"
18 #include "clang/AST/Type.h"
19 #include "clang/AST/TypeLoc.h"
20 #include "clang/Basic/LLVM.h"
21 #include "clang/Basic/SourceLocation.h"
22 #include "clang/Tooling/Core/Replacement.h"
23 #include "clang/Tooling/Syntax/Tokens.h"
24 #include "llvm/ADT/StringRef.h"
25 #include "llvm/Support/FormatVariadic.h"
26 #include "llvm/Support/raw_ostream.h"
35 // Tweak for removing full namespace qualifier under cursor on DeclRefExpr and
36 // types and adding "using" statement instead.
38 // Only qualifiers that refer exclusively to namespaces (no record types) are
39 // supported. There is some guessing of appropriate place to insert the using
40 // declaration. If we find any existing usings, we insert it there. If not, we
41 // insert right after the inner-most relevant namespace declaration. If there is
42 // none, or there is, but it was declared via macro, we insert above the first
45 // Currently this only removes qualifier from under the cursor. In the future,
46 // we should improve this to remove qualifier from all occurrences of this
48 class AddUsing
: public Tweak
{
50 const char *id() const override
;
52 bool prepare(const Selection
&Inputs
) override
;
53 Expected
<Effect
> apply(const Selection
&Inputs
) override
;
54 std::string
title() const override
;
55 llvm::StringLiteral
kind() const override
{
56 return CodeAction::REFACTOR_KIND
;
60 // All of the following are set by prepare().
61 // The qualifier to remove.
62 NestedNameSpecifierLoc QualifierToRemove
;
63 // Qualified name to use when spelling the using declaration. This might be
64 // different than SpelledQualifier in presence of error correction.
65 std::string QualifierToSpell
;
66 // The name and qualifier as spelled in the code.
67 llvm::StringRef SpelledQualifier
;
68 llvm::StringRef SpelledName
;
69 // If valid, the insertion point for "using" statement must come after this.
70 // This is relevant when the type is defined in the main file, to make sure
71 // the type/function is already defined at the point where "using" is added.
72 SourceLocation MustInsertAfterLoc
;
74 REGISTER_TWEAK(AddUsing
)
76 std::string
AddUsing::title() const {
77 return std::string(llvm::formatv(
78 "Add using-declaration for {0} and remove qualifier", SpelledName
));
81 // Locates all "using" statements relevant to SelectionDeclContext.
82 class UsingFinder
: public RecursiveASTVisitor
<UsingFinder
> {
84 UsingFinder(std::vector
<const UsingDecl
*> &Results
,
85 const DeclContext
*SelectionDeclContext
, const SourceManager
&SM
)
86 : Results(Results
), SelectionDeclContext(SelectionDeclContext
), SM(SM
) {}
88 bool VisitUsingDecl(UsingDecl
*D
) {
89 auto Loc
= D
->getUsingLoc();
90 if (SM
.getFileID(Loc
) != SM
.getMainFileID()) {
93 if (D
->getDeclContext()->Encloses(SelectionDeclContext
)) {
99 bool TraverseDecl(Decl
*Node
) {
102 // There is no need to go deeper into nodes that do not enclose selection,
103 // since "using" there will not affect selection, nor would it make a good
105 if (!Node
->getDeclContext() ||
106 Node
->getDeclContext()->Encloses(SelectionDeclContext
)) {
107 return RecursiveASTVisitor
<UsingFinder
>::TraverseDecl(Node
);
113 std::vector
<const UsingDecl
*> &Results
;
114 const DeclContext
*SelectionDeclContext
;
115 const SourceManager
&SM
;
118 bool isFullyQualified(const NestedNameSpecifier
*NNS
) {
121 return NNS
->getKind() == NestedNameSpecifier::Global
||
122 isFullyQualified(NNS
->getPrefix());
125 struct InsertionPointData
{
126 // Location to insert the "using" statement. If invalid then the statement
127 // should not be inserted at all (it already exists).
129 // Extra suffix to place after the "using" statement. Depending on what the
130 // insertion point is anchored to, we may need one or more \n to ensure
131 // proper formatting.
133 // Whether using should be fully qualified, even if what the user typed was
134 // not. This is based on our detection of the local style.
135 bool AlwaysFullyQualify
= false;
138 // Finds the best place to insert the "using" statement. Returns invalid
139 // SourceLocation if the "using" statement already exists.
141 // The insertion point might be a little awkward if the decl we're anchoring to
142 // has a comment in an unfortunate place (e.g. directly above function or using
143 // decl, or immediately following "namespace {". We should add some helpers for
144 // dealing with that and use them in other code modifications as well.
145 llvm::Expected
<InsertionPointData
>
146 findInsertionPoint(const Tweak::Selection
&Inputs
,
147 const NestedNameSpecifierLoc
&QualifierToRemove
,
148 const llvm::StringRef Name
,
149 const SourceLocation MustInsertAfterLoc
) {
150 auto &SM
= Inputs
.AST
->getSourceManager();
152 // Search for all using decls that affect this point in file. We need this for
153 // two reasons: to skip adding "using" if one already exists and to find best
154 // place to add it, if it doesn't exist.
155 SourceLocation LastUsingLoc
;
156 std::vector
<const UsingDecl
*> Usings
;
157 UsingFinder(Usings
, &Inputs
.ASTSelection
.commonAncestor()->getDeclContext(),
159 .TraverseAST(Inputs
.AST
->getASTContext());
161 auto IsValidPoint
= [&](const SourceLocation Loc
) {
162 return MustInsertAfterLoc
.isInvalid() ||
163 SM
.isBeforeInTranslationUnit(MustInsertAfterLoc
, Loc
);
166 bool AlwaysFullyQualify
= true;
167 for (auto &U
: Usings
) {
168 // Only "upgrade" to fully qualified is all relevant using decls are fully
169 // qualified. Otherwise trust what the user typed.
170 if (!isFullyQualified(U
->getQualifier()))
171 AlwaysFullyQualify
= false;
173 if (SM
.isBeforeInTranslationUnit(Inputs
.Cursor
, U
->getUsingLoc()))
174 // "Usings" is sorted, so we're done.
176 if (const auto *Namespace
= U
->getQualifier()->getAsNamespace()) {
177 if (Namespace
->getCanonicalDecl() ==
178 QualifierToRemove
.getNestedNameSpecifier()
180 ->getCanonicalDecl() &&
181 U
->getName() == Name
) {
182 return InsertionPointData();
186 // Insertion point will be before last UsingDecl that affects cursor
187 // position. For most cases this should stick with the local convention of
188 // add using inside or outside namespace.
189 LastUsingLoc
= U
->getUsingLoc();
191 if (LastUsingLoc
.isValid() && IsValidPoint(LastUsingLoc
)) {
192 InsertionPointData Out
;
193 Out
.Loc
= LastUsingLoc
;
194 Out
.AlwaysFullyQualify
= AlwaysFullyQualify
;
198 // No relevant "using" statements. Try the nearest namespace level.
199 const DeclContext
*ParentDeclCtx
=
200 &Inputs
.ASTSelection
.commonAncestor()->getDeclContext();
201 while (ParentDeclCtx
&& !ParentDeclCtx
->isFileContext()) {
202 ParentDeclCtx
= ParentDeclCtx
->getLexicalParent();
204 if (auto *ND
= llvm::dyn_cast_or_null
<NamespaceDecl
>(ParentDeclCtx
)) {
205 auto Toks
= Inputs
.AST
->getTokens().expandedTokens(ND
->getSourceRange());
206 const auto *Tok
= llvm::find_if(Toks
, [](const syntax::Token
&Tok
) {
207 return Tok
.kind() == tok::l_brace
;
209 if (Tok
== Toks
.end() || Tok
->endLocation().isInvalid()) {
210 return error("Namespace with no {{");
212 if (!Tok
->endLocation().isMacroID() && IsValidPoint(Tok
->endLocation())) {
213 InsertionPointData Out
;
214 Out
.Loc
= Tok
->endLocation();
219 // No using, no namespace, no idea where to insert. Try above the first
220 // top level decl after MustInsertAfterLoc.
221 auto TLDs
= Inputs
.AST
->getLocalTopLevelDecls();
222 for (const auto &TLD
: TLDs
) {
223 if (!IsValidPoint(TLD
->getBeginLoc()))
225 InsertionPointData Out
;
226 Out
.Loc
= SM
.getExpansionLoc(TLD
->getBeginLoc());
230 return error("Cannot find place to insert \"using\"");
233 bool isNamespaceForbidden(const Tweak::Selection
&Inputs
,
234 const NestedNameSpecifier
&Namespace
) {
235 std::string NamespaceStr
= printNamespaceScope(*Namespace
.getAsNamespace());
237 for (StringRef Banned
: Config::current().Style
.FullyQualifiedNamespaces
) {
238 StringRef PrefixMatch
= NamespaceStr
;
239 if (PrefixMatch
.consume_front(Banned
) && PrefixMatch
.consume_front("::"))
246 std::string
getNNSLAsString(NestedNameSpecifierLoc
&NNSL
,
247 const PrintingPolicy
&Policy
) {
249 llvm::raw_string_ostream
OutStream(Out
);
250 NNSL
.getNestedNameSpecifier()->print(OutStream
, Policy
);
251 return OutStream
.str();
254 bool AddUsing::prepare(const Selection
&Inputs
) {
255 auto &SM
= Inputs
.AST
->getSourceManager();
256 const auto &TB
= Inputs
.AST
->getTokens();
258 // Do not suggest "using" in header files. That way madness lies.
259 if (isHeaderFile(SM
.getFileEntryRefForID(SM
.getMainFileID())->getName(),
260 Inputs
.AST
->getLangOpts()))
263 auto *Node
= Inputs
.ASTSelection
.commonAncestor();
267 // If we're looking at a type or NestedNameSpecifier, walk up the tree until
268 // we find the "main" node we care about, which would be ElaboratedTypeLoc or
270 for (; Node
->Parent
; Node
= Node
->Parent
) {
271 if (Node
->ASTNode
.get
<NestedNameSpecifierLoc
>()) {
274 if (auto *T
= Node
->ASTNode
.get
<TypeLoc
>()) {
275 if (T
->getAs
<ElaboratedTypeLoc
>()) {
278 if (Node
->Parent
->ASTNode
.get
<TypeLoc
>() ||
279 Node
->Parent
->ASTNode
.get
<NestedNameSpecifierLoc
>()) {
280 // Node is TypeLoc, but it's parent is either TypeLoc or
281 // NestedNameSpecifier. In both cases, we want to go up, to find
282 // the outermost TypeLoc.
291 // Closed range for the fully qualified name as spelled in source code.
292 SourceRange SpelledNameRange
;
293 if (auto *D
= Node
->ASTNode
.get
<DeclRefExpr
>()) {
294 if (D
->getDecl()->getIdentifier()) {
295 QualifierToRemove
= D
->getQualifierLoc();
296 // Use the name range rather than expr, as the latter can contain template
297 // arguments in the range.
298 SpelledNameRange
= D
->getSourceRange();
299 // Remove the template arguments from the name, as they shouldn't be
300 // spelled in the using declaration.
301 if (auto AngleLoc
= D
->getLAngleLoc(); AngleLoc
.isValid())
302 SpelledNameRange
.setEnd(AngleLoc
.getLocWithOffset(-1));
303 MustInsertAfterLoc
= D
->getDecl()->getBeginLoc();
305 } else if (auto *T
= Node
->ASTNode
.get
<TypeLoc
>()) {
306 if (auto E
= T
->getAs
<ElaboratedTypeLoc
>()) {
307 QualifierToRemove
= E
.getQualifierLoc();
309 SpelledNameRange
= E
.getSourceRange();
310 if (auto T
= E
.getNamedTypeLoc().getAs
<TemplateSpecializationTypeLoc
>()) {
311 // Remove the template arguments from the name.
312 SpelledNameRange
.setEnd(T
.getLAngleLoc().getLocWithOffset(-1));
315 if (const auto *ET
= E
.getTypePtr()) {
316 if (const auto *TDT
=
317 dyn_cast
<TypedefType
>(ET
->getNamedType().getTypePtr())) {
318 MustInsertAfterLoc
= TDT
->getDecl()->getBeginLoc();
319 } else if (auto *TD
= ET
->getAsTagDecl()) {
320 MustInsertAfterLoc
= TD
->getBeginLoc();
325 if (!QualifierToRemove
||
326 // FIXME: This only supports removing qualifiers that are made up of just
327 // namespace names. If qualifier contains a type, we could take the
328 // longest namespace prefix and remove that.
329 !QualifierToRemove
.getNestedNameSpecifier()->getAsNamespace() ||
330 // Respect user config.
331 isNamespaceForbidden(Inputs
, *QualifierToRemove
.getNestedNameSpecifier()))
333 // Macros are difficult. We only want to offer code action when what's spelled
334 // under the cursor is a namespace qualifier. If it's a macro that expands to
335 // a qualifier, user would not know what code action will actually change.
336 // On the other hand, if the qualifier is part of the macro argument, we
337 // should still support that.
338 if (SM
.isMacroBodyExpansion(QualifierToRemove
.getBeginLoc()) ||
339 !SM
.isWrittenInSameFile(QualifierToRemove
.getBeginLoc(),
340 QualifierToRemove
.getEndLoc())) {
345 TB
.spelledForExpanded(TB
.expandedTokens(SpelledNameRange
));
349 syntax::Token::range(SM
, SpelledTokens
->front(), SpelledTokens
->back());
350 // We only drop qualifiers that're namespaces, so this is safe.
351 std::tie(SpelledQualifier
, SpelledName
) =
352 splitQualifiedName(SpelledRange
.text(SM
));
353 QualifierToSpell
= getNNSLAsString(
354 QualifierToRemove
, Inputs
.AST
->getASTContext().getPrintingPolicy());
355 if (!llvm::StringRef(QualifierToSpell
).endswith(SpelledQualifier
) ||
357 return false; // What's spelled doesn't match the qualifier.
361 Expected
<Tweak::Effect
> AddUsing::apply(const Selection
&Inputs
) {
362 auto &SM
= Inputs
.AST
->getSourceManager();
364 tooling::Replacements R
;
365 if (auto Err
= R
.add(tooling::Replacement(
366 SM
, SM
.getSpellingLoc(QualifierToRemove
.getBeginLoc()),
367 SpelledQualifier
.size(), ""))) {
368 return std::move(Err
);
371 auto InsertionPoint
= findInsertionPoint(Inputs
, QualifierToRemove
,
372 SpelledName
, MustInsertAfterLoc
);
373 if (!InsertionPoint
) {
374 return InsertionPoint
.takeError();
377 if (InsertionPoint
->Loc
.isValid()) {
378 // Add the using statement at appropriate location.
379 std::string UsingText
;
380 llvm::raw_string_ostream
UsingTextStream(UsingText
);
381 UsingTextStream
<< "using ";
382 if (InsertionPoint
->AlwaysFullyQualify
&&
383 !isFullyQualified(QualifierToRemove
.getNestedNameSpecifier()))
384 UsingTextStream
<< "::";
385 UsingTextStream
<< QualifierToSpell
<< SpelledName
<< ";"
386 << InsertionPoint
->Suffix
;
388 assert(SM
.getFileID(InsertionPoint
->Loc
) == SM
.getMainFileID());
389 if (auto Err
= R
.add(tooling::Replacement(SM
, InsertionPoint
->Loc
, 0,
390 UsingTextStream
.str()))) {
391 return std::move(Err
);
395 return Effect::mainFileEdit(Inputs
.AST
->getASTContext().getSourceManager(),
400 } // namespace clangd