1 //===--- AvoidGotoCheck.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 "AvoidGotoCheck.h" 10 #include "clang/AST/ASTContext.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 13 using namespace clang::ast_matchers; 14 15 namespace clang { 16 namespace tidy { 17 namespace cppcoreguidelines { 18 19 namespace { 20 AST_MATCHER(GotoStmt, isForwardJumping) { 21 return Node.getBeginLoc() < Node.getLabel()->getBeginLoc(); 22 } 23 } // namespace 24 25 void AvoidGotoCheck::registerMatchers(MatchFinder *Finder) { 26 if (!getLangOpts().CPlusPlus) 27 return; 28 29 // TODO: This check does not recognize `IndirectGotoStmt` which is a 30 // GNU extension. These must be matched separately and an AST matcher 31 // is currently missing for them. 32 33 // Check if the 'goto' is used for control flow other than jumping 34 // out of a nested loop. 35 auto Loop = stmt(anyOf(forStmt(), cxxForRangeStmt(), whileStmt(), doStmt())); 36 auto NestedLoop = 37 stmt(anyOf(forStmt(hasAncestor(Loop)), cxxForRangeStmt(hasAncestor(Loop)), 38 whileStmt(hasAncestor(Loop)), doStmt(hasAncestor(Loop)))); 39 40 Finder->addMatcher(gotoStmt(anyOf(unless(hasAncestor(NestedLoop)), 41 unless(isForwardJumping()))) 42 .bind("goto"), 43 this); 44 } 45 46 void AvoidGotoCheck::check(const MatchFinder::MatchResult &Result) { 47 const auto *Goto = Result.Nodes.getNodeAs<GotoStmt>("goto"); 48 49 diag(Goto->getGotoLoc(), "avoid using 'goto' for flow control") 50 << Goto->getSourceRange(); 51 diag(Goto->getLabel()->getBeginLoc(), "label defined here", 52 DiagnosticIDs::Note); 53 } 54 } // namespace cppcoreguidelines 55 } // namespace tidy 56 } // namespace clang 57