Skip to content

Commit

Permalink
[clang-tidy] Add cppcoreguidelines-avoid-do-while check
Browse files Browse the repository at this point in the history
Implements rule ES.75 of C++ Core Guidelines.

Differential Revision: https://reviews.llvm.org/D132461
  • Loading branch information
carlosgalvezp committed Oct 10, 2022
1 parent 0cf70a3 commit 1ae33bf
Show file tree
Hide file tree
Showing 8 changed files with 218 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -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
41 changes: 41 additions & 0 deletions clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidDoWhileCheck.h
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS

add_clang_library(clangTidyCppCoreGuidelinesModule
AvoidConstOrRefDataMembersCheck.cpp
AvoidDoWhileCheck.cpp
AvoidGotoCheck.cpp
AvoidNonConstGlobalVariablesCheck.cpp
CppCoreGuidelinesTidyModule.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -50,6 +51,8 @@ class CppCoreGuidelinesModule : public ClangTidyModule {
"cppcoreguidelines-avoid-c-arrays");
CheckFactories.registerCheck<AvoidConstOrRefDataMembersCheck>(
"cppcoreguidelines-avoid-const-or-ref-data-members");
CheckFactories.registerCheck<AvoidDoWhileCheck>(
"cppcoreguidelines-avoid-do-while");
CheckFactories.registerCheck<AvoidGotoCheck>(
"cppcoreguidelines-avoid-goto");
CheckFactories.registerCheck<readability::MagicNumbersCheck>(
Expand Down
5 changes: 5 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ New checks

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

Expand Down
Original file line number Diff line number Diff line change
@@ -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`.
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ Clang-Tidy Checks
`concurrency-mt-unsafe <concurrency/mt-unsafe.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-do-while <cppcoreguidelines/avoid-do-while.html>`_,
`cppcoreguidelines-avoid-goto <cppcoreguidelines/avoid-goto.html>`_,
`cppcoreguidelines-avoid-non-const-global-variables <cppcoreguidelines/avoid-non-const-global-variables.html>`_,
`cppcoreguidelines-init-variables <cppcoreguidelines/init-variables.html>`_, "Yes"
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit 1ae33bf

Please sign in to comment.