1 //===--- UnusedUsingDeclsCheck.cpp - clang-tidy----------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "UnusedUsingDeclsCheck.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 #include "clang/Lex/Lexer.h" 14 15 using namespace clang::ast_matchers; 16 17 namespace clang { 18 namespace tidy { 19 namespace misc { 20 21 // A function that helps to tell whether a TargetDecl in a UsingDecl will be 22 // checked. Only variable, function, function template, class template, class, 23 // enum declaration and enum constant declaration are considered. 24 static bool ShouldCheckDecl(const Decl *TargetDecl) { 25 return isa<RecordDecl>(TargetDecl) || isa<ClassTemplateDecl>(TargetDecl) || 26 isa<FunctionDecl>(TargetDecl) || isa<VarDecl>(TargetDecl) || 27 isa<FunctionTemplateDecl>(TargetDecl) || isa<EnumDecl>(TargetDecl) || 28 isa<EnumConstantDecl>(TargetDecl); 29 } 30 31 void UnusedUsingDeclsCheck::registerMatchers(MatchFinder *Finder) { 32 Finder->addMatcher(usingDecl(isExpansionInMainFile()).bind("using"), this); 33 auto DeclMatcher = hasDeclaration(namedDecl().bind("used")); 34 Finder->addMatcher(loc(enumType(DeclMatcher)), this); 35 Finder->addMatcher(loc(recordType(DeclMatcher)), this); 36 Finder->addMatcher(loc(templateSpecializationType(DeclMatcher)), this); 37 Finder->addMatcher(declRefExpr().bind("used"), this); 38 Finder->addMatcher(callExpr(callee(unresolvedLookupExpr().bind("used"))), 39 this); 40 Finder->addMatcher( 41 callExpr(hasDeclaration(functionDecl(hasAnyTemplateArgument( 42 anyOf(refersToTemplate(templateName().bind("used")), 43 refersToDeclaration(functionDecl().bind("used"))))))), 44 this); 45 Finder->addMatcher(loc(templateSpecializationType( 46 hasAnyTemplateArgument(templateArgument().bind("used")))), this); 47 } 48 49 void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) { 50 if (const auto *Using = Result.Nodes.getNodeAs<UsingDecl>("using")) { 51 // Ignores using-declarations defined in macros. 52 if (Using->getLocation().isMacroID()) 53 return; 54 55 // Ignores using-declarations defined in class definition. 56 if (isa<CXXRecordDecl>(Using->getDeclContext())) 57 return; 58 59 // FIXME: We ignore using-decls defined in function definitions at the 60 // moment because of false positives caused by ADL and different function 61 // scopes. 62 if (isa<FunctionDecl>(Using->getDeclContext())) 63 return; 64 65 UsingDeclContext Context(Using); 66 Context.UsingDeclRange = CharSourceRange::getCharRange( 67 Using->getLocStart(), 68 Lexer::findLocationAfterToken( 69 Using->getLocEnd(), tok::semi, *Result.SourceManager, getLangOpts(), 70 /*SkipTrailingWhitespaceAndNewLine=*/true)); 71 for (const auto *UsingShadow : Using->shadows()) { 72 const auto *TargetDecl = UsingShadow->getTargetDecl()->getCanonicalDecl(); 73 if (ShouldCheckDecl(TargetDecl)) 74 Context.UsingTargetDecls.insert(TargetDecl); 75 } 76 if (!Context.UsingTargetDecls.empty()) 77 Contexts.push_back(Context); 78 return; 79 } 80 // Mark using declarations as used by setting FoundDecls' value to zero. As 81 // the AST is walked in order, usages are only marked after a the 82 // corresponding using declaration has been found. 83 // FIXME: This currently doesn't look at whether the type reference is 84 // actually found with the help of the using declaration. 85 if (const auto *Used = Result.Nodes.getNodeAs<NamedDecl>("used")) { 86 if (const auto *FD = dyn_cast<FunctionDecl>(Used)) { 87 removeFromFoundDecls(FD->getPrimaryTemplate()); 88 } else if (const auto *Specialization = 89 dyn_cast<ClassTemplateSpecializationDecl>(Used)) { 90 Used = Specialization->getSpecializedTemplate(); 91 } 92 removeFromFoundDecls(Used); 93 return; 94 } 95 96 if (const auto *Used = Result.Nodes.getNodeAs<TemplateArgument>("used")) { 97 // FIXME: Support non-type template parameters. 98 if (Used->getKind() == TemplateArgument::Template) { 99 if (const auto *TD = Used->getAsTemplate().getAsTemplateDecl()) 100 removeFromFoundDecls(TD); 101 } else if (Used->getKind() == TemplateArgument::Type) { 102 if (auto *RD = Used->getAsType()->getAsCXXRecordDecl()) 103 removeFromFoundDecls(RD); 104 } 105 return; 106 } 107 108 if (const auto *Used = Result.Nodes.getNodeAs<TemplateName>("used")) { 109 removeFromFoundDecls(Used->getAsTemplateDecl()); 110 return; 111 } 112 113 if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("used")) { 114 if (const auto *FD = dyn_cast<FunctionDecl>(DRE->getDecl())) { 115 if (const auto *FDT = FD->getPrimaryTemplate()) 116 removeFromFoundDecls(FDT); 117 else 118 removeFromFoundDecls(FD); 119 } else if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { 120 removeFromFoundDecls(VD); 121 } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl())) { 122 removeFromFoundDecls(ECD); 123 if (const auto *ET = ECD->getType()->getAs<EnumType>()) 124 removeFromFoundDecls(ET->getDecl()); 125 } 126 } 127 // Check the uninstantiated template function usage. 128 if (const auto *ULE = Result.Nodes.getNodeAs<UnresolvedLookupExpr>("used")) { 129 for (const NamedDecl* ND : ULE->decls()) { 130 if (const auto *USD = dyn_cast<UsingShadowDecl>(ND)) 131 removeFromFoundDecls(USD->getTargetDecl()->getCanonicalDecl()); 132 } 133 } 134 } 135 136 void UnusedUsingDeclsCheck::removeFromFoundDecls(const Decl *D) { 137 if (!D) 138 return; 139 // FIXME: Currently, we don't handle the using-decls being used in different 140 // scopes (such as different namespaces, different functions). Instead of 141 // giving an incorrect message, we mark all of them as used. 142 // 143 // FIXME: Use a more efficient way to find a matching context. 144 for (auto &Context : Contexts) { 145 if (Context.UsingTargetDecls.count(D->getCanonicalDecl()) > 0) 146 Context.IsUsed = true; 147 } 148 } 149 150 void UnusedUsingDeclsCheck::onEndOfTranslationUnit() { 151 for (const auto &Context : Contexts) { 152 if (!Context.IsUsed) { 153 diag(Context.FoundUsingDecl->getLocation(), "using decl %0 is unused") 154 << Context.FoundUsingDecl 155 << FixItHint::CreateRemoval(Context.UsingDeclRange); 156 } 157 } 158 Contexts.clear(); 159 } 160 161 } // namespace misc 162 } // namespace tidy 163 } // namespace clang 164