10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12#include "clang/Lex/Lexer.h"
20AST_MATCHER_P(DeducedTemplateSpecializationType, refsToTemplatedDecl,
21 clang::ast_matchers::internal::Matcher<NamedDecl>, DeclMatcher) {
22 if (
const auto *TD = Node.getTemplateName().getAsTemplateDecl())
23 return DeclMatcher.matches(*TD, Finder,
Builder);
33 return isa<RecordDecl>(TargetDecl) || isa<ClassTemplateDecl>(TargetDecl) ||
34 isa<FunctionDecl>(TargetDecl) || isa<VarDecl>(TargetDecl) ||
35 isa<FunctionTemplateDecl>(TargetDecl) || isa<EnumDecl>(TargetDecl) ||
36 isa<EnumConstantDecl>(TargetDecl);
42 std::optional<StringRef> HeaderFileExtensionsOption =
44 RawStringHeaderFileExtensions =
46 if (HeaderFileExtensionsOption) {
51 << RawStringHeaderFileExtensions;
54 HeaderFileExtensions = Context->getHeaderFileExtensions();
58 Finder->addMatcher(usingDecl(isExpansionInMainFile()).bind(
"using"),
this);
59 auto DeclMatcher = hasDeclaration(namedDecl().bind(
"used"));
60 Finder->addMatcher(loc(templateSpecializationType(DeclMatcher)),
this);
61 Finder->addMatcher(loc(deducedTemplateSpecializationType(
62 refsToTemplatedDecl(namedDecl().bind(
"used")))),
64 Finder->addMatcher(callExpr(callee(unresolvedLookupExpr().bind(
"used"))),
67 callExpr(hasDeclaration(functionDecl(
68 forEachTemplateArgument(templateArgument().bind(
"used"))))),
70 Finder->addMatcher(loc(templateSpecializationType(forEachTemplateArgument(
71 templateArgument().bind(
"used")))),
73 Finder->addMatcher(userDefinedLiteral().bind(
"used"),
this);
77 auto ThroughShadowMatcher = throughUsingDecl(namedDecl().bind(
"usedShadow"));
78 Finder->addMatcher(declRefExpr(ThroughShadowMatcher),
this);
79 Finder->addMatcher(loc(usingType(ThroughShadowMatcher)),
this);
83 if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
87 if (
const auto *
MainFile = Result.SourceManager->getFileEntryForID(
88 Result.SourceManager->getMainFileID());
92 if (
const auto *Using = Result.Nodes.getNodeAs<UsingDecl>(
"using")) {
94 if (Using->getLocation().isMacroID())
98 if (isa<CXXRecordDecl>(Using->getDeclContext()))
104 if (isa<FunctionDecl>(Using->getDeclContext()))
107 UsingDeclContext Context(Using);
108 Context.UsingDeclRange = CharSourceRange::getCharRange(
109 Using->getBeginLoc(),
110 Lexer::findLocationAfterToken(
111 Using->getEndLoc(), tok::semi, *Result.SourceManager,
getLangOpts(),
113 for (
const auto *UsingShadow : Using->shadows()) {
114 const auto *TargetDecl = UsingShadow->getTargetDecl()->getCanonicalDecl();
116 Context.UsingTargetDecls.insert(TargetDecl);
118 if (!Context.UsingTargetDecls.empty())
119 Contexts.push_back(Context);
124 auto RemoveNamedDecl = [&](
const NamedDecl *Used) {
125 removeFromFoundDecls(Used);
127 if (
const auto *FD = dyn_cast<FunctionDecl>(Used)) {
128 removeFromFoundDecls(FD->getPrimaryTemplate());
129 }
else if (
const auto *Specialization =
130 dyn_cast<ClassTemplateSpecializationDecl>(Used)) {
131 removeFromFoundDecls(Specialization->getSpecializedTemplate());
132 }
else if (
const auto *ECD = dyn_cast<EnumConstantDecl>(Used)) {
133 if (
const auto *ET = ECD->getType()->getAs<EnumType>())
134 removeFromFoundDecls(ET->getDecl());
139 if (
const auto *Used = Result.Nodes.getNodeAs<NamedDecl>(
"used")) {
140 RemoveNamedDecl(Used);
144 if (
const auto *UsedShadow =
145 Result.Nodes.getNodeAs<UsingShadowDecl>(
"usedShadow")) {
146 removeFromFoundDecls(UsedShadow->getTargetDecl());
150 if (
const auto *Used = Result.Nodes.getNodeAs<TemplateArgument>(
"used")) {
151 if (Used->getKind() == TemplateArgument::Template) {
152 if (
const auto *TD = Used->getAsTemplate().getAsTemplateDecl())
153 removeFromFoundDecls(TD);
154 }
else if (Used->getKind() == TemplateArgument::Type) {
155 if (
auto *RD = Used->getAsType()->getAsCXXRecordDecl())
156 removeFromFoundDecls(RD);
157 }
else if (Used->getKind() == TemplateArgument::Declaration) {
158 RemoveNamedDecl(Used->getAsDecl());
163 if (
const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>(
"used")) {
164 RemoveNamedDecl(DRE->getDecl());
168 if (
const auto *ULE = Result.Nodes.getNodeAs<UnresolvedLookupExpr>(
"used")) {
169 for (
const NamedDecl *ND : ULE->decls()) {
170 if (
const auto *USD = dyn_cast<UsingShadowDecl>(ND))
171 removeFromFoundDecls(USD->getTargetDecl()->getCanonicalDecl());
176 if (
const auto *UDL = Result.Nodes.getNodeAs<UserDefinedLiteral>(
"used"))
177 removeFromFoundDecls(UDL->getCalleeDecl());
180void UnusedUsingDeclsCheck::removeFromFoundDecls(
const Decl *D) {
188 for (
auto &Context : Contexts) {
189 if (Context.UsingTargetDecls.contains(D->getCanonicalDecl()))
190 Context.IsUsed =
true;
195 for (
const auto &Context : Contexts) {
196 if (!Context.IsUsed) {
197 diag(Context.FoundUsingDecl->getLocation(),
"using decl %0 is unused")
198 << Context.FoundUsingDecl;
200 diag(Context.FoundUsingDecl->getLocation(),
201 "remove the using", DiagnosticIDs::Note)
202 << FixItHint::CreateRemoval(Context.UsingDeclRange);
const FunctionDecl * Decl
CodeCompletionBuilder Builder
std::optional< StringRef > get(StringRef LocalName) const
Read a named option from the Context.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
DiagnosticBuilder configurationDiag(StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning) const
Adds a diagnostic to report errors in the check's configuration.
const LangOptions & getLangOpts() const
Returns the language options from the context.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
UnusedUsingDeclsCheck(StringRef Name, ClangTidyContext *Context)
void onEndOfTranslationUnit() override
AST_MATCHER_P(FunctionDecl, parameterCountGE, unsigned, N)
Matches functions that have at least the specified amount of parameters.
static bool shouldCheckDecl(const Decl *TargetDecl)
StringRef defaultHeaderFileExtensions()
Returns recommended default value for the list of header file extensions.
StringRef defaultFileExtensionDelimiters()
Returns recommended default value for the list of file extension delimiters.
bool isFileExtension(StringRef FileName, const FileExtensionsSet &FileExtensions)
Decides whether a file has one of the specified file extensions.
bool parseFileExtensions(StringRef AllFileExtensions, FileExtensionsSet &FileExtensions, StringRef Delimiters)
Parses header file extensions from a semicolon-separated list.