1 //===--- MultipleStatementMacroCheck.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 "MultipleStatementMacroCheck.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 bugprone { 18 19 namespace { 20 21 AST_MATCHER(Expr, isInMacro) { return Node.getBeginLoc().isMacroID(); } 22 23 /// Find the next statement after `S`. 24 const Stmt *nextStmt(const MatchFinder::MatchResult &Result, const Stmt *S) { 25 auto Parents = Result.Context->getParents(*S); 26 if (Parents.empty()) 27 return nullptr; 28 const auto *Parent = Parents[0].get<Stmt>(); 29 if (!Parent) 30 return nullptr; 31 const Stmt *Prev = nullptr; 32 for (const Stmt *Child : Parent->children()) { 33 if (Prev == S) 34 return Child; 35 Prev = Child; 36 } 37 return nextStmt(Result, Parent); 38 } 39 40 using ExpansionRanges = std::vector<SourceRange>; 41 42 /// \bried Get all the macro expansion ranges related to `Loc`. 43 /// 44 /// The result is ordered from most inner to most outer. 45 ExpansionRanges getExpansionRanges(SourceLocation Loc, 46 const MatchFinder::MatchResult &Result) { 47 ExpansionRanges Locs; 48 while (Loc.isMacroID()) { 49 Locs.push_back( 50 Result.SourceManager->getImmediateExpansionRange(Loc).getAsRange()); 51 Loc = Locs.back().getBegin(); 52 } 53 return Locs; 54 } 55 56 } // namespace 57 58 void MultipleStatementMacroCheck::registerMatchers(MatchFinder *Finder) { 59 const auto Inner = expr(isInMacro(), unless(compoundStmt())).bind("inner"); 60 Finder->addMatcher( 61 stmt(anyOf(ifStmt(hasThen(Inner)), ifStmt(hasElse(Inner)).bind("else"), 62 whileStmt(hasBody(Inner)), forStmt(hasBody(Inner)))) 63 .bind("outer"), 64 this); 65 } 66 67 void MultipleStatementMacroCheck::check( 68 const MatchFinder::MatchResult &Result) { 69 const auto *Inner = Result.Nodes.getNodeAs<Expr>("inner"); 70 const auto *Outer = Result.Nodes.getNodeAs<Stmt>("outer"); 71 const auto *Next = nextStmt(Result, Outer); 72 if (!Next) 73 return; 74 75 SourceLocation OuterLoc = Outer->getBeginLoc(); 76 if (Result.Nodes.getNodeAs<Stmt>("else")) 77 OuterLoc = cast<IfStmt>(Outer)->getElseLoc(); 78 79 auto InnerRanges = getExpansionRanges(Inner->getBeginLoc(), Result); 80 auto OuterRanges = getExpansionRanges(OuterLoc, Result); 81 auto NextRanges = getExpansionRanges(Next->getBeginLoc(), Result); 82 83 // Remove all the common ranges, starting from the top (the last ones in the 84 // list). 85 while (!InnerRanges.empty() && !OuterRanges.empty() && !NextRanges.empty() && 86 InnerRanges.back() == OuterRanges.back() && 87 InnerRanges.back() == NextRanges.back()) { 88 InnerRanges.pop_back(); 89 OuterRanges.pop_back(); 90 NextRanges.pop_back(); 91 } 92 93 // Inner and Next must have at least one more macro that Outer doesn't have, 94 // and that range must be common to both. 95 if (InnerRanges.empty() || NextRanges.empty() || 96 InnerRanges.back() != NextRanges.back()) 97 return; 98 99 diag(InnerRanges.back().getBegin(), "multiple statement macro used without " 100 "braces; some statements will be " 101 "unconditionally executed"); 102 } 103 104 } // namespace bugprone 105 } // namespace tidy 106 } // namespace clang 107