[clang-tidy] Add cppcoreguidelines-avoid-do-while check

Implements rule ES.75 of C++ Core Guidelines.

Differential Revision: https://reviews.llvm.org/D132461
This commit is contained in:
Carlos Galvez 2022-08-23 06:14:03 +00:00
parent 0cf70a33f2
commit 1ae33bf426
8 changed files with 218 additions and 0 deletions

View File

@ -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<DoStmt>("x")) {
if (IgnoreMacros && MatchedDecl->getBeginLoc().isMacroID())
return;
diag(MatchedDecl->getBeginLoc(), "avoid do-while loops");
}
}
} // namespace cppcoreguidelines
} // namespace tidy
} // namespace clang

View File

@ -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

View File

@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
add_clang_library(clangTidyCppCoreGuidelinesModule add_clang_library(clangTidyCppCoreGuidelinesModule
AvoidConstOrRefDataMembersCheck.cpp AvoidConstOrRefDataMembersCheck.cpp
AvoidDoWhileCheck.cpp
AvoidGotoCheck.cpp AvoidGotoCheck.cpp
AvoidNonConstGlobalVariablesCheck.cpp AvoidNonConstGlobalVariablesCheck.cpp
CppCoreGuidelinesTidyModule.cpp CppCoreGuidelinesTidyModule.cpp

View File

@ -15,6 +15,7 @@
#include "../modernize/UseOverrideCheck.h" #include "../modernize/UseOverrideCheck.h"
#include "../readability/MagicNumbersCheck.h" #include "../readability/MagicNumbersCheck.h"
#include "AvoidConstOrRefDataMembersCheck.h" #include "AvoidConstOrRefDataMembersCheck.h"
#include "AvoidDoWhileCheck.h"
#include "AvoidGotoCheck.h" #include "AvoidGotoCheck.h"
#include "AvoidNonConstGlobalVariablesCheck.h" #include "AvoidNonConstGlobalVariablesCheck.h"
#include "InitVariablesCheck.h" #include "InitVariablesCheck.h"
@ -50,6 +51,8 @@ public:
"cppcoreguidelines-avoid-c-arrays"); "cppcoreguidelines-avoid-c-arrays");
CheckFactories.registerCheck<AvoidConstOrRefDataMembersCheck>( CheckFactories.registerCheck<AvoidConstOrRefDataMembersCheck>(
"cppcoreguidelines-avoid-const-or-ref-data-members"); "cppcoreguidelines-avoid-const-or-ref-data-members");
CheckFactories.registerCheck<AvoidDoWhileCheck>(
"cppcoreguidelines-avoid-do-while");
CheckFactories.registerCheck<AvoidGotoCheck>( CheckFactories.registerCheck<AvoidGotoCheck>(
"cppcoreguidelines-avoid-goto"); "cppcoreguidelines-avoid-goto");
CheckFactories.registerCheck<readability::MagicNumbersCheck>( CheckFactories.registerCheck<readability::MagicNumbersCheck>(

View File

@ -110,6 +110,11 @@ New checks
Warns when a struct or class uses const or reference (lvalue or rvalue) data members. Warns when a struct or class uses const or reference (lvalue or rvalue) data members.
- New :doc:`cppcoreguidelines-avoid-do-while
<clang-tidy/checks/cppcoreguidelines/avoid-do-while>` check.
Warns when using ``do-while`` loops.
New check aliases New check aliases
^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^

View File

@ -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 <https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Res-do>`_.
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`.

View File

@ -178,6 +178,7 @@ Clang-Tidy Checks
`concurrency-mt-unsafe <concurrency/mt-unsafe.html>`_, `concurrency-mt-unsafe <concurrency/mt-unsafe.html>`_,
`concurrency-thread-canceltype-asynchronous <concurrency/thread-canceltype-asynchronous.html>`_, `concurrency-thread-canceltype-asynchronous <concurrency/thread-canceltype-asynchronous.html>`_,
`cppcoreguidelines-avoid-const-or-ref-data-members <cppcoreguidelines/avoid-const-or-ref-data-members.html>`_, `cppcoreguidelines-avoid-const-or-ref-data-members <cppcoreguidelines/avoid-const-or-ref-data-members.html>`_,
`cppcoreguidelines-avoid-do-while <cppcoreguidelines/avoid-do-while.html>`_,
`cppcoreguidelines-avoid-goto <cppcoreguidelines/avoid-goto.html>`_, `cppcoreguidelines-avoid-goto <cppcoreguidelines/avoid-goto.html>`_,
`cppcoreguidelines-avoid-non-const-global-variables <cppcoreguidelines/avoid-non-const-global-variables.html>`_, `cppcoreguidelines-avoid-non-const-global-variables <cppcoreguidelines/avoid-non-const-global-variables.html>`_,
`cppcoreguidelines-init-variables <cppcoreguidelines/init-variables.html>`_, "Yes" `cppcoreguidelines-init-variables <cppcoreguidelines/init-variables.html>`_, "Yes"

View File

@ -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);
}