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(hasAnyTemplateArgument( 46 templateArgument().bind("used")))), 47 this); 48 } 49 50 void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) { 51 if (Result.Context->getDiagnostics().hasUncompilableErrorOccurred()) 52 return; 53 54 if (const auto *Using = Result.Nodes.getNodeAs<UsingDecl>("using")) { 55 // Ignores using-declarations defined in macros. 56 if (Using->getLocation().isMacroID()) 57 return; 58 59 // Ignores using-declarations defined in class definition. 60 if (isa<CXXRecordDecl>(Using->getDeclContext())) 61 return; 62 63 // FIXME: We ignore using-decls defined in function definitions at the 64 // moment because of false positives caused by ADL and different function 65 // scopes. 66 if (isa<FunctionDecl>(Using->getDeclContext())) 67 return; 68 69 UsingDeclContext Context(Using); 70 Context.UsingDeclRange = CharSourceRange::getCharRange( 71 Using->getBeginLoc(), 72 Lexer::findLocationAfterToken( 73 Using->getEndLoc(), tok::semi, *Result.SourceManager, getLangOpts(), 74 /*SkipTrailingWhitespaceAndNewLine=*/true)); 75 for (const auto *UsingShadow : Using->shadows()) { 76 const auto *TargetDecl = UsingShadow->getTargetDecl()->getCanonicalDecl(); 77 if (ShouldCheckDecl(TargetDecl)) 78 Context.UsingTargetDecls.insert(TargetDecl); 79 } 80 if (!Context.UsingTargetDecls.empty()) 81 Contexts.push_back(Context); 82 return; 83 } 84 // Mark using declarations as used by setting FoundDecls' value to zero. As 85 // the AST is walked in order, usages are only marked after a the 86 // corresponding using declaration has been found. 87 // FIXME: This currently doesn't look at whether the type reference is 88 // actually found with the help of the using declaration. 89 if (const auto *Used = Result.Nodes.getNodeAs<NamedDecl>("used")) { 90 if (const auto *FD = dyn_cast<FunctionDecl>(Used)) { 91 removeFromFoundDecls(FD->getPrimaryTemplate()); 92 } else if (const auto *Specialization = 93 dyn_cast<ClassTemplateSpecializationDecl>(Used)) { 94 Used = Specialization->getSpecializedTemplate(); 95 } 96 removeFromFoundDecls(Used); 97 return; 98 } 99 100 if (const auto *Used = Result.Nodes.getNodeAs<TemplateArgument>("used")) { 101 // FIXME: Support non-type template parameters. 102 if (Used->getKind() == TemplateArgument::Template) { 103 if (const auto *TD = Used->getAsTemplate().getAsTemplateDecl()) 104 removeFromFoundDecls(TD); 105 } else if (Used->getKind() == TemplateArgument::Type) { 106 if (auto *RD = Used->getAsType()->getAsCXXRecordDecl()) 107 removeFromFoundDecls(RD); 108 } 109 return; 110 } 111 112 if (const auto *Used = Result.Nodes.getNodeAs<TemplateName>("used")) { 113 removeFromFoundDecls(Used->getAsTemplateDecl()); 114 return; 115 } 116 117 if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("used")) { 118 if (const auto *FD = dyn_cast<FunctionDecl>(DRE->getDecl())) { 119 if (const auto *FDT = FD->getPrimaryTemplate()) 120 removeFromFoundDecls(FDT); 121 else 122 removeFromFoundDecls(FD); 123 } else if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { 124 removeFromFoundDecls(VD); 125 } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl())) { 126 removeFromFoundDecls(ECD); 127 if (const auto *ET = ECD->getType()->getAs<EnumType>()) 128 removeFromFoundDecls(ET->getDecl()); 129 } 130 } 131 // Check the uninstantiated template function usage. 132 if (const auto *ULE = Result.Nodes.getNodeAs<UnresolvedLookupExpr>("used")) { 133 for (const NamedDecl *ND : ULE->decls()) { 134 if (const auto *USD = dyn_cast<UsingShadowDecl>(ND)) 135 removeFromFoundDecls(USD->getTargetDecl()->getCanonicalDecl()); 136 } 137 } 138 } 139 140 void UnusedUsingDeclsCheck::removeFromFoundDecls(const Decl *D) { 141 if (!D) 142 return; 143 // FIXME: Currently, we don't handle the using-decls being used in different 144 // scopes (such as different namespaces, different functions). Instead of 145 // giving an incorrect message, we mark all of them as used. 146 // 147 // FIXME: Use a more efficient way to find a matching context. 148 for (auto &Context : Contexts) { 149 if (Context.UsingTargetDecls.count(D->getCanonicalDecl()) > 0) 150 Context.IsUsed = true; 151 } 152 } 153 154 void UnusedUsingDeclsCheck::onEndOfTranslationUnit() { 155 for (const auto &Context : Contexts) { 156 if (!Context.IsUsed) { 157 diag(Context.FoundUsingDecl->getLocation(), "using decl %0 is unused") 158 << Context.FoundUsingDecl 159 << FixItHint::CreateRemoval(Context.UsingDeclRange); 160 } 161 } 162 Contexts.clear(); 163 } 164 165 } // namespace misc 166 } // namespace tidy 167 } // namespace clang 168