1 | //===--- RedundantControlFlowCheck.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 "RedundantControlFlowCheck.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 | const char *const RedundantReturnDiag = "redundant return statement at the end " |
21 | "of a function with a void return type" ; |
22 | const char *const RedundantContinueDiag = "redundant continue statement at the " |
23 | "end of loop statement" ; |
24 | |
25 | bool isLocationInMacroExpansion(const SourceManager &SM, SourceLocation Loc) { |
26 | return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc); |
27 | } |
28 | |
29 | } // namespace |
30 | |
31 | void RedundantControlFlowCheck::registerMatchers(MatchFinder *Finder) { |
32 | Finder->addMatcher( |
33 | NodeMatch: functionDecl(isDefinition(), returns(InnerMatcher: voidType()), |
34 | hasBody(InnerMatcher: compoundStmt(hasAnySubstatement( |
35 | InnerMatcher: returnStmt(unless(has(expr()))))) |
36 | .bind(ID: "return" ))), |
37 | Action: this); |
38 | Finder->addMatcher( |
39 | NodeMatch: mapAnyOf(forStmt, cxxForRangeStmt, whileStmt, doStmt) |
40 | .with(hasBody(InnerMatcher: compoundStmt(hasAnySubstatement(InnerMatcher: continueStmt())) |
41 | .bind(ID: "continue" ))), |
42 | Action: this); |
43 | } |
44 | |
45 | void RedundantControlFlowCheck::check(const MatchFinder::MatchResult &Result) { |
46 | if (const auto *Return = Result.Nodes.getNodeAs<CompoundStmt>(ID: "return" )) |
47 | checkRedundantReturn(Result, Block: Return); |
48 | else if (const auto *Continue = |
49 | Result.Nodes.getNodeAs<CompoundStmt>(ID: "continue" )) |
50 | checkRedundantContinue(Result, Block: Continue); |
51 | } |
52 | |
53 | void RedundantControlFlowCheck::checkRedundantReturn( |
54 | const MatchFinder::MatchResult &Result, const CompoundStmt *Block) { |
55 | CompoundStmt::const_reverse_body_iterator Last = Block->body_rbegin(); |
56 | if (const auto *Return = dyn_cast<ReturnStmt>(Val: *Last)) |
57 | issueDiagnostic(Result, Block, StmtRange: Return->getSourceRange(), |
58 | Diag: RedundantReturnDiag); |
59 | } |
60 | |
61 | void RedundantControlFlowCheck::checkRedundantContinue( |
62 | const MatchFinder::MatchResult &Result, const CompoundStmt *Block) { |
63 | CompoundStmt::const_reverse_body_iterator Last = Block->body_rbegin(); |
64 | if (const auto *Continue = dyn_cast<ContinueStmt>(Val: *Last)) |
65 | issueDiagnostic(Result, Block, StmtRange: Continue->getSourceRange(), |
66 | Diag: RedundantContinueDiag); |
67 | } |
68 | |
69 | void RedundantControlFlowCheck::issueDiagnostic( |
70 | const MatchFinder::MatchResult &Result, const CompoundStmt *const Block, |
71 | const SourceRange &StmtRange, const char *const Diag) { |
72 | SourceManager &SM = *Result.SourceManager; |
73 | if (isLocationInMacroExpansion(SM, Loc: StmtRange.getBegin())) |
74 | return; |
75 | |
76 | CompoundStmt::const_reverse_body_iterator Previous = ++Block->body_rbegin(); |
77 | SourceLocation Start; |
78 | if (Previous != Block->body_rend()) |
79 | Start = Lexer::findLocationAfterToken( |
80 | loc: cast<Stmt>(Val: *Previous)->getEndLoc(), TKind: tok::semi, SM, LangOpts: getLangOpts(), |
81 | /*SkipTrailingWhitespaceAndNewLine=*/true); |
82 | if (!Start.isValid()) |
83 | Start = StmtRange.getBegin(); |
84 | auto RemovedRange = CharSourceRange::getCharRange( |
85 | B: Start, E: Lexer::findLocationAfterToken( |
86 | loc: StmtRange.getEnd(), TKind: tok::semi, SM, LangOpts: getLangOpts(), |
87 | /*SkipTrailingWhitespaceAndNewLine=*/true)); |
88 | |
89 | diag(Loc: StmtRange.getBegin(), Description: Diag) << FixItHint::CreateRemoval(RemoveRange: RemovedRange); |
90 | } |
91 | |
92 | } // namespace clang::tidy::readability |
93 | |