From 29e7a6a80aa1d4e24a6a59af5b033712fe912f3e Mon Sep 17 00:00:00 2001 From: Davide Cunial Date: Wed, 8 Oct 2025 20:24:17 +0200 Subject: [PATCH] [clang-tidy] Add new check 'bugprone-inconsistent-ifelse-braces' --- .../bugprone/BugproneTidyModule.cpp | 3 + .../clang-tidy/bugprone/CMakeLists.txt | 1 + .../InconsistentIfelseBracesCheck.cpp | 63 +++++++++++++++++++ .../bugprone/InconsistentIfelseBracesCheck.h | 34 ++++++++++ clang-tools-extra/docs/ReleaseNotes.rst | 5 ++ .../bugprone/inconsistent-ifelse-braces.rst | 6 ++ .../docs/clang-tidy/checks/list.rst | 3 +- .../bugprone/inconsistent-ifelse-braces.cpp | 52 +++++++++++++++ 8 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 clang-tools-extra/clang-tidy/bugprone/InconsistentIfelseBracesCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/bugprone/InconsistentIfelseBracesCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/bugprone/inconsistent-ifelse-braces.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/inconsistent-ifelse-braces.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp index fe261e729539c..e1e42d22c520e 100644 --- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -33,6 +33,7 @@ #include "ImplicitWideningOfMultiplicationResultCheck.h" #include "InaccurateEraseCheck.h" #include "IncDecInConditionsCheck.h" +#include "InconsistentIfelseBracesCheck.h" #include "IncorrectEnableIfCheck.h" #include "IncorrectEnableSharedFromThisCheck.h" #include "IncorrectRoundingsCheck.h" @@ -150,6 +151,8 @@ class BugproneModule : public ClangTidyModule { "bugprone-implicit-widening-of-multiplication-result"); CheckFactories.registerCheck( "bugprone-inaccurate-erase"); + CheckFactories.registerCheck( + "bugprone-inconsistent-ifelse-braces"); CheckFactories.registerCheck( "bugprone-incorrect-enable-if"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt index 46bc8efd44bc5..d19fd5017d2e0 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt @@ -28,6 +28,7 @@ add_clang_library(clangTidyBugproneModule STATIC ForwardingReferenceOverloadCheck.cpp ImplicitWideningOfMultiplicationResultCheck.cpp InaccurateEraseCheck.cpp + InconsistentIfelseBracesCheck.cpp IncorrectEnableIfCheck.cpp IncorrectEnableSharedFromThisCheck.cpp InvalidEnumDefaultInitializationCheck.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/InconsistentIfelseBracesCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/InconsistentIfelseBracesCheck.cpp new file mode 100644 index 0000000000000..b695467977c4f --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/InconsistentIfelseBracesCheck.cpp @@ -0,0 +1,63 @@ + +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "InconsistentIfelseBracesCheck.h" +#include "clang/AST/ASTTypeTraits.h" +#include "clang/AST/Stmt.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::bugprone { + +/// Check that at least one branch of the \p If statement is a \c CompoundStmt. +static bool shouldHaveBraces(const IfStmt *If) { + const Stmt *const Then = If->getThen(); + if (isa(Then)) + return true; + + const Stmt *const Else = If->getElse(); + if (const auto *NestedIf = dyn_cast(Else)) + return shouldHaveBraces(NestedIf); + + return isa(Else); +} + +/// Check that all branchs of the \p If statement is are \c CompoundStmt. +static bool doesHaveBraces(const IfStmt *If) { + const Stmt *const Then = If->getThen(); + if (!isa(Then)) + return false; + + const Stmt *const Else = If->getElse(); + if (const auto *NestedIf = dyn_cast(Else)) + return doesHaveBraces(NestedIf); + + return isa(Else); +} + +void InconsistentIfelseBracesCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + traverse(TK_IgnoreUnlessSpelledInSource, + ifStmt(hasElse(anything()), unless(hasParent(ifStmt()))) + .bind("if_stmt")), + this); +} + +void InconsistentIfelseBracesCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *MatchedIf = Result.Nodes.getNodeAs("if_stmt"); + + if (shouldHaveBraces(MatchedIf) && !doesHaveBraces(MatchedIf)) { + diag(MatchedIf->getBeginLoc(), "bad!") << MatchedIf->getSourceRange(); + } +} + +} // namespace clang::tidy::bugprone diff --git a/clang-tools-extra/clang-tidy/bugprone/InconsistentIfelseBracesCheck.h b/clang-tools-extra/clang-tidy/bugprone/InconsistentIfelseBracesCheck.h new file mode 100644 index 0000000000000..9f0848cff8a43 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/InconsistentIfelseBracesCheck.h @@ -0,0 +1,34 @@ + +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_INCONSISTENTIFELSEBRACESCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_INCONSISTENTIFELSEBRACESCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::bugprone { + +/// FIXME: Write a short description. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/inconsistent-ifelse-braces.html +class InconsistentIfelseBracesCheck : public ClangTidyCheck { +public: + InconsistentIfelseBracesCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } +}; + +} // namespace clang::tidy::bugprone + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_INCONSISTENTIFELSEBRACESCHECK_H diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 7cdff86beeec6..6ed9b93e8c226 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -157,6 +157,11 @@ Improvements to clang-tidy New checks ^^^^^^^^^^ +- New :doc:`bugprone-inconsistent-ifelse-braces + ` check. + + FIXME: Write a short description. + - New :doc:`bugprone-invalid-enum-default-initialization ` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/inconsistent-ifelse-braces.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/inconsistent-ifelse-braces.rst new file mode 100644 index 0000000000000..9592f39d6a13d --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/inconsistent-ifelse-braces.rst @@ -0,0 +1,6 @@ +.. title:: clang-tidy - bugprone-inconsistent-ifelse-braces + +bugprone-inconsistent-ifelse-braces +=================================== + +FIXME: Describe what patterns does the check detect and why. Give examples. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index c490d2ece2e0a..9438030c64828 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -101,6 +101,7 @@ Clang-Tidy Checks :doc:`bugprone-implicit-widening-of-multiplication-result `, "Yes" :doc:`bugprone-inaccurate-erase `, "Yes" :doc:`bugprone-inc-dec-in-conditions `, + :doc:`bugprone-inconsistent-ifelse-braces `, :doc:`bugprone-incorrect-enable-if `, "Yes" :doc:`bugprone-incorrect-enable-shared-from-this `, "Yes" :doc:`bugprone-incorrect-roundings `, @@ -256,7 +257,7 @@ Clang-Tidy Checks :doc:`llvm-prefer-static-over-anonymous-namespace `, :doc:`llvm-twine-local `, "Yes" :doc:`llvm-use-new-mlir-op-builder `, "Yes" - :doc:`llvm-use-ranges `, "Yes" + :doc:`llvm-use-ranges `, :doc:`llvmlibc-callee-namespace `, :doc:`llvmlibc-implementation-in-namespace `, :doc:`llvmlibc-inline-function-decl `, "Yes" diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/inconsistent-ifelse-braces.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/inconsistent-ifelse-braces.cpp new file mode 100644 index 0000000000000..2a281193814dc --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/inconsistent-ifelse-braces.cpp @@ -0,0 +1,52 @@ +// RUN: %check_clang_tidy %s bugprone-inconsistent-ifelse-braces %t + +void foo(bool flag) { + int x, y; + + if (flag) + x = 0; + else { + y = 1; + } + + if (flag) { + x = 2; + } else { + y = 3; + } + + if (flag) { + x = 4; + } else if (flag) { + x = 5; + } else { + y = 6; + } + + if (flag) + x = 7; + else if (flag) + y = 8; + else + y = 9; + + if (flag) + x = 10; + else if (flag) { + y = 11; + } else + y = 12; +} + +// FIXME: Add something that triggers the check here. +void f(); +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'f' is insufficiently awesome [bugprone-inconsistent-ifelse-braces] + +// FIXME: Verify the applied fix. +// * Make the CHECK patterns specific enough and try to make verified lines +// unique to avoid incorrect matches. +// * Use {{}} for regular expressions. +// CHECK-FIXES: {{^}}void awesome_f();{{$}} + +// FIXME: Add something that doesn't trigger the check here. +void awesome_f2();