diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidDoWhileCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidDoWhileCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidDoWhileCheck.h @@ -0,0 +1,41 @@ +//===--- AvoidDoWhileCheck.h - clang-tidy -----------------------*- C++ -*-===// +// +// 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_CPPCOREGUIDELINES_AVOIDDOWHILECHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_AVOIDDOWHILECHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { + +/// do-while loops are less readable than plan while loops, and can lead to +/// subtle bugs. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/avoid-do-while.html +class AvoidDoWhileCheck : public ClangTidyCheck { +public: + AvoidDoWhileCheck(StringRef Name, ClangTidyContext *Context); + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus; + } + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + bool AllowWhileFalse; +}; + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_AVOIDDOWHILECHECK_H diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidDoWhileCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidDoWhileCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidDoWhileCheck.cpp @@ -0,0 +1,46 @@ +//===--- AvoidDoWhileCheck.cpp - clang-tidy -------------------------------===// +// +// 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 "AvoidDoWhileCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { + +AvoidDoWhileCheck::AvoidDoWhileCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + AllowWhileFalse(Options.getLocalOrGlobal("AllowWhileFalse", false)) {} + +void AvoidDoWhileCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "AllowWhileFalse", AllowWhileFalse); +} + +void AvoidDoWhileCheck::registerMatchers(MatchFinder *Finder) { + if (AllowWhileFalse) + Finder->addMatcher( + doStmt(unless(hasCondition(cxxBoolLiteral(equals(false)))), + unless(hasCondition( + implicitCastExpr(has(integerLiteral(equals(0))))))) + .bind("x"), + this); + else + Finder->addMatcher(doStmt().bind("x"), this); +} + +void AvoidDoWhileCheck::check(const MatchFinder::MatchResult &Result) { + if (const auto *MatchedDecl = Result.Nodes.getNodeAs("x")) + diag(MatchedDecl->getBeginLoc(), "do-while loops shall not be used"); +} + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt @@ -5,6 +5,7 @@ add_clang_library(clangTidyCppCoreGuidelinesModule AvoidConstOrRefDataMembersCheck.cpp + AvoidDoWhileCheck.cpp AvoidGotoCheck.cpp AvoidNonConstGlobalVariablesCheck.cpp CppCoreGuidelinesTidyModule.cpp diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp @@ -15,6 +15,7 @@ #include "../modernize/UseOverrideCheck.h" #include "../readability/MagicNumbersCheck.h" #include "AvoidConstOrRefDataMembersCheck.h" +#include "AvoidDoWhileCheck.h" #include "AvoidGotoCheck.h" #include "AvoidNonConstGlobalVariablesCheck.h" #include "InitVariablesCheck.h" @@ -50,6 +51,8 @@ "cppcoreguidelines-avoid-c-arrays"); CheckFactories.registerCheck( "cppcoreguidelines-avoid-const-or-ref-data-members"); + CheckFactories.registerCheck( + "cppcoreguidelines-avoid-do-while"); CheckFactories.registerCheck( "cppcoreguidelines-avoid-goto"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -104,6 +104,11 @@ Warns when a struct or class uses const or reference (lvalue or rvalue) data members. +- New :doc:`cppcoreguidelines-avoid-do-while + ` check. + + Warns when using ``do-while`` loops. + New check aliases ^^^^^^^^^^^^^^^^^ @@ -120,7 +125,7 @@ ` check. Partial support for C++14 signal handler rules was added. Bug report generation was improved. - + - Improved `modernize-use-emplace `_ check. The check now supports detecting inefficient invocations of ``push`` and diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-do-while.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-do-while.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-do-while.rst @@ -0,0 +1,40 @@ +.. title:: clang-tidy - cppcoreguidelines-avoid-do-while + +cppcoreguidelines-avoid-do-while +================================ + +Warns when using ``do-while`` loops. They are less readable than plain ``while`` +loops, since the termination condition is at the end and the condition is not +checked prior to the first iteration. This can lead to subtle bugs. + +The check implements +`rule ES.75 of C++ Core Guidelines `_. + +Examples: + +.. code-block:: c++ + + int x; + do { + std::cin >> x; + // ... + } while (x < 0); + +Options +------- + +.. option:: AllowWhileFalse + + Allows having ``do-while`` loops where the condition is a literal ``false`` + or ``0``. This covers the typical use case of safely defining function-like + macros: + + .. code-block:: c++ + + #define FOO_BAR(x) \ + do { \ + foo(x); \ + bar(x); \ + } while(0) + + Defaults to `false`. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -177,6 +177,7 @@ `concurrency-mt-unsafe `_, `concurrency-thread-canceltype-asynchronous `_, `cppcoreguidelines-avoid-const-or-ref-data-members `_, + `cppcoreguidelines-avoid-do-while `_, `cppcoreguidelines-avoid-goto `_, `cppcoreguidelines-avoid-non-const-global-variables `_, `cppcoreguidelines-init-variables `_, "Yes" diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-do-while.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-do-while.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-do-while.cpp @@ -0,0 +1,70 @@ +// RUN: %check_clang_tidy -check-suffixes=DEFAULT %s cppcoreguidelines-avoid-do-while %t +// RUN: %check_clang_tidy -check-suffixes=ALLOW-WHILE-FALSE %s cppcoreguidelines-avoid-do-while %t -- -config='{CheckOptions: [{key: cppcoreguidelines-avoid-do-while.AllowWhileFalse, value: true}]}' + +#define FOO(x) \ + do { \ + } while (x != 0) + +#define BAR_0(x) \ + do { \ + bar(x); \ + } while (0) + +#define BAR_FALSE(x) \ + do { \ + bar(x); \ + } while (false) + +void bar(int); +int baz(); + +void foo() +{ + // CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: do-while loops shall not be used [cppcoreguidelines-avoid-do-while] + do { + + } while(0); + + // CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: do-while loops shall not be used [cppcoreguidelines-avoid-do-while] + do { + + } while(false); + + // CHECK-MESSAGES-ALLOW-WHILE-FALSE: :[[@LINE+3]]:5: warning: do-while loops shall not be used + // CHECK-MESSAGES-DEFAULT: :[[@LINE+2]]:5: warning: do-while loops shall not be used + int x1{0}; + do { + x1 = baz(); + } while (x1 > 0); + + + // CHECK-MESSAGES-ALLOW-WHILE-FALSE: :[[@LINE+2]]:5: warning: do-while loops shall not be used + // CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: do-while loops shall not be used + do { + + } while (x1 != 0); + + // CHECK-MESSAGES-ALLOW-WHILE-FALSE: :[[@LINE+3]]:5: warning: do-while loops shall not be used + // CHECK-MESSAGES-DEFAULT: :[[@LINE+2]]:5: warning: do-while loops shall not be used + constexpr int x2{0}; + do { + + } while (x2); + + // CHECK-MESSAGES-ALLOW-WHILE-FALSE: :[[@LINE+3]]:5: warning: do-while loops shall not be used + // CHECK-MESSAGES-DEFAULT: :[[@LINE+2]]:5: warning: do-while loops shall not be used + constexpr bool x3{false}; + do { + + } while (x3); + + // CHECK-MESSAGES-ALLOW-WHILE-FALSE: :[[@LINE+2]]:5: warning: do-while loops shall not be used + // CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: do-while loops shall not be used + FOO(x1); + + // CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: do-while loops shall not be used + BAR_0(x1); + + // CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: do-while loops shall not be used + BAR_FALSE(x1); +}