1 //===--- MultipleStatementMacroCheck.cpp - clang-tidy----------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "MultipleStatementMacroCheck.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 14 using namespace clang::ast_matchers; 15 16 namespace clang { 17 namespace tidy { 18 namespace bugprone { 19 20 namespace { 21 22 AST_MATCHER(Expr, isInMacro) { return Node.getLocStart().isMacroID(); } 23 24 /// \brief Find the next statement after `S`. 25 const Stmt *nextStmt(const MatchFinder::MatchResult &Result, const Stmt *S) { 26 auto Parents = Result.Context->getParents(*S); 27 if (Parents.empty()) 28 return nullptr; 29 const auto *Parent = Parents[0].get<Stmt>(); 30 if (!Parent) 31 return nullptr; 32 const Stmt *Prev = nullptr; 33 for (const Stmt *Child : Parent->children()) { 34 if (Prev == S) 35 return Child; 36 Prev = Child; 37 } 38 return nextStmt(Result, Parent); 39 } 40 41 using ExpansionRanges = std::vector<std::pair<SourceLocation, SourceLocation>>; 42 43 /// \bried Get all the macro expansion ranges related to `Loc`. 44 /// 45 /// The result is ordered from most inner to most outer. 46 ExpansionRanges getExpansionRanges(SourceLocation Loc, 47 const MatchFinder::MatchResult &Result) { 48 ExpansionRanges Locs; 49 while (Loc.isMacroID()) { 50 Locs.push_back(Result.SourceManager->getImmediateExpansionRange(Loc)); 51 Loc = Locs.back().first; 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->getLocStart(); 76 if (Result.Nodes.getNodeAs<Stmt>("else")) 77 OuterLoc = cast<IfStmt>(Outer)->getElseLoc(); 78 79 auto InnerRanges = getExpansionRanges(Inner->getLocStart(), Result); 80 auto OuterRanges = getExpansionRanges(OuterLoc, Result); 81 auto NextRanges = getExpansionRanges(Next->getLocStart(), 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().first, "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