92 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "RedundantControlFlowCheck.h"10#include "clang/ASTMatchers/ASTMatchFinder.h"11#include "clang/Lex/Lexer.h"12 13using namespace clang::ast_matchers;14 15namespace clang::tidy::readability {16 17static const char *const RedundantReturnDiag =18 "redundant return statement at the end "19 "of a function with a void return type";20static const char *const RedundantContinueDiag =21 "redundant continue statement at the "22 "end of loop statement";23 24static bool isLocationInMacroExpansion(const SourceManager &SM,25 SourceLocation Loc) {26 return SM.isMacroBodyExpansion(Loc) || SM.isMacroArgExpansion(Loc);27}28 29void RedundantControlFlowCheck::registerMatchers(MatchFinder *Finder) {30 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"))),40 this);41}42 43void 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 51void 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 59void 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}66 67void 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()))72 return;73 74 const CompoundStmt::const_reverse_body_iterator Previous =75 ++Block->body_rbegin();76 SourceLocation Start;77 if (Previous != Block->body_rend())78 Start = Lexer::findLocationAfterToken(79 cast<Stmt>(*Previous)->getEndLoc(), tok::semi, SM, getLangOpts(),80 /*SkipTrailingWhitespaceAndNewLine=*/true);81 if (!Start.isValid())82 Start = StmtRange.getBegin();83 auto RemovedRange = CharSourceRange::getCharRange(84 Start, Lexer::findLocationAfterToken(85 StmtRange.getEnd(), tok::semi, SM, getLangOpts(),86 /*SkipTrailingWhitespaceAndNewLine=*/true));87 88 diag(StmtRange.getBegin(), Diag) << FixItHint::CreateRemoval(RemovedRange);89}90 91} // namespace clang::tidy::readability92