10#include "clang/AST/ASTContext.h"
11#include "clang/AST/Decl.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
13#include "clang/Lex/Lexer.h"
21AST_MATCHER_P(DeducedTemplateSpecializationType, refsToTemplatedDecl,
22 clang::ast_matchers::internal::Matcher<NamedDecl>, DeclMatcher) {
23 if (
const auto *TD =
Node.getTemplateName().getAsTemplateDecl())
24 return DeclMatcher.matches(*TD, Finder,
Builder);
34 return isa<RecordDecl>(TargetDecl) || isa<ClassTemplateDecl>(TargetDecl) ||
35 isa<FunctionDecl>(TargetDecl) || isa<VarDecl>(TargetDecl) ||
36 isa<FunctionTemplateDecl>(TargetDecl) || isa<EnumDecl>(TargetDecl) ||
37 isa<EnumConstantDecl>(TargetDecl);
43 std::optional<StringRef> HeaderFileExtensionsOption =
45 RawStringHeaderFileExtensions =
47 if (HeaderFileExtensionsOption) {
52 << RawStringHeaderFileExtensions;
55 HeaderFileExtensions = Context->getHeaderFileExtensions();
59 Finder->addMatcher(usingDecl(isExpansionInMainFile()).bind(
"using"),
this);
60 auto DeclMatcher = hasDeclaration(namedDecl().bind(
"used"));
61 Finder->addMatcher(loc(templateSpecializationType(DeclMatcher)),
this);
62 Finder->addMatcher(loc(deducedTemplateSpecializationType(
63 refsToTemplatedDecl(namedDecl().bind(
"used")))),
65 Finder->addMatcher(callExpr(callee(unresolvedLookupExpr().bind(
"used"))),
68 callExpr(hasDeclaration(functionDecl(
69 forEachTemplateArgument(templateArgument().bind(
"used"))))),
71 Finder->addMatcher(loc(templateSpecializationType(forEachTemplateArgument(
72 templateArgument().bind(
"used")))),
74 Finder->addMatcher(userDefinedLiteral().bind(
"used"),
this);
76 loc(elaboratedType(unless(hasQualifier(nestedNameSpecifier())),
77 hasUnqualifiedDesugaredType(type().bind(
"usedType")))),
82 auto ThroughShadowMatcher = throughUsingDecl(namedDecl().bind(
"usedShadow"));
83 Finder->addMatcher(declRefExpr(ThroughShadowMatcher),
this);
84 Finder->addMatcher(loc(usingType(ThroughShadowMatcher)),
this);
88 if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred())
92 if (
auto MainFile = Result.SourceManager->getFileEntryRefForID(
93 Result.SourceManager->getMainFileID());
97 if (
const auto *Using = Result.Nodes.getNodeAs<UsingDecl>(
"using")) {
99 if (Using->getLocation().isMacroID())
103 if (isa<CXXRecordDecl>(Using->getDeclContext()))
109 if (isa<FunctionDecl>(Using->getDeclContext()))
112 UsingDeclContext Context(Using);
113 Context.UsingDeclRange = CharSourceRange::getCharRange(
114 Using->getBeginLoc(),
115 Lexer::findLocationAfterToken(
116 Using->getEndLoc(), tok::semi, *Result.SourceManager,
getLangOpts(),
118 for (
const auto *UsingShadow : Using->shadows()) {
119 const auto *TargetDecl = UsingShadow->getTargetDecl()->getCanonicalDecl();
121 Context.UsingTargetDecls.insert(TargetDecl);
123 if (!Context.UsingTargetDecls.empty())
124 Contexts.push_back(Context);
129 auto RemoveNamedDecl = [&](
const NamedDecl *Used) {
130 removeFromFoundDecls(Used);
132 if (
const auto *FD = dyn_cast<FunctionDecl>(Used)) {
133 removeFromFoundDecls(FD->getPrimaryTemplate());
136 if (
const auto *Specialization =
137 dyn_cast<ClassTemplateSpecializationDecl>(Used)) {
138 removeFromFoundDecls(Specialization->getSpecializedTemplate());
141 if (
const auto *ECD = dyn_cast<EnumConstantDecl>(Used)) {
142 if (
const auto *ET = ECD->getType()->getAs<EnumType>())
143 removeFromFoundDecls(ET->getDecl());
148 if (
const auto *Used = Result.Nodes.getNodeAs<NamedDecl>(
"used")) {
149 RemoveNamedDecl(Used);
153 if (
const auto *T = Result.Nodes.getNodeAs<
Type>(
"usedType")) {
154 if (
const auto *ND = T->getAsTagDecl())
159 if (
const auto *UsedShadow =
160 Result.Nodes.getNodeAs<UsingShadowDecl>(
"usedShadow")) {
161 removeFromFoundDecls(UsedShadow->getTargetDecl());
165 if (
const auto *Used = Result.Nodes.getNodeAs<TemplateArgument>(
"used")) {
166 if (Used->getKind() == TemplateArgument::Template) {
167 if (
const auto *TD = Used->getAsTemplate().getAsTemplateDecl())
168 removeFromFoundDecls(TD);
172 if (Used->getKind() == TemplateArgument::Type) {
173 if (
auto *RD = Used->getAsType()->getAsCXXRecordDecl())
174 removeFromFoundDecls(RD);
178 if (Used->getKind() == TemplateArgument::Declaration) {
179 RemoveNamedDecl(Used->getAsDecl());
184 if (
const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>(
"used")) {
185 RemoveNamedDecl(DRE->getDecl());
189 if (
const auto *ULE = Result.Nodes.getNodeAs<UnresolvedLookupExpr>(
"used")) {
190 for (
const NamedDecl *ND : ULE->decls()) {
191 if (
const auto *USD = dyn_cast<UsingShadowDecl>(ND))
192 removeFromFoundDecls(USD->getTargetDecl()->getCanonicalDecl());
197 if (
const auto *UDL = Result.Nodes.getNodeAs<UserDefinedLiteral>(
"used"))
198 removeFromFoundDecls(UDL->getCalleeDecl());
201void UnusedUsingDeclsCheck::removeFromFoundDecls(
const Decl *D) {
209 for (
auto &Context : Contexts) {
210 if (Context.UsingTargetDecls.contains(D->getCanonicalDecl()))
211 Context.IsUsed =
true;
216 for (
const auto &Context : Contexts) {
217 if (!Context.IsUsed) {
218 diag(Context.FoundUsingDecl->getLocation(),
"using decl %0 is unused")
219 << Context.FoundUsingDecl;
221 diag(Context.FoundUsingDecl->getLocation(),
222 "remove the using", DiagnosticIDs::Note)
223 << FixItHint::CreateRemoval(Context.UsingDeclRange);
const FunctionDecl * Decl
CodeCompletionBuilder Builder
::clang::DynTypedNode Node
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(UserDefinedLiteral, hasLiteral, clang::ast_matchers::internal::Matcher< Expr >, InnerMatcher)
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.