blob: 4a8388c61ee074d0331ce4911a0cda523ccbb57a [file] [log] [blame]
Samuel Benzaquen4fa2d572016-04-14 21:15:57 +00001//===--- MultipleStatementMacroCheck.cpp - clang-tidy----------------------===//
2//
Chandler Carruth2946cd72019-01-19 08:50:56 +00003// 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
Samuel Benzaquen4fa2d572016-04-14 21:15:57 +00006//
7//===----------------------------------------------------------------------===//
8
9#include "MultipleStatementMacroCheck.h"
10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
12
13using namespace clang::ast_matchers;
14
15namespace clang {
16namespace tidy {
Alexander Kornienkod4ac4af2017-11-24 14:16:29 +000017namespace bugprone {
Samuel Benzaquen4fa2d572016-04-14 21:15:57 +000018
19namespace {
20
Stephen Kelly43465bf2018-08-09 22:42:26 +000021AST_MATCHER(Expr, isInMacro) { return Node.getBeginLoc().isMacroID(); }
Samuel Benzaquen4fa2d572016-04-14 21:15:57 +000022
Dmitri Gribenko282dc722019-08-22 11:32:57 +000023/// Find the next statement after `S`.
Samuel Benzaquen4fa2d572016-04-14 21:15:57 +000024const Stmt *nextStmt(const MatchFinder::MatchResult &Result, const Stmt *S) {
25 auto Parents = Result.Context->getParents(*S);
26 if (Parents.empty())
27 return nullptr;
Piotr Padlewski08124b12016-12-14 15:29:23 +000028 const auto *Parent = Parents[0].get<Stmt>();
Samuel Benzaquen4fa2d572016-04-14 21:15:57 +000029 if (!Parent)
30 return nullptr;
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +000031 const Stmt *Prev = nullptr;
Samuel Benzaquen4fa2d572016-04-14 21:15:57 +000032 for (const Stmt *Child : Parent->children()) {
33 if (Prev == S)
34 return Child;
35 Prev = Child;
36 }
37 return nextStmt(Result, Parent);
38}
39
Richard Smith4bb15ab2018-04-30 05:26:07 +000040using ExpansionRanges = std::vector<SourceRange>;
Samuel Benzaquen4fa2d572016-04-14 21:15:57 +000041
42/// \bried Get all the macro expansion ranges related to `Loc`.
43///
44/// The result is ordered from most inner to most outer.
45ExpansionRanges getExpansionRanges(SourceLocation Loc,
46 const MatchFinder::MatchResult &Result) {
47 ExpansionRanges Locs;
48 while (Loc.isMacroID()) {
Richard Smith4bb15ab2018-04-30 05:26:07 +000049 Locs.push_back(
50 Result.SourceManager->getImmediateExpansionRange(Loc).getAsRange());
51 Loc = Locs.back().getBegin();
Samuel Benzaquen4fa2d572016-04-14 21:15:57 +000052 }
53 return Locs;
54}
55
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +000056} // namespace
Samuel Benzaquen4fa2d572016-04-14 21:15:57 +000057
58void 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
67void 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
Stephen Kelly43465bf2018-08-09 22:42:26 +000075 SourceLocation OuterLoc = Outer->getBeginLoc();
Samuel Benzaquen4fa2d572016-04-14 21:15:57 +000076 if (Result.Nodes.getNodeAs<Stmt>("else"))
77 OuterLoc = cast<IfStmt>(Outer)->getElseLoc();
78
Stephen Kelly43465bf2018-08-09 22:42:26 +000079 auto InnerRanges = getExpansionRanges(Inner->getBeginLoc(), Result);
Samuel Benzaquen4fa2d572016-04-14 21:15:57 +000080 auto OuterRanges = getExpansionRanges(OuterLoc, Result);
Stephen Kelly43465bf2018-08-09 22:42:26 +000081 auto NextRanges = getExpansionRanges(Next->getBeginLoc(), Result);
Samuel Benzaquen4fa2d572016-04-14 21:15:57 +000082
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
Richard Smith4bb15ab2018-04-30 05:26:07 +000099 diag(InnerRanges.back().getBegin(), "multiple statement macro used without "
100 "braces; some statements will be "
101 "unconditionally executed");
Samuel Benzaquen4fa2d572016-04-14 21:15:57 +0000102}
103
Alexander Kornienkod4ac4af2017-11-24 14:16:29 +0000104} // namespace bugprone
Samuel Benzaquen4fa2d572016-04-14 21:15:57 +0000105} // namespace tidy
106} // namespace clang