1 //===--- AST.cpp - Utility AST functions -----------------------*- 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 11 #include "clang/AST/ASTContext.h" 12 #include "clang/AST/Decl.h" 13 #include "clang/AST/DeclTemplate.h" 14 #include "clang/AST/TemplateBase.h" 15 #include "clang/Basic/SourceLocation.h" 16 #include "clang/Basic/SourceManager.h" 17 #include "clang/Index/USRGeneration.h" 18 #include "llvm/ADT/Optional.h" 19 #include "llvm/Support/Casting.h" 20 #include "llvm/Support/ScopedPrinter.h" 21 #include "llvm/Support/raw_ostream.h" 22 23 namespace clang { 24 namespace clangd { 25 26 namespace { 27 llvm::Optional<llvm::ArrayRef<TemplateArgumentLoc>> 28 getTemplateSpecializationArgLocs(const NamedDecl &ND) { 29 if (auto *Func = llvm::dyn_cast<FunctionDecl>(&ND)) { 30 if (const ASTTemplateArgumentListInfo *Args = 31 Func->getTemplateSpecializationArgsAsWritten()) 32 return Args->arguments(); 33 } else if (auto *Cls = 34 llvm::dyn_cast<ClassTemplatePartialSpecializationDecl>(&ND)) { 35 if (auto *Args = Cls->getTemplateArgsAsWritten()) 36 return Args->arguments(); 37 } else if (auto *Var = llvm::dyn_cast<VarTemplateSpecializationDecl>(&ND)) 38 return Var->getTemplateArgsInfo().arguments(); 39 // We return None for ClassTemplateSpecializationDecls because it does not 40 // contain TemplateArgumentLoc information. 41 return llvm::None; 42 } 43 } // namespace 44 45 // Returns true if the complete name of decl \p D is spelled in the source code. 46 // This is not the case for: 47 // * symbols formed via macro concatenation, the spelling location will 48 // be "<scratch space>" 49 // * symbols controlled and defined by a compile command-line option 50 // `-DName=foo`, the spelling location will be "<command line>". 51 bool isSpelledInSourceCode(const Decl *D) { 52 const auto &SM = D->getASTContext().getSourceManager(); 53 auto Loc = D->getLocation(); 54 // FIXME: Revisit the strategy, the heuristic is limitted when handling 55 // macros, we should use the location where the whole definition occurs. 56 if (Loc.isMacroID()) { 57 std::string PrintLoc = SM.getSpellingLoc(Loc).printToString(SM); 58 if (llvm::StringRef(PrintLoc).startswith("<scratch") || 59 llvm::StringRef(PrintLoc).startswith("<command line>")) 60 return false; 61 } 62 return true; 63 } 64 65 bool isImplementationDetail(const Decl *D) { return !isSpelledInSourceCode(D); } 66 67 SourceLocation findNameLoc(const clang::Decl *D) { 68 const auto &SM = D->getASTContext().getSourceManager(); 69 if (!isSpelledInSourceCode(D)) 70 // Use the expansion location as spelling location is not interesting. 71 return SM.getExpansionRange(D->getLocation()).getBegin(); 72 return SM.getSpellingLoc(D->getLocation()); 73 } 74 75 std::string printQualifiedName(const NamedDecl &ND) { 76 std::string QName; 77 llvm::raw_string_ostream OS(QName); 78 PrintingPolicy Policy(ND.getASTContext().getLangOpts()); 79 // Note that inline namespaces are treated as transparent scopes. This 80 // reflects the way they're most commonly used for lookup. Ideally we'd 81 // include them, but at query time it's hard to find all the inline 82 // namespaces to query: the preamble doesn't have a dedicated list. 83 Policy.SuppressUnwrittenScope = true; 84 ND.printQualifiedName(OS, Policy); 85 OS.flush(); 86 assert(!StringRef(QName).startswith("::")); 87 return QName; 88 } 89 90 std::string printName(const ASTContext &Ctx, const NamedDecl &ND) { 91 std::string Name; 92 llvm::raw_string_ostream Out(Name); 93 PrintingPolicy PP(Ctx.getLangOpts()); 94 // Handle 'using namespace'. They all have the same name - <using-directive>. 95 if (auto *UD = llvm::dyn_cast<UsingDirectiveDecl>(&ND)) { 96 Out << "using namespace "; 97 if (auto *Qual = UD->getQualifier()) 98 Qual->print(Out, PP); 99 UD->getNominatedNamespaceAsWritten()->printName(Out); 100 return Out.str(); 101 } 102 ND.getDeclName().print(Out, PP); 103 if (!Out.str().empty()) { 104 Out << printTemplateSpecializationArgs(ND); 105 return Out.str(); 106 } 107 // The name was empty, so present an anonymous entity. 108 if (isa<NamespaceDecl>(ND)) 109 return "(anonymous namespace)"; 110 if (auto *Cls = llvm::dyn_cast<RecordDecl>(&ND)) 111 return ("(anonymous " + Cls->getKindName() + ")").str(); 112 if (isa<EnumDecl>(ND)) 113 return "(anonymous enum)"; 114 return "(anonymous)"; 115 } 116 117 std::string printTemplateSpecializationArgs(const NamedDecl &ND) { 118 std::string TemplateArgs; 119 llvm::raw_string_ostream OS(TemplateArgs); 120 PrintingPolicy Policy(ND.getASTContext().getLangOpts()); 121 if (llvm::Optional<llvm::ArrayRef<TemplateArgumentLoc>> Args = 122 getTemplateSpecializationArgLocs(ND)) { 123 printTemplateArgumentList(OS, *Args, Policy); 124 } else if (auto *Cls = llvm::dyn_cast<ClassTemplateSpecializationDecl>(&ND)) { 125 if (const TypeSourceInfo *TSI = Cls->getTypeAsWritten()) { 126 // ClassTemplateSpecializationDecls do not contain 127 // TemplateArgumentTypeLocs, they only have TemplateArgumentTypes. So we 128 // create a new argument location list from TypeSourceInfo. 129 auto STL = TSI->getTypeLoc().getAs<TemplateSpecializationTypeLoc>(); 130 llvm::SmallVector<TemplateArgumentLoc, 8> ArgLocs; 131 ArgLocs.reserve(STL.getNumArgs()); 132 for (unsigned I = 0; I < STL.getNumArgs(); ++I) 133 ArgLocs.push_back(STL.getArgLoc(I)); 134 printTemplateArgumentList(OS, ArgLocs, Policy); 135 } else { 136 // FIXME: Fix cases when getTypeAsWritten returns null inside clang AST, 137 // e.g. friend decls. Currently we fallback to Template Arguments without 138 // location information. 139 printTemplateArgumentList(OS, Cls->getTemplateArgs().asArray(), Policy); 140 } 141 } 142 OS.flush(); 143 return TemplateArgs; 144 } 145 146 std::string printNamespaceScope(const DeclContext &DC) { 147 for (const auto *Ctx = &DC; Ctx != nullptr; Ctx = Ctx->getParent()) 148 if (const auto *NS = dyn_cast<NamespaceDecl>(Ctx)) 149 if (!NS->isAnonymousNamespace() && !NS->isInlineNamespace()) 150 return printQualifiedName(*NS) + "::"; 151 return ""; 152 } 153 154 llvm::Optional<SymbolID> getSymbolID(const Decl *D) { 155 llvm::SmallString<128> USR; 156 if (index::generateUSRForDecl(D, USR)) 157 return None; 158 return SymbolID(USR); 159 } 160 161 llvm::Optional<SymbolID> getSymbolID(const IdentifierInfo &II, 162 const MacroInfo *MI, 163 const SourceManager &SM) { 164 if (MI == nullptr) 165 return None; 166 llvm::SmallString<128> USR; 167 if (index::generateUSRForMacro(II.getName(), MI->getDefinitionLoc(), SM, USR)) 168 return None; 169 return SymbolID(USR); 170 } 171 172 } // namespace clangd 173 } // namespace clang 174