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(recordType(DeclMatcher)), this); 35 Finder->addMatcher(loc(templateSpecializationType(DeclMatcher)), this); 36 Finder->addMatcher(declRefExpr().bind("used"), this); 37 Finder->addMatcher(callExpr(callee(unresolvedLookupExpr().bind("used"))), 38 this); 39 } 40 41 void UnusedUsingDeclsCheck::check(const MatchFinder::MatchResult &Result) { 42 if (const auto *Using = Result.Nodes.getNodeAs<UsingDecl>("using")) { 43 // Ignores using-declarations defined in macros. 44 if (Using->getLocation().isMacroID()) 45 return; 46 47 // Ignores using-declarations defined in class definition. 48 if (isa<CXXRecordDecl>(Using->getDeclContext())) 49 return; 50 51 // FIXME: We ignore using-decls defined in function definitions at the 52 // moment because of false positives caused by ADL and different function 53 // scopes. 54 if (isa<FunctionDecl>(Using->getDeclContext())) 55 return; 56 57 UsingDeclContext Context(Using); 58 Context.UsingDeclRange = CharSourceRange::getCharRange( 59 Using->getLocStart(), 60 Lexer::findLocationAfterToken( 61 Using->getLocEnd(), tok::semi, *Result.SourceManager, 62 Result.Context->getLangOpts(), 63 /*SkipTrailingWhitespaceAndNewLine=*/true)); 64 for (const auto *UsingShadow : Using->shadows()) { 65 const auto *TargetDecl = UsingShadow->getTargetDecl()->getCanonicalDecl(); 66 if (ShouldCheckDecl(TargetDecl)) 67 Context.UsingTargetDecls.insert(TargetDecl); 68 } 69 if (!Context.UsingTargetDecls.empty()) 70 Contexts.push_back(Context); 71 return; 72 } 73 74 // Mark using declarations as used by setting FoundDecls' value to zero. As 75 // the AST is walked in order, usages are only marked after a the 76 // corresponding using declaration has been found. 77 // FIXME: This currently doesn't look at whether the type reference is 78 // actually found with the help of the using declaration. 79 if (const auto *Used = Result.Nodes.getNodeAs<NamedDecl>("used")) { 80 if (const auto *Specialization = 81 dyn_cast<ClassTemplateSpecializationDecl>(Used)) 82 Used = Specialization->getSpecializedTemplate(); 83 removeFromFoundDecls(Used); 84 return; 85 } 86 87 if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("used")) { 88 if (const auto *FD = dyn_cast<FunctionDecl>(DRE->getDecl())) { 89 if (const auto *FDT = FD->getPrimaryTemplate()) 90 removeFromFoundDecls(FDT); 91 else 92 removeFromFoundDecls(FD); 93 } else if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { 94 removeFromFoundDecls(VD); 95 } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(DRE->getDecl())) { 96 removeFromFoundDecls(ECD); 97 } 98 } 99 // Check the uninstantiated template function usage. 100 if (const auto *ULE = Result.Nodes.getNodeAs<UnresolvedLookupExpr>("used")) { 101 for (const NamedDecl* ND : ULE->decls()) { 102 if (const auto *USD = dyn_cast<UsingShadowDecl>(ND)) 103 removeFromFoundDecls(USD->getTargetDecl()->getCanonicalDecl()); 104 } 105 } 106 } 107 108 void UnusedUsingDeclsCheck::removeFromFoundDecls(const Decl *D) { 109 // FIXME: Currently, we don't handle the using-decls being used in different 110 // scopes (such as different namespaces, different functions). Instead of 111 // giving an incorrect message, we mark all of them as used. 112 // 113 // FIXME: Use a more efficient way to find a matching context. 114 for (auto &Context : Contexts) { 115 if (Context.UsingTargetDecls.count(D->getCanonicalDecl()) > 0) 116 Context.IsUsed = true; 117 } 118 } 119 120 void UnusedUsingDeclsCheck::onEndOfTranslationUnit() { 121 for (const auto &Context : Contexts) { 122 if (!Context.IsUsed) { 123 diag(Context.FoundUsingDecl->getLocation(), "using decl %0 is unused") 124 << Context.FoundUsingDecl 125 << FixItHint::CreateRemoval(Context.UsingDeclRange); 126 } 127 } 128 Contexts.clear(); 129 } 130 131 } // namespace misc 132 } // namespace tidy 133 } // namespace clang 134