165 lines
5.4 KiB
C++
165 lines
5.4 KiB
C++
//===--- WalkAST.cpp - Find declaration references in the AST -------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "AnalysisInternal.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/AST/DeclCXX.h"
|
|
#include "clang/AST/Expr.h"
|
|
#include "clang/AST/ExprCXX.h"
|
|
#include "clang/AST/RecursiveASTVisitor.h"
|
|
#include "clang/AST/TemplateName.h"
|
|
#include "clang/AST/Type.h"
|
|
#include "clang/AST/TypeLoc.h"
|
|
#include "clang/Basic/SourceLocation.h"
|
|
#include "clang/Basic/SourceManager.h"
|
|
#include "llvm/ADT/STLExtras.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;
|
|
// Whether we're traversing declarations coming from a header file.
|
|
// This helps figure out whether certain symbols can be assumed as unused
|
|
// (e.g. overloads brought into an implementation file, but not used).
|
|
bool IsHeader = false;
|
|
|
|
bool handleTemplateName(SourceLocation Loc, TemplateName TN) {
|
|
// For using-templates, only mark the alias.
|
|
if (auto *USD = TN.getAsUsingShadowDecl()) {
|
|
report(Loc, USD);
|
|
return true;
|
|
}
|
|
report(Loc, TN.getAsTemplateDecl());
|
|
return true;
|
|
}
|
|
|
|
void report(SourceLocation Loc, NamedDecl *ND,
|
|
RefType RT = RefType::Explicit) {
|
|
if (!ND || Loc.isInvalid())
|
|
return;
|
|
Callback(Loc, *cast<NamedDecl>(ND->getCanonicalDecl()), RT);
|
|
}
|
|
|
|
public:
|
|
ASTWalker(DeclCallback Callback, bool IsHeader)
|
|
: Callback(Callback), IsHeader(IsHeader) {}
|
|
|
|
bool VisitDeclRefExpr(DeclRefExpr *DRE) {
|
|
report(DRE->getLocation(), DRE->getFoundDecl());
|
|
return true;
|
|
}
|
|
|
|
bool VisitMemberExpr(MemberExpr *E) {
|
|
report(E->getMemberLoc(), E->getFoundDecl().getDecl());
|
|
return true;
|
|
}
|
|
|
|
bool VisitCXXConstructExpr(CXXConstructExpr *E) {
|
|
report(E->getLocation(), E->getConstructor(),
|
|
E->getParenOrBraceRange().isValid() ? RefType::Explicit
|
|
: RefType::Implicit);
|
|
return true;
|
|
}
|
|
|
|
bool VisitOverloadExpr(OverloadExpr *E) {
|
|
// Since we can't prove which overloads are used, report all of them.
|
|
llvm::for_each(E->decls(), [this, E](NamedDecl *D) {
|
|
report(E->getNameLoc(), D, RefType::Ambiguous);
|
|
});
|
|
return true;
|
|
}
|
|
|
|
bool VisitUsingDecl(UsingDecl *UD) {
|
|
for (const auto *Shadow : UD->shadows()) {
|
|
auto *TD = Shadow->getTargetDecl();
|
|
auto IsUsed = TD->isUsed() || TD->isReferenced();
|
|
// We ignore unused overloads inside implementation files, as the ones in
|
|
// headers might still be used by the dependents of the header.
|
|
if (!IsUsed && !IsHeader)
|
|
continue;
|
|
report(UD->getLocation(), TD,
|
|
IsUsed ? RefType::Explicit : RefType::Ambiguous);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool VisitFunctionDecl(FunctionDecl *FD) {
|
|
// Mark declaration from definition as it needs type-checking.
|
|
if (FD->isThisDeclarationADefinition())
|
|
report(FD->getLocation(), FD);
|
|
return true;
|
|
}
|
|
|
|
bool VisitEnumDecl(EnumDecl *D) {
|
|
// Definition of an enum with an underlying type references declaration for
|
|
// type-checking purposes.
|
|
if (D->isThisDeclarationADefinition() && D->getIntegerTypeSourceInfo())
|
|
report(D->getLocation(), D);
|
|
return true;
|
|
}
|
|
|
|
// TypeLoc visitors.
|
|
bool VisitUsingTypeLoc(UsingTypeLoc TL) {
|
|
report(TL.getNameLoc(), TL.getFoundDecl());
|
|
return true;
|
|
}
|
|
|
|
bool VisitTagTypeLoc(TagTypeLoc TTL) {
|
|
report(TTL.getNameLoc(), TTL.getDecl());
|
|
return true;
|
|
}
|
|
|
|
bool VisitTypedefTypeLoc(TypedefTypeLoc TTL) {
|
|
report(TTL.getNameLoc(), TTL.getTypedefNameDecl());
|
|
return true;
|
|
}
|
|
|
|
bool VisitTemplateSpecializationTypeLoc(TemplateSpecializationTypeLoc TL) {
|
|
// FIXME: Handle explicit specializations.
|
|
return handleTemplateName(TL.getTemplateNameLoc(),
|
|
TL.getTypePtr()->getTemplateName());
|
|
}
|
|
|
|
bool VisitDeducedTemplateSpecializationTypeLoc(
|
|
DeducedTemplateSpecializationTypeLoc TL) {
|
|
// FIXME: Handle specializations.
|
|
return handleTemplateName(TL.getTemplateNameLoc(),
|
|
TL.getTypePtr()->getTemplateName());
|
|
}
|
|
|
|
bool TraverseTemplateArgumentLoc(const TemplateArgumentLoc &TL) {
|
|
auto &Arg = TL.getArgument();
|
|
// Template-template parameters require special attention, as there's no
|
|
// TemplateNameLoc.
|
|
if (Arg.getKind() == TemplateArgument::Template ||
|
|
Arg.getKind() == TemplateArgument::TemplateExpansion)
|
|
return handleTemplateName(TL.getLocation(),
|
|
Arg.getAsTemplateOrTemplatePattern());
|
|
return RecursiveASTVisitor::TraverseTemplateArgumentLoc(TL);
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
void walkAST(Decl &Root, DeclCallback Callback) {
|
|
auto &AST = Root.getASTContext();
|
|
auto &SM = AST.getSourceManager();
|
|
// If decl isn't written in main file, assume it's coming from an include,
|
|
// hence written in a header.
|
|
bool IsRootedAtHeader =
|
|
AST.getLangOpts().IsHeaderFile ||
|
|
!SM.isWrittenInMainFile(SM.getSpellingLoc(Root.getLocation()));
|
|
ASTWalker(Callback, IsRootedAtHeader).TraverseDecl(&Root);
|
|
}
|
|
|
|
} // namespace clang::include_cleaner
|