llvm-project
363 строки · 14.2 Кб
1//===--- WalkAST.cpp - Find declaration references in the AST -------------===//
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 "AnalysisInternal.h"
10#include "clang-include-cleaner/Types.h"
11#include "clang/AST/ASTFwd.h"
12#include "clang/AST/Decl.h"
13#include "clang/AST/DeclCXX.h"
14#include "clang/AST/DeclFriend.h"
15#include "clang/AST/DeclTemplate.h"
16#include "clang/AST/Expr.h"
17#include "clang/AST/ExprCXX.h"
18#include "clang/AST/RecursiveASTVisitor.h"
19#include "clang/AST/TemplateBase.h"
20#include "clang/AST/TemplateName.h"
21#include "clang/AST/Type.h"
22#include "clang/AST/TypeLoc.h"
23#include "clang/Basic/IdentifierTable.h"
24#include "clang/Basic/SourceLocation.h"
25#include "clang/Basic/Specifiers.h"
26#include "llvm/ADT/STLExtras.h"
27#include "llvm/ADT/STLFunctionalExtras.h"
28#include "llvm/ADT/SmallVector.h"
29#include "llvm/Support/Casting.h"
30
31namespace clang::include_cleaner {
32namespace {
33using DeclCallback =
34llvm::function_ref<void(SourceLocation, NamedDecl &, RefType)>;
35
36class ASTWalker : public RecursiveASTVisitor<ASTWalker> {
37DeclCallback Callback;
38
39void report(SourceLocation Loc, NamedDecl *ND,
40RefType RT = RefType::Explicit) {
41if (!ND || Loc.isInvalid())
42return;
43Callback(Loc, *cast<NamedDecl>(ND->getCanonicalDecl()), RT);
44}
45
46NamedDecl *resolveTemplateName(TemplateName TN) {
47// For using-templates, only mark the alias.
48if (auto *USD = TN.getAsUsingShadowDecl())
49return USD;
50return TN.getAsTemplateDecl();
51}
52NamedDecl *getMemberProvider(QualType Base) {
53if (Base->isPointerType())
54return getMemberProvider(Base->getPointeeType());
55// Unwrap the sugar ElaboratedType.
56if (const auto *ElTy = dyn_cast<ElaboratedType>(Base))
57return getMemberProvider(ElTy->getNamedType());
58
59if (const auto *TT = dyn_cast<TypedefType>(Base))
60return TT->getDecl();
61if (const auto *UT = dyn_cast<UsingType>(Base))
62return UT->getFoundDecl();
63// A heuristic: to resolve a template type to **only** its template name.
64// We're only using this method for the base type of MemberExpr, in general
65// the template provides the member, and the critical case `unique_ptr<Foo>`
66// is supported (the base type is a Foo*).
67//
68// There are some exceptions that this heuristic could fail (dependent base,
69// dependent typealias), but we believe these are rare.
70if (const auto *TST = dyn_cast<TemplateSpecializationType>(Base))
71return resolveTemplateName(TST->getTemplateName());
72return Base->getAsRecordDecl();
73}
74// Templated as TemplateSpecializationType and
75// DeducedTemplateSpecializationType doesn't share a common base.
76template <typename T>
77// Picks the most specific specialization for a
78// (Deduced)TemplateSpecializationType, while prioritizing using-decls.
79NamedDecl *getMostRelevantTemplatePattern(const T *TST) {
80// In case of exported template names always prefer the using-decl. This
81// implies we'll point at the using-decl even when there's an explicit
82// specializaiton using the exported name, but that's rare.
83auto *ND = resolveTemplateName(TST->getTemplateName());
84if (llvm::isa_and_present<UsingShadowDecl, TypeAliasTemplateDecl>(ND))
85return ND;
86// This is the underlying decl used by TemplateSpecializationType, can be
87// null when type is dependent or not resolved to a pattern yet.
88// If so, fallback to primary template.
89CXXRecordDecl *TD = TST->getAsCXXRecordDecl();
90if (!TD || TD->getTemplateSpecializationKind() == TSK_Undeclared)
91return ND;
92// We ignore explicit instantiations. This might imply marking the wrong
93// declaration as used in specific cases, but seems like the right trade-off
94// in general (e.g. we don't want to include a custom library that has an
95// explicit specialization of a common type).
96if (auto *Pat = TD->getTemplateInstantiationPattern())
97return Pat;
98// For explicit specializations, use the specialized decl directly.
99return TD;
100}
101
102public:
103ASTWalker(DeclCallback Callback) : Callback(Callback) {}
104
105// Operators are almost always ADL extension points and by design references
106// to them doesn't count as uses (generally the type should provide them, so
107// ignore them).
108// Unless we're using an operator defined as a member, in such cases treat
109// these as regular member references.
110bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *S) {
111if (!WalkUpFromCXXOperatorCallExpr(S))
112return false;
113if (auto *CD = S->getCalleeDecl()) {
114if (llvm::isa<CXXMethodDecl>(CD)) {
115// Treat this as a regular member reference.
116report(S->getOperatorLoc(), getMemberProvider(S->getArg(0)->getType()),
117RefType::Implicit);
118} else {
119report(S->getOperatorLoc(), llvm::dyn_cast<NamedDecl>(CD),
120RefType::Implicit);
121}
122}
123for (auto *Arg : S->arguments())
124if (!TraverseStmt(Arg))
125return false;
126return true;
127}
128
129bool VisitDeclRefExpr(DeclRefExpr *DRE) {
130auto *FD = DRE->getFoundDecl();
131// Prefer the underlying decl if FoundDecl isn't a shadow decl, e.g:
132// - For templates, found-decl is always primary template, but we want the
133// specializaiton itself.
134if (!llvm::isa<UsingShadowDecl>(FD))
135FD = DRE->getDecl();
136// For refs to non-meber-like decls, use the found decl.
137// For member-like decls, we should have a reference from the qualifier to
138// the container decl instead, which is preferred as it'll handle
139// aliases/exports properly.
140if (!FD->isCXXClassMember() && !llvm::isa<EnumConstantDecl>(FD)) {
141report(DRE->getLocation(), FD);
142return true;
143}
144// If the ref is without a qualifier, and is a member, ignore it. As it is
145// available in current context due to some other construct (e.g. base
146// specifiers, using decls) that has to spell the name explicitly.
147//
148// If it's an enum constant, it must be due to prior decl. Report references
149// to it when qualifier isn't a type.
150if (llvm::isa<EnumConstantDecl>(FD)) {
151if (!DRE->getQualifier() || DRE->getQualifier()->getAsNamespace())
152report(DRE->getLocation(), FD);
153}
154return true;
155}
156
157bool VisitMemberExpr(MemberExpr *E) {
158// Reporting a usage of the member decl would cause issues (e.g. force
159// including the base class for inherited members). Instead, we report a
160// usage of the base type of the MemberExpr, so that e.g. code
161// `returnFoo().bar` can keep #include "foo.h" (rather than inserting
162// "bar.h" for the underlying base type `Bar`).
163QualType Type = E->getBase()->IgnoreImpCasts()->getType();
164report(E->getMemberLoc(), getMemberProvider(Type), RefType::Implicit);
165return true;
166}
167bool VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E) {
168report(E->getMemberLoc(), getMemberProvider(E->getBaseType()),
169RefType::Implicit);
170return true;
171}
172
173bool VisitCXXConstructExpr(CXXConstructExpr *E) {
174// Always treat consturctor calls as implicit. We'll have an explicit
175// reference for the constructor calls that mention the type-name (through
176// TypeLocs). This reference only matters for cases where there's no
177// explicit syntax at all or there're only braces.
178report(E->getLocation(), getMemberProvider(E->getType()),
179RefType::Implicit);
180return true;
181}
182
183bool VisitOverloadExpr(OverloadExpr *E) {
184// Since we can't prove which overloads are used, report all of them.
185for (NamedDecl *D : E->decls())
186report(E->getNameLoc(), D, RefType::Ambiguous);
187return true;
188}
189
190// Report all (partial) specializations of a class/var template decl.
191template <typename TemplateDeclType, typename ParitialDeclType>
192void reportSpecializations(SourceLocation Loc, NamedDecl *ND) {
193const auto *TD = llvm::dyn_cast<TemplateDeclType>(ND);
194if (!TD)
195return;
196
197for (auto *Spec : TD->specializations())
198report(Loc, Spec, RefType::Ambiguous);
199llvm::SmallVector<ParitialDeclType *> PartialSpecializations;
200TD->getPartialSpecializations(PartialSpecializations);
201for (auto *PartialSpec : PartialSpecializations)
202report(Loc, PartialSpec, RefType::Ambiguous);
203}
204bool VisitUsingDecl(UsingDecl *UD) {
205for (const auto *Shadow : UD->shadows()) {
206auto *TD = Shadow->getTargetDecl();
207auto IsUsed = TD->isUsed() || TD->isReferenced();
208report(UD->getLocation(), TD,
209IsUsed ? RefType::Explicit : RefType::Ambiguous);
210
211// All (partial) template specializations are visible via a using-decl,
212// However a using-decl only refers to the primary template (per C++ name
213// lookup). Thus, we need to manually report all specializations.
214reportSpecializations<ClassTemplateDecl,
215ClassTemplatePartialSpecializationDecl>(
216UD->getLocation(), TD);
217reportSpecializations<VarTemplateDecl,
218VarTemplatePartialSpecializationDecl>(
219UD->getLocation(), TD);
220if (const auto *FTD = llvm::dyn_cast<FunctionTemplateDecl>(TD))
221for (auto *Spec : FTD->specializations())
222report(UD->getLocation(), Spec, RefType::Ambiguous);
223}
224return true;
225}
226
227bool VisitFunctionDecl(FunctionDecl *FD) {
228// Mark declaration from definition as it needs type-checking.
229if (FD->isThisDeclarationADefinition())
230report(FD->getLocation(), FD);
231// Explicit specializaiton/instantiations of a function template requires
232// primary template.
233if (clang::isTemplateExplicitInstantiationOrSpecialization(
234FD->getTemplateSpecializationKind()))
235report(FD->getLocation(), FD->getPrimaryTemplate());
236return true;
237}
238bool VisitVarDecl(VarDecl *VD) {
239// Ignore the parameter decl itself (its children were handled elsewhere),
240// as they don't contribute to the main-file #include.
241if (llvm::isa<ParmVarDecl>(VD))
242return true;
243// Mark declaration from definition as it needs type-checking.
244if (VD->isThisDeclarationADefinition())
245report(VD->getLocation(), VD);
246return true;
247}
248
249bool VisitEnumDecl(EnumDecl *D) {
250// Definition of an enum with an underlying type references declaration for
251// type-checking purposes.
252if (D->isThisDeclarationADefinition() && D->getIntegerTypeSourceInfo())
253report(D->getLocation(), D);
254return true;
255}
256
257bool VisitFriendDecl(FriendDecl *D) {
258// We already visit the TypeLoc properly, but need to special case the decl
259// case.
260if (auto *FD = D->getFriendDecl())
261report(D->getLocation(), FD);
262return true;
263}
264
265bool VisitConceptReference(const ConceptReference *CR) {
266report(CR->getConceptNameLoc(), CR->getFoundDecl());
267return true;
268}
269
270// Report a reference from explicit specializations/instantiations to the
271// specialized template. Implicit ones are filtered out by RAV.
272bool
273VisitClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl *CTSD) {
274// if (CTSD->isExplicitSpecialization())
275if (clang::isTemplateExplicitInstantiationOrSpecialization(
276CTSD->getTemplateSpecializationKind()))
277report(CTSD->getLocation(),
278CTSD->getSpecializedTemplate()->getTemplatedDecl());
279return true;
280}
281bool VisitVarTemplateSpecializationDecl(VarTemplateSpecializationDecl *VTSD) {
282// if (VTSD->isExplicitSpecialization())
283if (clang::isTemplateExplicitInstantiationOrSpecialization(
284VTSD->getTemplateSpecializationKind()))
285report(VTSD->getLocation(),
286VTSD->getSpecializedTemplate()->getTemplatedDecl());
287return true;
288}
289
290// TypeLoc visitors.
291void reportType(SourceLocation RefLoc, NamedDecl *ND) {
292// Reporting explicit references to types nested inside classes can cause
293// issues, e.g. a type accessed through a derived class shouldn't require
294// inclusion of the base.
295// Hence we report all such references as implicit. The code must spell the
296// outer type-location somewhere, which will trigger an explicit reference
297// and per IWYS, it's that spelling's responsibility to bring in necessary
298// declarations.
299RefType RT = llvm::isa<RecordDecl>(ND->getDeclContext())
300? RefType::Implicit
301: RefType::Explicit;
302return report(RefLoc, ND, RT);
303}
304
305bool VisitUsingTypeLoc(UsingTypeLoc TL) {
306reportType(TL.getNameLoc(), TL.getFoundDecl());
307return true;
308}
309
310bool VisitTagTypeLoc(TagTypeLoc TTL) {
311reportType(TTL.getNameLoc(), TTL.getDecl());
312return true;
313}
314
315bool VisitTypedefTypeLoc(TypedefTypeLoc TTL) {
316reportType(TTL.getNameLoc(), TTL.getTypedefNameDecl());
317return true;
318}
319
320bool VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc TL) {
321reportType(TL.getTemplateNameLoc(),
322getMostRelevantTemplatePattern(TL.getTypePtr()));
323return true;
324}
325
326bool VisitDeducedTemplateSpecializationTypeLoc(
327DeducedTemplateSpecializationTypeLoc TL) {
328reportType(TL.getTemplateNameLoc(),
329getMostRelevantTemplatePattern(TL.getTypePtr()));
330return true;
331}
332
333bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &TL) {
334auto &Arg = TL.getArgument();
335// Template-template parameters require special attention, as there's no
336// TemplateNameLoc.
337if (Arg.getKind() == TemplateArgument::Template ||
338Arg.getKind() == TemplateArgument::TemplateExpansion) {
339report(TL.getLocation(),
340resolveTemplateName(Arg.getAsTemplateOrTemplatePattern()));
341return true;
342}
343return RecursiveASTVisitor::TraverseTemplateArgumentLoc(TL);
344}
345
346bool VisitCXXStdInitializerListExpr(CXXStdInitializerListExpr *E) {
347// Reliance on initializer_lists requires std::initializer_list to be
348// visible per standard. So report a reference to it, otherwise include of
349// `<initializer_list>` might not receive any use.
350report(E->getExprLoc(),
351const_cast<CXXRecordDecl *>(E->getBestDynamicClassType()),
352RefType::Implicit);
353return true;
354}
355};
356
357} // namespace
358
359void walkAST(Decl &Root, DeclCallback Callback) {
360ASTWalker(Callback).TraverseDecl(&Root);
361}
362
363} // namespace clang::include_cleaner
364