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