Skip to content

Commit a0a7126

Browse files
[clang-tidy] Add new check 'bugprone-inconsistent-ifelse-braces'
1 parent 48babe1 commit a0a7126

File tree

9 files changed

+289
-1
lines changed

9 files changed

+289
-1
lines changed

clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "ImplicitWideningOfMultiplicationResultCheck.h"
3434
#include "InaccurateEraseCheck.h"
3535
#include "IncDecInConditionsCheck.h"
36+
#include "InconsistentIfelseBracesCheck.h"
3637
#include "IncorrectEnableIfCheck.h"
3738
#include "IncorrectEnableSharedFromThisCheck.h"
3839
#include "IncorrectRoundingsCheck.h"
@@ -150,6 +151,8 @@ class BugproneModule : public ClangTidyModule {
150151
"bugprone-implicit-widening-of-multiplication-result");
151152
CheckFactories.registerCheck<InaccurateEraseCheck>(
152153
"bugprone-inaccurate-erase");
154+
CheckFactories.registerCheck<InconsistentIfelseBracesCheck>(
155+
"bugprone-inconsistent-ifelse-braces");
153156
CheckFactories.registerCheck<IncorrectEnableIfCheck>(
154157
"bugprone-incorrect-enable-if");
155158
CheckFactories.registerCheck<IncorrectEnableSharedFromThisCheck>(

clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ add_clang_library(clangTidyBugproneModule STATIC
2828
ForwardingReferenceOverloadCheck.cpp
2929
ImplicitWideningOfMultiplicationResultCheck.cpp
3030
InaccurateEraseCheck.cpp
31+
InconsistentIfelseBracesCheck.cpp
3132
IncorrectEnableIfCheck.cpp
3233
IncorrectEnableSharedFromThisCheck.cpp
3334
InvalidEnumDefaultInitializationCheck.cpp
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#include "InconsistentIfelseBracesCheck.h"
11+
#include "../utils/BracesAroundStatement.h"
12+
#include "clang/AST/ASTContext.h"
13+
#include "clang/ASTMatchers/ASTMatchers.h"
14+
15+
using namespace clang::ast_matchers;
16+
17+
namespace clang::tidy::bugprone {
18+
19+
/// Check that at least one branch of the \p If statement is a \c CompoundStmt.
20+
static bool shouldHaveBraces(const IfStmt *If) {
21+
const Stmt *const Then = If->getThen();
22+
if (isa<CompoundStmt>(Then))
23+
return true;
24+
25+
if (const Stmt *const Else = If->getElse()) {
26+
if (const auto *NestedIf = dyn_cast<const IfStmt>(Else))
27+
return shouldHaveBraces(NestedIf);
28+
29+
return isa<CompoundStmt>(Else);
30+
}
31+
32+
return false;
33+
}
34+
35+
void InconsistentIfelseBracesCheck::registerMatchers(MatchFinder *Finder) {
36+
Finder->addMatcher(
37+
traverse(TK_IgnoreUnlessSpelledInSource,
38+
ifStmt(hasElse(anything()),
39+
unless(isConsteval()), // 'if consteval' always has braces
40+
unless(hasParent(ifStmt())))
41+
.bind("if_stmt")),
42+
this);
43+
}
44+
45+
void InconsistentIfelseBracesCheck::check(
46+
const MatchFinder::MatchResult &Result) {
47+
const auto *MatchedIf = Result.Nodes.getNodeAs<IfStmt>("if_stmt");
48+
if (!shouldHaveBraces(MatchedIf))
49+
return;
50+
51+
// TODO: Test behavior with macros. This might be the reason why
52+
// readability-braces-around-statements defines a findRParenLoc() function
53+
// rather than using IfStmt/WhileStmt::getRParenLoc().
54+
checkIfStmt(Result, MatchedIf);
55+
}
56+
57+
void InconsistentIfelseBracesCheck::checkIfStmt(
58+
const ast_matchers::MatchFinder::MatchResult &Result, const IfStmt *If) {
59+
const Stmt *Then = If->getThen();
60+
if (const auto *NestedIf = dyn_cast<const IfStmt>(Then)) {
61+
// If the then-branch is a nested IfStmt, first we need to add braces to
62+
// it, then we need to check the inner IfStmt.
63+
checkStmt(Result, If->getThen(), If->getRParenLoc(), If->getElseLoc());
64+
if (shouldHaveBraces(NestedIf))
65+
return checkIfStmt(Result, NestedIf);
66+
}
67+
68+
if (!isa<CompoundStmt>(Then))
69+
checkStmt(Result, If->getThen(), If->getRParenLoc(), If->getElseLoc());
70+
71+
if (const Stmt *const Else = If->getElse()) {
72+
if (const auto *NestedIf = dyn_cast<const IfStmt>(Else))
73+
return checkIfStmt(Result, NestedIf);
74+
75+
if (!isa<CompoundStmt>(Else))
76+
checkStmt(Result, If->getElse(), If->getElseLoc());
77+
}
78+
}
79+
80+
void InconsistentIfelseBracesCheck::checkStmt(
81+
const ast_matchers::MatchFinder::MatchResult &Result, const Stmt *S,
82+
SourceLocation StartLoc, SourceLocation EndLocHint) {
83+
const SourceManager &SM = *Result.SourceManager;
84+
const LangOptions &LangOpts = Result.Context->getLangOpts();
85+
86+
const utils::BraceInsertionHints Hints =
87+
utils::getBraceInsertionsHints(S, LangOpts, SM, StartLoc, EndLocHint);
88+
if (Hints) {
89+
DiagnosticBuilder Diag = diag(Hints.DiagnosticPos, "<message>");
90+
if (Hints.offersFixIts()) {
91+
Diag << Hints.openingBraceFixIt() << Hints.closingBraceFixIt();
92+
}
93+
}
94+
}
95+
} // namespace clang::tidy::bugprone
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_INCONSISTENTIFELSEBRACESCHECK_H
11+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_INCONSISTENTIFELSEBRACESCHECK_H
12+
13+
#include "../ClangTidyCheck.h"
14+
15+
namespace clang::tidy::bugprone {
16+
17+
/// FIXME: Write a short description.
18+
///
19+
/// For the user-facing documentation see:
20+
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/inconsistent-ifelse-braces.html
21+
class InconsistentIfelseBracesCheck : public ClangTidyCheck {
22+
public:
23+
InconsistentIfelseBracesCheck(StringRef Name, ClangTidyContext *Context)
24+
: ClangTidyCheck(Name, Context) {}
25+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
26+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
27+
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
28+
return LangOpts.CPlusPlus;
29+
}
30+
31+
private:
32+
void checkIfStmt(const ast_matchers::MatchFinder::MatchResult &Result,
33+
const IfStmt *If);
34+
void checkStmt(const ast_matchers::MatchFinder::MatchResult &Result,
35+
const Stmt *S, SourceLocation StartLoc,
36+
SourceLocation EndLocHint = SourceLocation());
37+
};
38+
39+
} // namespace clang::tidy::bugprone
40+
41+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_INCONSISTENTIFELSEBRACESCHECK_H

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,11 @@ Improvements to clang-tidy
157157
New checks
158158
^^^^^^^^^^
159159

160+
- New :doc:`bugprone-inconsistent-ifelse-braces
161+
<clang-tidy/checks/bugprone/inconsistent-ifelse-braces>` check.
162+
163+
FIXME: Write a short description.
164+
160165
- New :doc:`bugprone-invalid-enum-default-initialization
161166
<clang-tidy/checks/bugprone/invalid-enum-default-initialization>` check.
162167

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.. title:: clang-tidy - bugprone-inconsistent-ifelse-braces
2+
3+
bugprone-inconsistent-ifelse-braces
4+
===================================
5+
6+
FIXME: Describe what patterns does the check detect and why. Give examples.

clang-tools-extra/docs/clang-tidy/checks/list.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ Clang-Tidy Checks
101101
:doc:`bugprone-implicit-widening-of-multiplication-result <bugprone/implicit-widening-of-multiplication-result>`, "Yes"
102102
:doc:`bugprone-inaccurate-erase <bugprone/inaccurate-erase>`, "Yes"
103103
:doc:`bugprone-inc-dec-in-conditions <bugprone/inc-dec-in-conditions>`,
104+
:doc:`bugprone-inconsistent-ifelse-braces <bugprone/inconsistent-ifelse-braces>`,
104105
:doc:`bugprone-incorrect-enable-if <bugprone/incorrect-enable-if>`, "Yes"
105106
:doc:`bugprone-incorrect-enable-shared-from-this <bugprone/incorrect-enable-shared-from-this>`, "Yes"
106107
:doc:`bugprone-incorrect-roundings <bugprone/incorrect-roundings>`,
@@ -256,7 +257,7 @@ Clang-Tidy Checks
256257
:doc:`llvm-prefer-static-over-anonymous-namespace <llvm/prefer-static-over-anonymous-namespace>`,
257258
:doc:`llvm-twine-local <llvm/twine-local>`, "Yes"
258259
:doc:`llvm-use-new-mlir-op-builder <llvm/use-new-mlir-op-builder>`, "Yes"
259-
:doc:`llvm-use-ranges <llvm/use-ranges>`, "Yes"
260+
:doc:`llvm-use-ranges <llvm/use-ranges>`,
260261
:doc:`llvmlibc-callee-namespace <llvmlibc/callee-namespace>`,
261262
:doc:`llvmlibc-implementation-in-namespace <llvmlibc/implementation-in-namespace>`,
262263
:doc:`llvmlibc-inline-function-decl <llvmlibc/inline-function-decl>`, "Yes"
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// RUN: %check_clang_tidy -std=c++23-or-later %s bugprone-inconsistent-ifelse-braces %t
2+
3+
bool cond(const char *) { return false; }
4+
void do_something(const char *) {}
5+
6+
// Positive tests.
7+
void f() {
8+
if consteval {
9+
if (cond("if1"))
10+
do_something("if-consteval-single-line");
11+
else {
12+
}
13+
// CHECK-MESSAGES: :[[@LINE-4]]:21: warning: <message> [bugprone-inconsistent-ifelse-braces]
14+
// CHECK-FIXES: if (cond("if1")) {
15+
// CHECK-FIXES: } else {
16+
} else {
17+
if (cond("if1.1")) {
18+
} else
19+
do_something("if-consteval-single-line");
20+
// CHECK-MESSAGES: :[[@LINE-2]]:11: warning: <message> [bugprone-inconsistent-ifelse-braces]
21+
// CHECK-FIXES: } else {
22+
// CHECK-FIXES: }
23+
}
24+
}
25+
26+
// Negative tests.
27+
void g() {
28+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// RUN: %check_clang_tidy %s bugprone-inconsistent-ifelse-braces %t
2+
3+
bool cond(const char *) { return false; }
4+
void do_something(const char *) {}
5+
6+
// Positive tests.
7+
void f() {
8+
if (cond("if0") /*comment*/) do_something("if-same-line");
9+
else {
10+
}
11+
// CHECK-MESSAGES: :[[@LINE-3]]:31: warning: <message> [bugprone-inconsistent-ifelse-braces]
12+
// CHECK-FIXES: if (cond("if0") /*comment*/) { do_something("if-same-line");
13+
// CHECK-FIXES: } else {
14+
15+
if (cond("if0.1") /*comment*/) {
16+
} else do_something("else-same-line");
17+
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: <message> [bugprone-inconsistent-ifelse-braces]
18+
// CHECK-FIXES: } else { do_something("else-same-line");
19+
// CHECK-FIXES: }
20+
21+
if (cond("if1"))
22+
do_something("if-single-line");
23+
else {
24+
}
25+
// CHECK-MESSAGES: :[[@LINE-4]]:19: warning: <message> [bugprone-inconsistent-ifelse-braces]
26+
// CHECK-FIXES: if (cond("if1")) {
27+
// CHECK-FIXES: } else {
28+
29+
if (cond("if1.1")) {
30+
} else
31+
do_something("else-single-line");
32+
// CHECK-MESSAGES: :[[@LINE-2]]:9: warning: <message> [bugprone-inconsistent-ifelse-braces]
33+
// CHECK-FIXES: } else {
34+
// CHECK-FIXES: }
35+
36+
if (cond("if2") /*comment*/)
37+
// some comment
38+
do_something("if-multi-line");
39+
else {
40+
}
41+
// CHECK-MESSAGES: :[[@LINE-5]]:31: warning: <message> [bugprone-inconsistent-ifelse-braces]
42+
// CHECK-FIXES: if (cond("if2") /*comment*/) {
43+
// CHECK-FIXES: } else {
44+
45+
if (cond("if2.1") /*comment*/) {
46+
} else
47+
// some comment
48+
do_something("else-multi-line");
49+
// CHECK-MESSAGES: :[[@LINE-3]]:9: warning: <message> [bugprone-inconsistent-ifelse-braces]
50+
// CHECK-FIXES: } else {
51+
// CHECK-FIXES: }
52+
53+
if (cond("if3")) do_something("elseif-same-line");
54+
else if (cond("if3")) {
55+
} else {
56+
}
57+
// CHECK-MESSAGES: :[[@LINE-4]]:19: warning: <message> [bugprone-inconsistent-ifelse-braces]
58+
// CHECK-FIXES: if (cond("if3")) { do_something("elseif-same-line");
59+
// CHECK-FIXES: } else if (cond("if3")) {
60+
61+
if (cond("if3.1")) {
62+
} else if (cond("if3.1")) do_something("elseif-same-line");
63+
else {
64+
}
65+
// CHECK-MESSAGES: :[[@LINE-3]]:28: warning: <message> [bugprone-inconsistent-ifelse-braces]
66+
// CHECK-FIXES: } else if (cond("if3.1")) { do_something("elseif-same-line");
67+
// CHECK-FIXES: } else {
68+
69+
if (cond("if3.2")) {
70+
} else if (cond("if3.2")) {
71+
} else do_something("elseif-same-line");
72+
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: <message> [bugprone-inconsistent-ifelse-braces]
73+
// CHECK-FIXES: } else { do_something("elseif-same-line");
74+
// CHECK-FIXES: }
75+
76+
if (cond("if4-outer"))
77+
if (cond("if4-inner"))
78+
do_something("nested-if-single-line");
79+
else {
80+
}
81+
else {
82+
}
83+
// CHECK-MESSAGES: :[[@LINE-7]]:25: warning: <message> [bugprone-inconsistent-ifelse-braces]
84+
// CHECK-MESSAGES: :[[@LINE-7]]:27: warning: <message> [bugprone-inconsistent-ifelse-braces]
85+
// CHECK-FIXES: if (cond("if4-outer")) {
86+
// CHECK-FIXES: if (cond("if4-inner")) {
87+
// CHECK-FIXES: } else {
88+
// CHECK-FIXES: } else {
89+
90+
if (cond("if5"))
91+
do_something("noelse-single-line");
92+
else if (cond("if5")) {
93+
}
94+
// CHECK-MESSAGES: :[[@LINE-4]]:19: warning: <message> [bugprone-inconsistent-ifelse-braces]
95+
// CHECK-FIXES: if (cond("if5")) {
96+
// CHECK-FIXES: } else if (cond("if5")) {
97+
98+
if (cond("if5.1")) {
99+
} else if (cond("if5.1"))
100+
do_something("noelse-single-line");
101+
// CHECK-MESSAGES: :[[@LINE-2]]:28: warning: <message> [bugprone-inconsistent-ifelse-braces]
102+
// CHECK-FIXES: } else if (cond("if5.1")) {
103+
// CHECK-FIXES: }
104+
}
105+
106+
// Negative tests.
107+
void g() {
108+
}

0 commit comments

Comments
 (0)