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"
48class 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 {
62 NestedNameSpecifierLoc QualifierToRemove;
65 std::string QualifierToSpell;
67 llvm::StringRef SpelledQualifier;
68 llvm::StringRef SpelledName;
72 SourceLocation MustInsertAfterLoc;
76std::string AddUsing::title()
const {
77 return std::string(llvm::formatv(
78 "Add using-declaration for {0} and remove qualifier", SpelledName));
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) {
105 if (!
Node->getDeclContext() ||
106 Node->getDeclContext()->Encloses(SelectionDeclContext)) {
113 std::vector<const UsingDecl *> &
Results;
114 const DeclContext *SelectionDeclContext;
115 const SourceManager &SM;
118bool isFullyQualified(
const NestedNameSpecifier *NNS) {
121 return NNS->getKind() == NestedNameSpecifier::Global ||
122 isFullyQualified(NNS->getPrefix());
125struct InsertionPointData {
145llvm::Expected<InsertionPointData>
146findInsertionPoint(
const Tweak::Selection &Inputs,
147 const NestedNameSpecifierLoc &QualifierToRemove,
148 const llvm::StringRef
Name,
149 const SourceLocation MustInsertAfterLoc) {
150 auto &SM = Inputs.AST->getSourceManager();
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);
167 for (
auto &U : Usings) {
170 if (!isFullyQualified(U->getQualifier()))
173 if (SM.isBeforeInTranslationUnit(Inputs.Cursor, U->getUsingLoc()))
176 if (
const auto *
Namespace = U->getQualifier()->getAsNamespace()) {
178 QualifierToRemove.getNestedNameSpecifier()
180 ->getCanonicalDecl() &&
181 U->getName() ==
Name) {
182 return InsertionPointData();
189 LastUsingLoc = U->getUsingLoc();
191 if (LastUsingLoc.isValid() && IsValidPoint(LastUsingLoc)) {
192 InsertionPointData
Out;
193 Out.Loc = LastUsingLoc;
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();
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\"");
233bool isNamespaceForbidden(
const Tweak::Selection &Inputs,
237 for (StringRef Banned :
Config::current().Style.FullyQualifiedNamespaces) {
238 StringRef PrefixMatch = NamespaceStr;
239 if (PrefixMatch.consume_front(Banned) && PrefixMatch.consume_front(
"::"))
246std::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();
254bool AddUsing::prepare(
const Selection &Inputs) {
255 auto &SM = Inputs.AST->getSourceManager();
256 const auto &TB = Inputs.AST->getTokens();
259 if (
isHeaderFile(SM.getFileEntryRefForID(SM.getMainFileID())->getName(),
260 Inputs.AST->getLangOpts()))
263 auto *
Node = Inputs.ASTSelection.commonAncestor();
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>()) {
292 SourceRange SpelledNameRange;
293 if (
auto *D =
Node->ASTNode.get<DeclRefExpr>()) {
294 if (D->getDecl()->getIdentifier()) {
295 QualifierToRemove = D->getQualifierLoc();
298 SpelledNameRange = D->getSourceRange();
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>()) {
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 ||
329 !QualifierToRemove.getNestedNameSpecifier()->getAsNamespace() ||
331 isNamespaceForbidden(Inputs, *QualifierToRemove.getNestedNameSpecifier()))
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());
351 std::tie(SpelledQualifier, SpelledName) =
353 QualifierToSpell = getNNSLAsString(
354 QualifierToRemove, Inputs.AST->getASTContext().getPrintingPolicy());
355 if (!llvm::StringRef(QualifierToSpell).ends_with(SpelledQualifier) ||
361Expected<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()) {
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(),
const FunctionDecl * Decl
llvm::SmallString< 256U > Name
std::vector< CodeCompletionResult > Results
CompiledFragmentImpl & Out
::clang::DynTypedNode Node
#define REGISTER_TWEAK(Subclass)
std::pair< StringRef, StringRef > splitQualifiedName(StringRef QName)
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&... Vals)
bool isHeaderFile(llvm::StringRef FileName, std::optional< LangOptions > LangOpts)
Infers whether this is a header from the FileName and LangOpts (if presents).
std::string printNamespaceScope(const DeclContext &DC)
Returns the first enclosing namespace scope starting from DC.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static const llvm::StringLiteral REFACTOR_KIND
static const Config & current()
Returns the Config of the current Context, or an empty configuration.