xref: /llvm-project/clang-tools-extra/clang-tidy/readability/RedundantDeclarationCheck.cpp (revision 11a411a49b62c129bba551df4587dd446fcdc660)
1 //===--- RedundantDeclarationCheck.cpp - clang-tidy------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "RedundantDeclarationCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/Lex/Lexer.h"
13 
14 using namespace clang::ast_matchers;
15 
16 namespace clang::tidy::readability {
17 
AST_MATCHER(FunctionDecl,doesDeclarationForceExternallyVisibleDefinition)18 AST_MATCHER(FunctionDecl, doesDeclarationForceExternallyVisibleDefinition) {
19   return Node.doesDeclarationForceExternallyVisibleDefinition();
20 }
21 
RedundantDeclarationCheck(StringRef Name,ClangTidyContext * Context)22 RedundantDeclarationCheck::RedundantDeclarationCheck(StringRef Name,
23                                                      ClangTidyContext *Context)
24     : ClangTidyCheck(Name, Context),
25       IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", true)) {}
26 
storeOptions(ClangTidyOptions::OptionMap & Opts)27 void RedundantDeclarationCheck::storeOptions(
28     ClangTidyOptions::OptionMap &Opts) {
29   Options.store(Opts, "IgnoreMacros", IgnoreMacros);
30 }
31 
registerMatchers(MatchFinder * Finder)32 void RedundantDeclarationCheck::registerMatchers(MatchFinder *Finder) {
33   Finder->addMatcher(
34       namedDecl(anyOf(varDecl(unless(isDefinition())),
35                       functionDecl(unless(anyOf(
36                           isDefinition(), isDefaulted(),
37                           doesDeclarationForceExternallyVisibleDefinition(),
38                           hasAncestor(friendDecl()))))),
39                 optionally(hasParent(linkageSpecDecl().bind("extern"))))
40           .bind("Decl"),
41       this);
42 }
43 
check(const MatchFinder::MatchResult & Result)44 void RedundantDeclarationCheck::check(const MatchFinder::MatchResult &Result) {
45   const auto *D = Result.Nodes.getNodeAs<NamedDecl>("Decl");
46   const auto *Prev = D->getPreviousDecl();
47   if (!Prev)
48     return;
49   if (!Prev->getLocation().isValid())
50     return;
51   if (Prev->getLocation() == D->getLocation())
52     return;
53   if (IgnoreMacros &&
54       (D->getLocation().isMacroID() || Prev->getLocation().isMacroID()))
55     return;
56   // Don't complain when the previous declaration is a friend declaration.
57   for (const auto &Parent : Result.Context->getParents(*Prev))
58     if (Parent.get<FriendDecl>())
59       return;
60 
61   const SourceManager &SM = *Result.SourceManager;
62 
63   const bool DifferentHeaders =
64       !SM.isInMainFile(D->getLocation()) &&
65       !SM.isWrittenInSameFile(Prev->getLocation(), D->getLocation());
66 
67   bool MultiVar = false;
68   if (const auto *VD = dyn_cast<VarDecl>(D)) {
69     // Is this a multivariable declaration?
70     for (const auto *Other : VD->getDeclContext()->decls()) {
71       if (Other != D && Other->getBeginLoc() == VD->getBeginLoc()) {
72         MultiVar = true;
73         break;
74       }
75     }
76   }
77 
78   SourceLocation EndLoc = Lexer::getLocForEndOfToken(
79       D->getSourceRange().getEnd(), 0, SM, Result.Context->getLangOpts());
80   {
81     auto Diag = diag(D->getLocation(), "redundant %0 declaration") << D;
82     if (!MultiVar && !DifferentHeaders) {
83       SourceLocation BeginLoc;
84       if (const auto *Extern =
85               Result.Nodes.getNodeAs<LinkageSpecDecl>("extern");
86           Extern && !Extern->hasBraces())
87         BeginLoc = Extern->getExternLoc();
88       else
89         BeginLoc = D->getSourceRange().getBegin();
90 
91       Diag << FixItHint::CreateRemoval(SourceRange(BeginLoc, EndLoc));
92     }
93   }
94   diag(Prev->getLocation(), "previously declared here", DiagnosticIDs::Note);
95 }
96 } // namespace clang::tidy::readability
97