1 //===--- AddUsing.cpp --------------------------------------------*- C++-*-===// 2 // 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 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "AST.h" 10 #include "Config.h" 11 #include "FindTarget.h" 12 #include "refactor/Tweak.h" 13 #include "support/Logger.h" 14 #include "clang/AST/Decl.h" 15 #include "clang/AST/RecursiveASTVisitor.h" 16 17 namespace clang { 18 namespace clangd { 19 namespace { 20 21 // Tweak for removing full namespace qualifier under cursor on DeclRefExpr and 22 // types and adding "using" statement instead. 23 // 24 // Only qualifiers that refer exclusively to namespaces (no record types) are 25 // supported. There is some guessing of appropriate place to insert the using 26 // declaration. If we find any existing usings, we insert it there. If not, we 27 // insert right after the inner-most relevant namespace declaration. If there is 28 // none, or there is, but it was declared via macro, we insert above the first 29 // top level decl. 30 // 31 // Currently this only removes qualifier from under the cursor. In the future, 32 // we should improve this to remove qualifier from all occurrences of this 33 // symbol. 34 class AddUsing : public Tweak { 35 public: 36 const char *id() const override; 37 38 bool prepare(const Selection &Inputs) override; 39 Expected<Effect> apply(const Selection &Inputs) override; 40 std::string title() const override; 41 llvm::StringLiteral kind() const override { 42 return CodeAction::REFACTOR_KIND; 43 } 44 45 private: 46 // All of the following are set by prepare(). 47 // The qualifier to remove. 48 NestedNameSpecifierLoc QualifierToRemove; 49 // The name following QualifierToRemove. 50 llvm::StringRef Name; 51 // If valid, the insertion point for "using" statement must come after this. 52 // This is relevant when the type is defined in the main file, to make sure 53 // the type/function is already defined at the point where "using" is added. 54 SourceLocation MustInsertAfterLoc; 55 }; 56 REGISTER_TWEAK(AddUsing) 57 58 std::string AddUsing::title() const { 59 return std::string(llvm::formatv( 60 "Add using-declaration for {0} and remove qualifier", Name)); 61 } 62 63 // Locates all "using" statements relevant to SelectionDeclContext. 64 class UsingFinder : public RecursiveASTVisitor<UsingFinder> { 65 public: 66 UsingFinder(std::vector<const UsingDecl *> &Results, 67 const DeclContext *SelectionDeclContext, const SourceManager &SM) 68 : Results(Results), SelectionDeclContext(SelectionDeclContext), SM(SM) {} 69 70 bool VisitUsingDecl(UsingDecl *D) { 71 auto Loc = D->getUsingLoc(); 72 if (SM.getFileID(Loc) != SM.getMainFileID()) { 73 return true; 74 } 75 if (D->getDeclContext()->Encloses(SelectionDeclContext)) { 76 Results.push_back(D); 77 } 78 return true; 79 } 80 81 bool TraverseDecl(Decl *Node) { 82 // There is no need to go deeper into nodes that do not enclose selection, 83 // since "using" there will not affect selection, nor would it make a good 84 // insertion point. 85 if (!Node->getDeclContext() || 86 Node->getDeclContext()->Encloses(SelectionDeclContext)) { 87 return RecursiveASTVisitor<UsingFinder>::TraverseDecl(Node); 88 } 89 return true; 90 } 91 92 private: 93 std::vector<const UsingDecl *> &Results; 94 const DeclContext *SelectionDeclContext; 95 const SourceManager &SM; 96 }; 97 98 bool isFullyQualified(const NestedNameSpecifier *NNS) { 99 if (!NNS) 100 return false; 101 return NNS->getKind() == NestedNameSpecifier::Global || 102 isFullyQualified(NNS->getPrefix()); 103 } 104 105 struct InsertionPointData { 106 // Location to insert the "using" statement. If invalid then the statement 107 // should not be inserted at all (it already exists). 108 SourceLocation Loc; 109 // Extra suffix to place after the "using" statement. Depending on what the 110 // insertion point is anchored to, we may need one or more \n to ensure 111 // proper formatting. 112 std::string Suffix; 113 // Whether using should be fully qualified, even if what the user typed was 114 // not. This is based on our detection of the local style. 115 bool AlwaysFullyQualify = false; 116 }; 117 118 // Finds the best place to insert the "using" statement. Returns invalid 119 // SourceLocation if the "using" statement already exists. 120 // 121 // The insertion point might be a little awkward if the decl we're anchoring to 122 // has a comment in an unfortunate place (e.g. directly above function or using 123 // decl, or immediately following "namespace {". We should add some helpers for 124 // dealing with that and use them in other code modifications as well. 125 llvm::Expected<InsertionPointData> 126 findInsertionPoint(const Tweak::Selection &Inputs, 127 const NestedNameSpecifierLoc &QualifierToRemove, 128 const llvm::StringRef Name, 129 const SourceLocation MustInsertAfterLoc) { 130 auto &SM = Inputs.AST->getSourceManager(); 131 132 // Search for all using decls that affect this point in file. We need this for 133 // two reasons: to skip adding "using" if one already exists and to find best 134 // place to add it, if it doesn't exist. 135 SourceLocation LastUsingLoc; 136 std::vector<const UsingDecl *> Usings; 137 UsingFinder(Usings, &Inputs.ASTSelection.commonAncestor()->getDeclContext(), 138 SM) 139 .TraverseAST(Inputs.AST->getASTContext()); 140 141 auto IsValidPoint = [&](const SourceLocation Loc) { 142 return MustInsertAfterLoc.isInvalid() || 143 SM.isBeforeInTranslationUnit(MustInsertAfterLoc, Loc); 144 }; 145 146 bool AlwaysFullyQualify = true; 147 for (auto &U : Usings) { 148 // Only "upgrade" to fully qualified is all relevant using decls are fully 149 // qualified. Otherwise trust what the user typed. 150 if (!isFullyQualified(U->getQualifier())) 151 AlwaysFullyQualify = false; 152 153 if (SM.isBeforeInTranslationUnit(Inputs.Cursor, U->getUsingLoc())) 154 // "Usings" is sorted, so we're done. 155 break; 156 if (const auto *Namespace = U->getQualifier()->getAsNamespace()) { 157 if (Namespace->getCanonicalDecl() == 158 QualifierToRemove.getNestedNameSpecifier() 159 ->getAsNamespace() 160 ->getCanonicalDecl() && 161 U->getName() == Name) { 162 return InsertionPointData(); 163 } 164 } 165 166 // Insertion point will be before last UsingDecl that affects cursor 167 // position. For most cases this should stick with the local convention of 168 // add using inside or outside namespace. 169 LastUsingLoc = U->getUsingLoc(); 170 } 171 if (LastUsingLoc.isValid() && IsValidPoint(LastUsingLoc)) { 172 InsertionPointData Out; 173 Out.Loc = LastUsingLoc; 174 Out.AlwaysFullyQualify = AlwaysFullyQualify; 175 return Out; 176 } 177 178 // No relevant "using" statements. Try the nearest namespace level. 179 const DeclContext *ParentDeclCtx = 180 &Inputs.ASTSelection.commonAncestor()->getDeclContext(); 181 while (ParentDeclCtx && !ParentDeclCtx->isFileContext()) { 182 ParentDeclCtx = ParentDeclCtx->getLexicalParent(); 183 } 184 if (auto *ND = llvm::dyn_cast_or_null<NamespaceDecl>(ParentDeclCtx)) { 185 auto Toks = Inputs.AST->getTokens().expandedTokens(ND->getSourceRange()); 186 const auto *Tok = llvm::find_if(Toks, [](const syntax::Token &Tok) { 187 return Tok.kind() == tok::l_brace; 188 }); 189 if (Tok == Toks.end() || Tok->endLocation().isInvalid()) { 190 return error("Namespace with no {{"); 191 } 192 if (!Tok->endLocation().isMacroID() && IsValidPoint(Tok->endLocation())) { 193 InsertionPointData Out; 194 Out.Loc = Tok->endLocation(); 195 Out.Suffix = "\n"; 196 return Out; 197 } 198 } 199 // No using, no namespace, no idea where to insert. Try above the first 200 // top level decl after MustInsertAfterLoc. 201 auto TLDs = Inputs.AST->getLocalTopLevelDecls(); 202 for (const auto &TLD : TLDs) { 203 if (!IsValidPoint(TLD->getBeginLoc())) 204 continue; 205 InsertionPointData Out; 206 Out.Loc = SM.getExpansionLoc(TLD->getBeginLoc()); 207 Out.Suffix = "\n\n"; 208 return Out; 209 } 210 return error("Cannot find place to insert \"using\""); 211 } 212 213 bool isNamespaceForbidden(const Tweak::Selection &Inputs, 214 const NestedNameSpecifier &Namespace) { 215 std::string NamespaceStr = printNamespaceScope(*Namespace.getAsNamespace()); 216 217 for (StringRef Banned : Config::current().Style.FullyQualifiedNamespaces) { 218 StringRef PrefixMatch = NamespaceStr; 219 if (PrefixMatch.consume_front(Banned) && PrefixMatch.consume_front("::")) 220 return true; 221 } 222 223 return false; 224 } 225 226 std::string getNNSLAsString(NestedNameSpecifierLoc &NNSL, 227 const PrintingPolicy &Policy) { 228 std::string Out; 229 llvm::raw_string_ostream OutStream(Out); 230 NNSL.getNestedNameSpecifier()->print(OutStream, Policy); 231 return OutStream.str(); 232 } 233 234 bool AddUsing::prepare(const Selection &Inputs) { 235 auto &SM = Inputs.AST->getSourceManager(); 236 const auto &TB = Inputs.AST->getTokens(); 237 238 // Do not suggest "using" in header files. That way madness lies. 239 if (isHeaderFile(SM.getFileEntryForID(SM.getMainFileID())->getName(), 240 Inputs.AST->getLangOpts())) 241 return false; 242 243 auto *Node = Inputs.ASTSelection.commonAncestor(); 244 if (Node == nullptr) 245 return false; 246 247 // If we're looking at a type or NestedNameSpecifier, walk up the tree until 248 // we find the "main" node we care about, which would be ElaboratedTypeLoc or 249 // DeclRefExpr. 250 for (; Node->Parent; Node = Node->Parent) { 251 if (Node->ASTNode.get<NestedNameSpecifierLoc>()) { 252 continue; 253 } 254 if (auto *T = Node->ASTNode.get<TypeLoc>()) { 255 if (T->getAs<ElaboratedTypeLoc>()) { 256 break; 257 } 258 if (Node->Parent->ASTNode.get<TypeLoc>() || 259 Node->Parent->ASTNode.get<NestedNameSpecifierLoc>()) { 260 // Node is TypeLoc, but it's parent is either TypeLoc or 261 // NestedNameSpecifier. In both cases, we want to go up, to find 262 // the outermost TypeLoc. 263 continue; 264 } 265 } 266 break; 267 } 268 if (Node == nullptr) 269 return false; 270 271 if (auto *D = Node->ASTNode.get<DeclRefExpr>()) { 272 if (auto *II = D->getDecl()->getIdentifier()) { 273 QualifierToRemove = D->getQualifierLoc(); 274 Name = II->getName(); 275 MustInsertAfterLoc = D->getDecl()->getBeginLoc(); 276 } 277 } else if (auto *T = Node->ASTNode.get<TypeLoc>()) { 278 if (auto E = T->getAs<ElaboratedTypeLoc>()) { 279 QualifierToRemove = E.getQualifierLoc(); 280 if (!QualifierToRemove) 281 return false; 282 283 auto NameRange = E.getSourceRange(); 284 if (auto T = E.getNamedTypeLoc().getAs<TemplateSpecializationTypeLoc>()) { 285 // Remove the template arguments from the name. 286 NameRange.setEnd(T.getLAngleLoc().getLocWithOffset(-1)); 287 } 288 289 auto SpelledTokens = TB.spelledForExpanded(TB.expandedTokens(NameRange)); 290 if (!SpelledTokens) 291 return false; 292 auto SpelledRange = syntax::Token::range(SM, SpelledTokens->front(), 293 SpelledTokens->back()); 294 Name = SpelledRange.text(SM); 295 296 std::string QualifierToRemoveStr = getNNSLAsString( 297 QualifierToRemove, Inputs.AST->getASTContext().getPrintingPolicy()); 298 if (!Name.consume_front(QualifierToRemoveStr)) 299 return false; // What's spelled doesn't match the qualifier. 300 301 if (const auto *ET = E.getTypePtr()) { 302 if (const auto *TDT = 303 dyn_cast<TypedefType>(ET->getNamedType().getTypePtr())) { 304 MustInsertAfterLoc = TDT->getDecl()->getBeginLoc(); 305 } else if (auto *TD = ET->getAsTagDecl()) { 306 MustInsertAfterLoc = TD->getBeginLoc(); 307 } 308 } 309 } 310 } 311 312 // FIXME: This only supports removing qualifiers that are made up of just 313 // namespace names. If qualifier contains a type, we could take the longest 314 // namespace prefix and remove that. 315 if (!QualifierToRemove.hasQualifier() || 316 !QualifierToRemove.getNestedNameSpecifier()->getAsNamespace() || 317 Name.empty()) { 318 return false; 319 } 320 321 if (isNamespaceForbidden(Inputs, *QualifierToRemove.getNestedNameSpecifier())) 322 return false; 323 324 // Macros are difficult. We only want to offer code action when what's spelled 325 // under the cursor is a namespace qualifier. If it's a macro that expands to 326 // a qualifier, user would not know what code action will actually change. 327 // On the other hand, if the qualifier is part of the macro argument, we 328 // should still support that. 329 if (SM.isMacroBodyExpansion(QualifierToRemove.getBeginLoc()) || 330 !SM.isWrittenInSameFile(QualifierToRemove.getBeginLoc(), 331 QualifierToRemove.getEndLoc())) { 332 return false; 333 } 334 335 return true; 336 } 337 338 Expected<Tweak::Effect> AddUsing::apply(const Selection &Inputs) { 339 auto &SM = Inputs.AST->getSourceManager(); 340 341 std::string QualifierToRemoveStr = getNNSLAsString( 342 QualifierToRemove, Inputs.AST->getASTContext().getPrintingPolicy()); 343 tooling::Replacements R; 344 if (auto Err = R.add(tooling::Replacement( 345 SM, SM.getSpellingLoc(QualifierToRemove.getBeginLoc()), 346 QualifierToRemoveStr.length(), ""))) { 347 return std::move(Err); 348 } 349 350 auto InsertionPoint = 351 findInsertionPoint(Inputs, QualifierToRemove, Name, MustInsertAfterLoc); 352 if (!InsertionPoint) { 353 return InsertionPoint.takeError(); 354 } 355 356 if (InsertionPoint->Loc.isValid()) { 357 // Add the using statement at appropriate location. 358 std::string UsingText; 359 llvm::raw_string_ostream UsingTextStream(UsingText); 360 UsingTextStream << "using "; 361 if (InsertionPoint->AlwaysFullyQualify && 362 !isFullyQualified(QualifierToRemove.getNestedNameSpecifier())) 363 UsingTextStream << "::"; 364 UsingTextStream << QualifierToRemoveStr << Name << ";" 365 << InsertionPoint->Suffix; 366 367 assert(SM.getFileID(InsertionPoint->Loc) == SM.getMainFileID()); 368 if (auto Err = R.add(tooling::Replacement(SM, InsertionPoint->Loc, 0, 369 UsingTextStream.str()))) { 370 return std::move(Err); 371 } 372 } 373 374 return Effect::mainFileEdit(Inputs.AST->getASTContext().getSourceManager(), 375 std::move(R)); 376 } 377 378 } // namespace 379 } // namespace clangd 380 } // namespace clang 381