1 //===--- MultipleStatementMacroCheck.cpp - clang-tidy----------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "MultipleStatementMacroCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 using namespace clang::ast_matchers
;
15 namespace clang::tidy::bugprone
{
19 AST_MATCHER(Expr
, isInMacro
) { return Node
.getBeginLoc().isMacroID(); }
21 /// Find the next statement after `S`.
22 const Stmt
*nextStmt(const MatchFinder::MatchResult
&Result
, const Stmt
*S
) {
23 auto Parents
= Result
.Context
->getParents(*S
);
26 const auto *Parent
= Parents
[0].get
<Stmt
>();
29 const Stmt
*Prev
= nullptr;
30 for (const Stmt
*Child
: Parent
->children()) {
35 return nextStmt(Result
, Parent
);
38 using ExpansionRanges
= std::vector
<SourceRange
>;
40 /// \brief Get all the macro expansion ranges related to `Loc`.
42 /// The result is ordered from most inner to most outer.
43 ExpansionRanges
getExpansionRanges(SourceLocation Loc
,
44 const MatchFinder::MatchResult
&Result
) {
46 while (Loc
.isMacroID()) {
48 Result
.SourceManager
->getImmediateExpansionRange(Loc
).getAsRange());
49 Loc
= Locs
.back().getBegin();
56 void MultipleStatementMacroCheck::registerMatchers(MatchFinder
*Finder
) {
57 const auto Inner
= expr(isInMacro(), unless(compoundStmt())).bind("inner");
59 stmt(anyOf(ifStmt(hasThen(Inner
)), ifStmt(hasElse(Inner
)).bind("else"),
60 whileStmt(hasBody(Inner
)), forStmt(hasBody(Inner
))))
65 void MultipleStatementMacroCheck::check(
66 const MatchFinder::MatchResult
&Result
) {
67 const auto *Inner
= Result
.Nodes
.getNodeAs
<Expr
>("inner");
68 const auto *Outer
= Result
.Nodes
.getNodeAs
<Stmt
>("outer");
69 const auto *Next
= nextStmt(Result
, Outer
);
73 SourceLocation OuterLoc
= Outer
->getBeginLoc();
74 if (Result
.Nodes
.getNodeAs
<Stmt
>("else"))
75 OuterLoc
= cast
<IfStmt
>(Outer
)->getElseLoc();
77 auto InnerRanges
= getExpansionRanges(Inner
->getBeginLoc(), Result
);
78 auto OuterRanges
= getExpansionRanges(OuterLoc
, Result
);
79 auto NextRanges
= getExpansionRanges(Next
->getBeginLoc(), Result
);
81 // Remove all the common ranges, starting from the top (the last ones in the
83 while (!InnerRanges
.empty() && !OuterRanges
.empty() && !NextRanges
.empty() &&
84 InnerRanges
.back() == OuterRanges
.back() &&
85 InnerRanges
.back() == NextRanges
.back()) {
86 InnerRanges
.pop_back();
87 OuterRanges
.pop_back();
88 NextRanges
.pop_back();
91 // Inner and Next must have at least one more macro that Outer doesn't have,
92 // and that range must be common to both.
93 if (InnerRanges
.empty() || NextRanges
.empty() ||
94 InnerRanges
.back() != NextRanges
.back())
97 diag(InnerRanges
.back().getBegin(), "multiple statement macro used without "
98 "braces; some statements will be "
99 "unconditionally executed");
102 } // namespace clang::tidy::bugprone