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 | namespace { |
19 | |
20 | AST_MATCHER(FunctionDecl, doesDeclarationForceExternallyVisibleDefinition) { |
21 | return Node.doesDeclarationForceExternallyVisibleDefinition(); |
22 | } |
23 | |
24 | } // namespace |
25 | |
26 | RedundantDeclarationCheck::RedundantDeclarationCheck(StringRef Name, |
27 | ClangTidyContext *Context) |
28 | : ClangTidyCheck(Name, Context), |
29 | IgnoreMacros(Options.getLocalOrGlobal(LocalName: "IgnoreMacros" , Default: true)) {} |
30 | |
31 | void RedundantDeclarationCheck::storeOptions( |
32 | ClangTidyOptions::OptionMap &Opts) { |
33 | Options.store(Options&: Opts, LocalName: "IgnoreMacros" , Value: IgnoreMacros); |
34 | } |
35 | |
36 | void RedundantDeclarationCheck::registerMatchers(MatchFinder *Finder) { |
37 | Finder->addMatcher( |
38 | NodeMatch: namedDecl(anyOf(varDecl(unless(isDefinition())), |
39 | functionDecl(unless(anyOf( |
40 | isDefinition(), isDefaulted(), |
41 | doesDeclarationForceExternallyVisibleDefinition(), |
42 | hasAncestor(friendDecl()))))), |
43 | optionally(hasParent(linkageSpecDecl().bind(ID: "extern" )))) |
44 | .bind(ID: "Decl" ), |
45 | Action: this); |
46 | } |
47 | |
48 | void RedundantDeclarationCheck::check(const MatchFinder::MatchResult &Result) { |
49 | const auto *D = Result.Nodes.getNodeAs<NamedDecl>(ID: "Decl" ); |
50 | const auto *Prev = D->getPreviousDecl(); |
51 | if (!Prev) |
52 | return; |
53 | if (!Prev->getLocation().isValid()) |
54 | return; |
55 | if (Prev->getLocation() == D->getLocation()) |
56 | return; |
57 | if (IgnoreMacros && |
58 | (D->getLocation().isMacroID() || Prev->getLocation().isMacroID())) |
59 | return; |
60 | // Don't complain when the previous declaration is a friend declaration. |
61 | for (const auto &Parent : Result.Context->getParents(*Prev)) |
62 | if (Parent.get<FriendDecl>()) |
63 | return; |
64 | |
65 | const SourceManager &SM = *Result.SourceManager; |
66 | |
67 | const bool = |
68 | !SM.isInMainFile(Loc: D->getLocation()) && |
69 | !SM.isWrittenInSameFile(Loc1: Prev->getLocation(), Loc2: D->getLocation()); |
70 | |
71 | bool MultiVar = false; |
72 | if (const auto *VD = dyn_cast<VarDecl>(Val: D)) { |
73 | // Is this a multivariable declaration? |
74 | for (const auto *Other : VD->getDeclContext()->decls()) { |
75 | if (Other != D && Other->getBeginLoc() == VD->getBeginLoc()) { |
76 | MultiVar = true; |
77 | break; |
78 | } |
79 | } |
80 | } |
81 | |
82 | SourceLocation EndLoc = Lexer::getLocForEndOfToken( |
83 | Loc: D->getSourceRange().getEnd(), Offset: 0, SM, LangOpts: Result.Context->getLangOpts()); |
84 | { |
85 | auto Diag = diag(D->getLocation(), "redundant %0 declaration" ) << D; |
86 | if (!MultiVar && !DifferentHeaders) { |
87 | SourceLocation BeginLoc; |
88 | if (const auto *Extern = |
89 | Result.Nodes.getNodeAs<LinkageSpecDecl>(ID: "extern" ); |
90 | Extern && !Extern->hasBraces()) |
91 | BeginLoc = Extern->getExternLoc(); |
92 | else |
93 | BeginLoc = D->getSourceRange().getBegin(); |
94 | |
95 | Diag << FixItHint::CreateRemoval(RemoveRange: SourceRange(BeginLoc, EndLoc)); |
96 | } |
97 | } |
98 | diag(Prev->getLocation(), "previously declared here" , DiagnosticIDs::Note); |
99 | } |
100 | } // namespace clang::tidy::readability |
101 | |