1 //===--- AST.cpp - Utility AST functions -----------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
11 #include "SourceCode.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/AST/Decl.h"
14 #include "clang/AST/DeclTemplate.h"
15 #include "clang/AST/DeclarationName.h"
16 #include "clang/AST/NestedNameSpecifier.h"
17 #include "clang/AST/PrettyPrinter.h"
18 #include "clang/AST/TemplateBase.h"
19 #include "clang/Basic/SourceLocation.h"
20 #include "clang/Basic/SourceManager.h"
21 #include "clang/Basic/Specifiers.h"
22 #include "clang/Index/USRGeneration.h"
23 #include "llvm/ADT/Optional.h"
24 #include "llvm/Support/Casting.h"
25 #include "llvm/Support/ScopedPrinter.h"
26 #include "llvm/Support/raw_ostream.h"
32 llvm::Optional<llvm::ArrayRef<TemplateArgumentLoc>>
33 getTemplateSpecializationArgLocs(const NamedDecl &ND) {
34 if (auto *Func = llvm::dyn_cast<FunctionDecl>(&ND)) {
35 if (const ASTTemplateArgumentListInfo *Args =
36 Func->getTemplateSpecializationArgsAsWritten())
37 return Args->arguments();
38 } else if (auto *Cls =
39 llvm::dyn_cast<ClassTemplatePartialSpecializationDecl>(&ND)) {
40 if (auto *Args = Cls->getTemplateArgsAsWritten())
41 return Args->arguments();
42 } else if (auto *Var =
43 llvm::dyn_cast<VarTemplatePartialSpecializationDecl>(&ND)) {
44 if (auto *Args = Var->getTemplateArgsAsWritten())
45 return Args->arguments();
46 } else if (auto *Var = llvm::dyn_cast<VarTemplateSpecializationDecl>(&ND))
47 return Var->getTemplateArgsInfo().arguments();
48 // We return None for ClassTemplateSpecializationDecls because it does not
49 // contain TemplateArgumentLoc information.
54 bool isTemplateSpecializationKind(const NamedDecl *D,
55 TemplateSpecializationKind Kind) {
56 if (const auto *TD = dyn_cast<T>(D))
57 return TD->getTemplateSpecializationKind() == Kind;
61 bool isTemplateSpecializationKind(const NamedDecl *D,
62 TemplateSpecializationKind Kind) {
63 return isTemplateSpecializationKind<FunctionDecl>(D, Kind) ||
64 isTemplateSpecializationKind<CXXRecordDecl>(D, Kind) ||
65 isTemplateSpecializationKind<VarDecl>(D, Kind);
70 bool isImplicitTemplateInstantiation(const NamedDecl *D) {
71 return isTemplateSpecializationKind(D, TSK_ImplicitInstantiation);
74 bool isExplicitTemplateSpecialization(const NamedDecl *D) {
75 return isTemplateSpecializationKind(D, TSK_ExplicitSpecialization);
78 bool isImplementationDetail(const Decl *D) {
79 return !isSpelledInSource(D->getLocation(),
80 D->getASTContext().getSourceManager());
83 SourceLocation findName(const clang::Decl *D) {
84 return D->getLocation();
87 std::string printQualifiedName(const NamedDecl &ND) {
89 llvm::raw_string_ostream OS(QName);
90 PrintingPolicy Policy(ND.getASTContext().getLangOpts());
91 // Note that inline namespaces are treated as transparent scopes. This
92 // reflects the way they're most commonly used for lookup. Ideally we'd
93 // include them, but at query time it's hard to find all the inline
94 // namespaces to query: the preamble doesn't have a dedicated list.
95 Policy.SuppressUnwrittenScope = true;
96 ND.printQualifiedName(OS, Policy);
98 assert(!StringRef(QName).startswith("::"));
102 static bool isAnonymous(const DeclarationName &N) {
103 return N.isIdentifier() && !N.getAsIdentifierInfo();
106 /// Returns a nested name specifier of \p ND if it was present in the source,
108 /// void ns::something::foo() -> returns 'ns::something'
109 /// void foo() -> returns null
110 static NestedNameSpecifier *getQualifier(const NamedDecl &ND) {
111 if (auto *V = llvm::dyn_cast<DeclaratorDecl>(&ND))
112 return V->getQualifier();
113 if (auto *T = llvm::dyn_cast<TagDecl>(&ND))
114 return T->getQualifier();
118 std::string printUsingNamespaceName(const ASTContext &Ctx,
119 const UsingDirectiveDecl &D) {
120 PrintingPolicy PP(Ctx.getLangOpts());
122 llvm::raw_string_ostream Out(Name);
124 if (auto *Qual = D.getQualifier())
125 Qual->print(Out, PP);
126 D.getNominatedNamespaceAsWritten()->printName(Out);
130 std::string printName(const ASTContext &Ctx, const NamedDecl &ND) {
132 llvm::raw_string_ostream Out(Name);
133 PrintingPolicy PP(Ctx.getLangOpts());
135 // Handle 'using namespace'. They all have the same name - <using-directive>.
136 if (auto *UD = llvm::dyn_cast<UsingDirectiveDecl>(&ND)) {
137 Out << "using namespace ";
138 if (auto *Qual = UD->getQualifier())
139 Qual->print(Out, PP);
140 UD->getNominatedNamespaceAsWritten()->printName(Out);
144 if (isAnonymous(ND.getDeclName())) {
145 // Come up with a presentation for an anonymous entity.
146 if (isa<NamespaceDecl>(ND))
147 return "(anonymous namespace)";
148 if (auto *Cls = llvm::dyn_cast<RecordDecl>(&ND))
149 return ("(anonymous " + Cls->getKindName() + ")").str();
150 if (isa<EnumDecl>(ND))
151 return "(anonymous enum)";
152 return "(anonymous)";
155 // Print nested name qualifier if it was written in the source code.
156 if (auto *Qualifier = getQualifier(ND))
157 Qualifier->print(Out, PP);
158 // Print the name itself.
159 ND.getDeclName().print(Out, PP);
160 // Print template arguments.
161 Out << printTemplateSpecializationArgs(ND);
166 std::string printTemplateSpecializationArgs(const NamedDecl &ND) {
167 std::string TemplateArgs;
168 llvm::raw_string_ostream OS(TemplateArgs);
169 PrintingPolicy Policy(ND.getASTContext().getLangOpts());
170 if (llvm::Optional<llvm::ArrayRef<TemplateArgumentLoc>> Args =
171 getTemplateSpecializationArgLocs(ND)) {
172 printTemplateArgumentList(OS, *Args, Policy);
173 } else if (auto *Cls = llvm::dyn_cast<ClassTemplateSpecializationDecl>(&ND)) {
174 if (const TypeSourceInfo *TSI = Cls->getTypeAsWritten()) {
175 // ClassTemplateSpecializationDecls do not contain
176 // TemplateArgumentTypeLocs, they only have TemplateArgumentTypes. So we
177 // create a new argument location list from TypeSourceInfo.
178 auto STL = TSI->getTypeLoc().getAs<TemplateSpecializationTypeLoc>();
179 llvm::SmallVector<TemplateArgumentLoc, 8> ArgLocs;
180 ArgLocs.reserve(STL.getNumArgs());
181 for (unsigned I = 0; I < STL.getNumArgs(); ++I)
182 ArgLocs.push_back(STL.getArgLoc(I));
183 printTemplateArgumentList(OS, ArgLocs, Policy);
185 // FIXME: Fix cases when getTypeAsWritten returns null inside clang AST,
186 // e.g. friend decls. Currently we fallback to Template Arguments without
187 // location information.
188 printTemplateArgumentList(OS, Cls->getTemplateArgs().asArray(), Policy);
195 std::string printNamespaceScope(const DeclContext &DC) {
196 for (const auto *Ctx = &DC; Ctx != nullptr; Ctx = Ctx->getParent())
197 if (const auto *NS = dyn_cast<NamespaceDecl>(Ctx))
198 if (!NS->isAnonymousNamespace() && !NS->isInlineNamespace())
199 return printQualifiedName(*NS) + "::";
203 llvm::Optional<SymbolID> getSymbolID(const Decl *D) {
204 llvm::SmallString<128> USR;
205 if (index::generateUSRForDecl(D, USR))
207 return SymbolID(USR);
210 llvm::Optional<SymbolID> getSymbolID(const IdentifierInfo &II,
212 const SourceManager &SM) {
215 llvm::SmallString<128> USR;
216 if (index::generateUSRForMacro(II.getName(), MI->getDefinitionLoc(), SM, USR))
218 return SymbolID(USR);
221 std::string shortenNamespace(const llvm::StringRef OriginalName,
222 const llvm::StringRef CurrentNamespace) {
223 llvm::SmallVector<llvm::StringRef, 8> OriginalParts;
224 llvm::SmallVector<llvm::StringRef, 8> CurrentParts;
225 llvm::SmallVector<llvm::StringRef, 8> Result;
226 OriginalName.split(OriginalParts, "::");
227 CurrentNamespace.split(CurrentParts, "::");
228 auto MinLength = std::min(CurrentParts.size(), OriginalParts.size());
230 unsigned DifferentAt = 0;
231 while (DifferentAt < MinLength &&
232 CurrentParts[DifferentAt] == OriginalParts[DifferentAt]) {
236 for (unsigned i = DifferentAt; i < OriginalParts.size(); ++i) {
237 Result.push_back(OriginalParts[i]);
239 return join(Result, "::");
242 std::string printType(const QualType QT, const DeclContext & Context){
243 PrintingPolicy PP(Context.getParentASTContext().getPrintingPolicy());
244 PP.SuppressUnwrittenScope = 1;
245 PP.SuppressTagKeyword = 1;
246 return shortenNamespace(
248 printNamespaceScope(Context) );
252 } // namespace clangd