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 IgnoreMacros; +}; + +} // 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,41 @@ +//===--- 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), + IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", false)) {} + +void AvoidDoWhileCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "IgnoreMacros", IgnoreMacros); +} + +void AvoidDoWhileCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(doStmt().bind("x"), this); +} + +void AvoidDoWhileCheck::check(const MatchFinder::MatchResult &Result) { + if (const auto *MatchedDecl = Result.Nodes.getNodeAs("x")) { + if (IgnoreMacros && MatchedDecl->getBeginLoc().isMacroID()) + return; + diag(MatchedDecl->getBeginLoc(), "avoid do-while loops"); + } +} + +} // 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 @@ -110,6 +110,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 ^^^^^^^^^^^^^^^^^ 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,38 @@ +.. 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:: IgnoreMacros + + Ignore the check when analyzing macros. This is useful for 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 @@ -178,6 +178,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,88 @@ +// RUN: %check_clang_tidy -check-suffixes=DEFAULT %s cppcoreguidelines-avoid-do-while %t +// RUN: %check_clang_tidy -check-suffixes=IGNORE-MACROS %s cppcoreguidelines-avoid-do-while %t -- -config='{CheckOptions: [{key: cppcoreguidelines-avoid-do-while.IgnoreMacros, 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-IGNORE-MACROS: :[[@LINE+2]]:5: warning: avoid do-while loops [cppcoreguidelines-avoid-do-while] + // CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: avoid do-while loops [cppcoreguidelines-avoid-do-while] + do { + + } while(0); + + // CHECK-MESSAGES-IGNORE-MACROS: :[[@LINE+2]]:5: warning: avoid do-while loops + // CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: avoid do-while loops + do { + + } while(1); + + // CHECK-MESSAGES-IGNORE-MACROS: :[[@LINE+2]]:5: warning: avoid do-while loops + // CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: avoid do-while loops + do { + + } while(-1); + + // CHECK-MESSAGES-IGNORE-MACROS: :[[@LINE+2]]:5: warning: avoid do-while loops + // CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: avoid do-while loops + do { + + } while(false); + + // CHECK-MESSAGES-IGNORE-MACROS: :[[@LINE+2]]:5: warning: avoid do-while loops + // CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: avoid do-while loops + do { + + } while(true); + + // CHECK-MESSAGES-IGNORE-MACROS: :[[@LINE+3]]:5: warning: avoid do-while loops + // CHECK-MESSAGES-DEFAULT: :[[@LINE+2]]:5: warning: avoid do-while loops + int x1{0}; + do { + x1 = baz(); + } while (x1 > 0); + + // CHECK-MESSAGES-IGNORE-MACROS: :[[@LINE+2]]:5: warning: avoid do-while loops + // CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: avoid do-while loops + do { + + } while (x1 != 0); + + // CHECK-MESSAGES-IGNORE-MACROS: :[[@LINE+3]]:5: warning: avoid do-while loops + // CHECK-MESSAGES-DEFAULT: :[[@LINE+2]]:5: warning: avoid do-while loops + constexpr int x2{0}; + do { + + } while (x2); + + // CHECK-MESSAGES-IGNORE-MACROS: :[[@LINE+3]]:5: warning: avoid do-while loops + // CHECK-MESSAGES-DEFAULT: :[[@LINE+2]]:5: warning: avoid do-while loops + constexpr bool x3{false}; + do { + + } while (x3); + + // CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: avoid do-while loops + FOO(x1); + + // CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: avoid do-while loops + BAR_0(x1); + + // CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: avoid do-while loops + BAR_FALSE(x1); +}