#include "AnalysisInternal.h"
#include "clang-include-cleaner/Types.h"
#include "clang/AST/ASTFwd.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclFriend.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/TemplateBase.h"
#include "clang/AST/TemplateName.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeLoc.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/Specifiers.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/STLFunctionalExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
namespace clang::include_cleaner {
namespace {
using DeclCallback =
llvm::function_ref<void(SourceLocation, NamedDecl &, RefType)>;
class ASTWalker : public RecursiveASTVisitor<ASTWalker> {
DeclCallback Callback;
void report(SourceLocation Loc, NamedDecl *ND,
RefType RT = RefType::Explicit) {
if (!ND || Loc.isInvalid())
return;
Callback(Loc, *cast<NamedDecl>(ND->getCanonicalDecl()), RT);
}
NamedDecl *resolveTemplateName(TemplateName TN) {
if (auto *USD = TN.getAsUsingShadowDecl())
return USD;
return TN.getAsTemplateDecl();
}
NamedDecl *getMemberProvider(QualType Base) {
if (Base->isPointerType())
return getMemberProvider(Base->getPointeeType());
if (const auto *ElTy = dyn_cast<ElaboratedType>(Base))
return getMemberProvider(ElTy->getNamedType());
if (const auto *TT = dyn_cast<TypedefType>(Base))
return TT->getDecl();
if (const auto *UT = dyn_cast<UsingType>(Base))
return UT->getFoundDecl();
if (const auto *TST = dyn_cast<TemplateSpecializationType>(Base))
return resolveTemplateName(TST->getTemplateName());
return Base->getAsRecordDecl();
}
template <typename T>
NamedDecl *getMostRelevantTemplatePattern(const T *TST) {
auto *ND = resolveTemplateName(TST->getTemplateName());
if (llvm::isa_and_present<UsingShadowDecl, TypeAliasTemplateDecl>(ND))
return ND;
CXXRecordDecl *TD = TST->getAsCXXRecordDecl();
if (!TD || TD->getTemplateSpecializationKind() == TSK_Undeclared)
return ND;
if (auto *Pat = TD->getTemplateInstantiationPattern())
return Pat;
return TD;
}
public:
ASTWalker(DeclCallback Callback) : Callback(Callback) {}
bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *S) {
if (!WalkUpFromCXXOperatorCallExpr(S))
return false;
if (auto *CD = S->getCalleeDecl()) {
if (llvm::isa<CXXMethodDecl>(CD)) {
report(S->getOperatorLoc(), getMemberProvider(S->getArg(0)->getType()),
RefType::Implicit);
} else {
report(S->getOperatorLoc(), llvm::dyn_cast<NamedDecl>(CD),
RefType::Implicit);
}
}
for (auto *Arg : S->arguments())
if (!TraverseStmt(Arg))
return false;
return true;
}
bool VisitDeclRefExpr(DeclRefExpr *DRE) {
auto *FD = DRE->getFoundDecl();
if (!llvm::isa<UsingShadowDecl>(FD))
FD = DRE->getDecl();
if (!FD->isCXXClassMember() && !llvm::isa<EnumConstantDecl>(FD)) {
report(DRE->getLocation(), FD);
return true;
}
if (llvm::isa<EnumConstantDecl>(FD)) {
if (!DRE->getQualifier() || DRE->getQualifier()->getAsNamespace())
report(DRE->getLocation(), FD);
}
return true;
}
bool VisitMemberExpr(MemberExpr *E) {
QualType Type = E->getBase()->IgnoreImpCasts()->getType();
report(E->getMemberLoc(), getMemberProvider(Type), RefType::Implicit);
return true;
}
bool VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E) {
report(E->getMemberLoc(), getMemberProvider(E->getBaseType()),
RefType::Implicit);
return true;
}
bool VisitCXXConstructExpr(CXXConstructExpr *E) {
report(E->getLocation(), getMemberProvider(E->getType()),
RefType::Implicit);
return true;
}
bool VisitOverloadExpr(OverloadExpr *E) {
for (NamedDecl *D : E->decls())
report(E->getNameLoc(), D, RefType::Ambiguous);
return true;
}
template <typename TemplateDeclType, typename ParitialDeclType>
void reportSpecializations(SourceLocation Loc, NamedDecl *ND) {
const auto *TD = llvm::dyn_cast<TemplateDeclType>(ND);
if (!TD)
return;
for (auto *Spec : TD->specializations())
report(Loc, Spec, RefType::Ambiguous);
llvm::SmallVector<ParitialDeclType *> PartialSpecializations;
TD->getPartialSpecializations(PartialSpecializations);
for (auto *PartialSpec : PartialSpecializations)
report(Loc, PartialSpec, RefType::Ambiguous);
}
bool VisitUsingDecl(UsingDecl *UD) {
for (const auto *Shadow : UD->shadows()) {
auto *TD = Shadow->getTargetDecl();
auto IsUsed = TD->isUsed() || TD->isReferenced();
report(UD->getLocation(), TD,
IsUsed ? RefType::Explicit : RefType::Ambiguous);
reportSpecializations<ClassTemplateDecl,
ClassTemplatePartialSpecializationDecl>(
UD->getLocation(), TD);
reportSpecializations<VarTemplateDecl,
VarTemplatePartialSpecializationDecl>(
UD->getLocation(), TD);
if (const auto *FTD = llvm::dyn_cast<FunctionTemplateDecl>(TD))
for (auto *Spec : FTD->specializations())
report(UD->getLocation(), Spec, RefType::Ambiguous);
}
return true;
}
bool VisitFunctionDecl(FunctionDecl *FD) {
if (FD->isThisDeclarationADefinition())
report(FD->getLocation(), FD);
if (clang::isTemplateExplicitInstantiationOrSpecialization(
FD->getTemplateSpecializationKind()))
report(FD->getLocation(), FD->getPrimaryTemplate());
return true;
}
bool VisitVarDecl(VarDecl *VD) {
if (llvm::isa<ParmVarDecl>(VD))
return true;
if (VD->isThisDeclarationADefinition())
report(VD->getLocation(), VD);
return true;
}
bool VisitEnumDecl(EnumDecl *D) {
if (D->isThisDeclarationADefinition() && D->getIntegerTypeSourceInfo())
report(D->getLocation(), D);
return true;
}
bool VisitFriendDecl(FriendDecl *D) {
if (auto *FD = D->getFriendDecl())
report(D->getLocation(), FD);
return true;
}
bool VisitConceptReference(const ConceptReference *CR) {
report(CR->getConceptNameLoc(), CR->getFoundDecl());
return true;
}
bool
VisitClassTemplateSpecializationDecl(ClassTemplateSpecializationDecl *CTSD) {
if (clang::isTemplateExplicitInstantiationOrSpecialization(
CTSD->getTemplateSpecializationKind()))
report(CTSD->getLocation(),
CTSD->getSpecializedTemplate()->getTemplatedDecl());
return true;
}
bool VisitVarTemplateSpecializationDecl(VarTemplateSpecializationDecl *VTSD) {
if (clang::isTemplateExplicitInstantiationOrSpecialization(
VTSD->getTemplateSpecializationKind()))
report(VTSD->getLocation(),
VTSD->getSpecializedTemplate()->getTemplatedDecl());
return true;
}
void reportType(SourceLocation RefLoc, NamedDecl *ND) {
RefType RT = llvm::isa<RecordDecl>(ND->getDeclContext())
? RefType::Implicit
: RefType::Explicit;
return report(RefLoc, ND, RT);
}
bool VisitUsingTypeLoc(UsingTypeLoc TL) {
reportType(TL.getNameLoc(), TL.getFoundDecl());
return true;
}
bool VisitTagTypeLoc(TagTypeLoc TTL) {
reportType(TTL.getNameLoc(), TTL.getDecl());
return true;
}
bool VisitTypedefTypeLoc(TypedefTypeLoc TTL) {
reportType(TTL.getNameLoc(), TTL.getTypedefNameDecl());
return true;
}
bool VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc TL) {
reportType(TL.getTemplateNameLoc(),
getMostRelevantTemplatePattern(TL.getTypePtr()));
return true;
}
bool VisitDeducedTemplateSpecializationTypeLoc(
DeducedTemplateSpecializationTypeLoc TL) {
reportType(TL.getTemplateNameLoc(),
getMostRelevantTemplatePattern(TL.getTypePtr()));
return true;
}
bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &TL) {
auto &Arg = TL.getArgument();
if (Arg.getKind() == TemplateArgument::Template ||
Arg.getKind() == TemplateArgument::TemplateExpansion) {
report(TL.getLocation(),
resolveTemplateName(Arg.getAsTemplateOrTemplatePattern()));
return true;
}
return RecursiveASTVisitor::TraverseTemplateArgumentLoc(TL);
}
bool VisitCXXStdInitializerListExpr(CXXStdInitializerListExpr *E) {
report(E->getExprLoc(),
const_cast<CXXRecordDecl *>(E->getBestDynamicClassType()),
RefType::Implicit);
return true;
}
};
}
void walkAST(Decl &Root, DeclCallback Callback) {
ASTWalker(Callback).TraverseDecl(&Root);
}
}