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 | |
18 | AST_MATCHER(FunctionDecl, doesDeclarationForceExternallyVisibleDefinition) { |
19 | return Node.doesDeclarationForceExternallyVisibleDefinition(); |
20 | } |
21 | |
22 | RedundantDeclarationCheck::RedundantDeclarationCheck(StringRef Name, |
23 | ClangTidyContext *Context) |
24 | : ClangTidyCheck(Name, Context), |
25 | IgnoreMacros(Options.getLocalOrGlobal(LocalName: "IgnoreMacros" , Default: true)) {} |
26 | |
27 | void RedundantDeclarationCheck::storeOptions( |
28 | ClangTidyOptions::OptionMap &Opts) { |
29 | Options.store(Options&: Opts, LocalName: "IgnoreMacros" , Value: IgnoreMacros); |
30 | } |
31 | |
32 | void RedundantDeclarationCheck::registerMatchers(MatchFinder *Finder) { |
33 | Finder->addMatcher( |
34 | NodeMatch: namedDecl(anyOf(varDecl(unless(isDefinition())), |
35 | functionDecl(unless(anyOf( |
36 | isDefinition(), isDefaulted(), |
37 | doesDeclarationForceExternallyVisibleDefinition(), |
38 | hasAncestor(friendDecl()))))), |
39 | optionally(hasParent(linkageSpecDecl().bind(ID: "extern" )))) |
40 | .bind(ID: "Decl" ), |
41 | Action: this); |
42 | } |
43 | |
44 | void RedundantDeclarationCheck::check(const MatchFinder::MatchResult &Result) { |
45 | const auto *D = Result.Nodes.getNodeAs<NamedDecl>(ID: "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 = |
64 | !SM.isInMainFile(Loc: D->getLocation()) && |
65 | !SM.isWrittenInSameFile(Loc1: Prev->getLocation(), Loc2: D->getLocation()); |
66 | |
67 | bool MultiVar = false; |
68 | if (const auto *VD = dyn_cast<VarDecl>(Val: 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 | Loc: D->getSourceRange().getEnd(), Offset: 0, SM, LangOpts: 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>(ID: "extern" ); |
86 | Extern && !Extern->hasBraces()) |
87 | BeginLoc = Extern->getExternLoc(); |
88 | else |
89 | BeginLoc = D->getSourceRange().getBegin(); |
90 | |
91 | Diag << FixItHint::CreateRemoval(RemoveRange: SourceRange(BeginLoc, EndLoc)); |
92 | } |
93 | } |
94 | diag(Prev->getLocation(), "previously declared here" , DiagnosticIDs::Note); |
95 | } |
96 | } // namespace clang::tidy::readability |
97 | |