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