|
8 | 8 |
|
9 | 9 | #include "RedundantControlFlowCheck.h" |
10 | 10 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
| 11 | +#include "clang/ASTMatchers/ASTMatchersMacros.h" |
11 | 12 | #include "clang/Lex/Lexer.h" |
12 | 13 |
|
13 | 14 | using namespace clang::ast_matchers; |
14 | 15 |
|
15 | 16 | namespace clang::tidy::readability { |
16 | 17 |
|
17 | | -static const char *const RedundantReturnDiag = |
| 18 | +namespace { |
| 19 | + |
| 20 | +AST_MATCHER_P(CompoundStmt, hasFinalStmt, StatementMatcher, InnerMatcher) { |
| 21 | + return !Node.body_empty() && |
| 22 | + InnerMatcher.matches(*Node.body_back(), Finder, Builder); |
| 23 | +} |
| 24 | + |
| 25 | +} // namespace |
| 26 | + |
| 27 | +static constexpr StringRef RedundantReturnDiag = |
18 | 28 | "redundant return statement at the end " |
19 | 29 | "of a function with a void return type"; |
20 | | -static const char *const RedundantContinueDiag = |
| 30 | +static constexpr StringRef RedundantContinueDiag = |
21 | 31 | "redundant continue statement at the " |
22 | 32 | "end of loop statement"; |
23 | 33 |
|
24 | | -static bool isLocationInMacroExpansion(const SourceManager &SM, |
25 | | - SourceLocation Loc) { |
26 | | - return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc); |
27 | | -} |
28 | | - |
29 | 34 | void RedundantControlFlowCheck::registerMatchers(MatchFinder *Finder) { |
30 | 35 | Finder->addMatcher( |
31 | | - functionDecl(isDefinition(), returns(voidType()), |
32 | | - hasBody(compoundStmt(hasAnySubstatement( |
33 | | - returnStmt(unless(has(expr()))))) |
34 | | - .bind("return"))), |
35 | | - this); |
36 | | - Finder->addMatcher( |
37 | | - mapAnyOf(forStmt, cxxForRangeStmt, whileStmt, doStmt) |
38 | | - .with(hasBody(compoundStmt(hasAnySubstatement(continueStmt())) |
39 | | - .bind("continue"))), |
| 36 | + functionDecl(returns(voidType()), |
| 37 | + hasBody(compoundStmt(hasFinalStmt( |
| 38 | + returnStmt(unless(has(expr()))).bind("stmt"))))), |
40 | 39 | this); |
| 40 | + Finder->addMatcher(mapAnyOf(forStmt, cxxForRangeStmt, whileStmt, doStmt) |
| 41 | + .with(hasBody(compoundStmt( |
| 42 | + hasFinalStmt(continueStmt().bind("stmt"))))), |
| 43 | + this); |
41 | 44 | } |
42 | 45 |
|
43 | 46 | void RedundantControlFlowCheck::check(const MatchFinder::MatchResult &Result) { |
44 | | - if (const auto *Return = Result.Nodes.getNodeAs<CompoundStmt>("return")) |
45 | | - checkRedundantReturn(Result, Return); |
46 | | - else if (const auto *Continue = |
47 | | - Result.Nodes.getNodeAs<CompoundStmt>("continue")) |
48 | | - checkRedundantContinue(Result, Continue); |
49 | | -} |
50 | | - |
51 | | -void RedundantControlFlowCheck::checkRedundantReturn( |
52 | | - const MatchFinder::MatchResult &Result, const CompoundStmt *Block) { |
53 | | - const CompoundStmt::const_reverse_body_iterator Last = Block->body_rbegin(); |
54 | | - if (const auto *Return = dyn_cast<ReturnStmt>(*Last)) |
55 | | - issueDiagnostic(Result, Block, Return->getSourceRange(), |
56 | | - RedundantReturnDiag); |
57 | | -} |
58 | | - |
59 | | -void RedundantControlFlowCheck::checkRedundantContinue( |
60 | | - const MatchFinder::MatchResult &Result, const CompoundStmt *Block) { |
61 | | - const CompoundStmt::const_reverse_body_iterator Last = Block->body_rbegin(); |
62 | | - if (const auto *Continue = dyn_cast<ContinueStmt>(*Last)) |
63 | | - issueDiagnostic(Result, Block, Continue->getSourceRange(), |
64 | | - RedundantContinueDiag); |
65 | | -} |
| 47 | + const auto &RedundantStmt = *Result.Nodes.getNodeAs<Stmt>("stmt"); |
| 48 | + const SourceRange StmtRange = RedundantStmt.getSourceRange(); |
66 | 49 |
|
67 | | -void RedundantControlFlowCheck::issueDiagnostic( |
68 | | - const MatchFinder::MatchResult &Result, const CompoundStmt *const Block, |
69 | | - const SourceRange &StmtRange, const char *const Diag) { |
70 | | - const SourceManager &SM = *Result.SourceManager; |
71 | | - if (isLocationInMacroExpansion(SM, StmtRange.getBegin())) |
| 50 | + if (StmtRange.getBegin().isMacroID()) |
72 | 51 | return; |
73 | 52 |
|
74 | 53 | const auto RemovedRange = CharSourceRange::getCharRange( |
75 | 54 | StmtRange.getBegin(), |
76 | | - Lexer::findLocationAfterToken(StmtRange.getEnd(), tok::semi, SM, |
77 | | - getLangOpts(), |
| 55 | + Lexer::findLocationAfterToken(StmtRange.getEnd(), tok::semi, |
| 56 | + *Result.SourceManager, getLangOpts(), |
78 | 57 | /*SkipTrailingWhitespaceAndNewLine=*/true)); |
79 | 58 |
|
80 | | - diag(StmtRange.getBegin(), Diag) << FixItHint::CreateRemoval(RemovedRange); |
| 59 | + diag(StmtRange.getBegin(), isa<ReturnStmt>(RedundantStmt) |
| 60 | + ? RedundantReturnDiag |
| 61 | + : RedundantContinueDiag) |
| 62 | + << FixItHint::CreateRemoval(RemovedRange); |
81 | 63 | } |
82 | 64 |
|
83 | 65 | } // namespace clang::tidy::readability |
0 commit comments