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