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