Skip to content

Commit 1ae33bf

Browse files
committed
[clang-tidy] Add cppcoreguidelines-avoid-do-while check
Implements rule ES.75 of C++ Core Guidelines. Differential Revision: https://reviews.llvm.org/D132461
1 parent 0cf70a3 commit 1ae33bf

File tree

8 files changed

+218
-0
lines changed

8 files changed

+218
-0
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//===--- AvoidDoWhileCheck.cpp - clang-tidy -------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "AvoidDoWhileCheck.h"
10+
#include "clang/AST/ASTContext.h"
11+
#include "clang/ASTMatchers/ASTMatchFinder.h"
12+
13+
using namespace clang::ast_matchers;
14+
15+
namespace clang {
16+
namespace tidy {
17+
namespace cppcoreguidelines {
18+
19+
AvoidDoWhileCheck::AvoidDoWhileCheck(StringRef Name, ClangTidyContext *Context)
20+
: ClangTidyCheck(Name, Context),
21+
IgnoreMacros(Options.getLocalOrGlobal("IgnoreMacros", false)) {}
22+
23+
void AvoidDoWhileCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
24+
Options.store(Opts, "IgnoreMacros", IgnoreMacros);
25+
}
26+
27+
void AvoidDoWhileCheck::registerMatchers(MatchFinder *Finder) {
28+
Finder->addMatcher(doStmt().bind("x"), this);
29+
}
30+
31+
void AvoidDoWhileCheck::check(const MatchFinder::MatchResult &Result) {
32+
if (const auto *MatchedDecl = Result.Nodes.getNodeAs<DoStmt>("x")) {
33+
if (IgnoreMacros && MatchedDecl->getBeginLoc().isMacroID())
34+
return;
35+
diag(MatchedDecl->getBeginLoc(), "avoid do-while loops");
36+
}
37+
}
38+
39+
} // namespace cppcoreguidelines
40+
} // namespace tidy
41+
} // namespace clang
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
//===--- AvoidDoWhileCheck.h - clang-tidy -----------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_AVOIDDOWHILECHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_AVOIDDOWHILECHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
14+
namespace clang {
15+
namespace tidy {
16+
namespace cppcoreguidelines {
17+
18+
/// do-while loops are less readable than plan while loops, and can lead to
19+
/// subtle bugs.
20+
///
21+
/// For the user-facing documentation see:
22+
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/avoid-do-while.html
23+
class AvoidDoWhileCheck : public ClangTidyCheck {
24+
public:
25+
AvoidDoWhileCheck(StringRef Name, ClangTidyContext *Context);
26+
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
27+
return LangOpts.CPlusPlus;
28+
}
29+
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
30+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
31+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
32+
33+
private:
34+
bool IgnoreMacros;
35+
};
36+
37+
} // namespace cppcoreguidelines
38+
} // namespace tidy
39+
} // namespace clang
40+
41+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_AVOIDDOWHILECHECK_H

clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
55

66
add_clang_library(clangTidyCppCoreGuidelinesModule
77
AvoidConstOrRefDataMembersCheck.cpp
8+
AvoidDoWhileCheck.cpp
89
AvoidGotoCheck.cpp
910
AvoidNonConstGlobalVariablesCheck.cpp
1011
CppCoreGuidelinesTidyModule.cpp

clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "../modernize/UseOverrideCheck.h"
1616
#include "../readability/MagicNumbersCheck.h"
1717
#include "AvoidConstOrRefDataMembersCheck.h"
18+
#include "AvoidDoWhileCheck.h"
1819
#include "AvoidGotoCheck.h"
1920
#include "AvoidNonConstGlobalVariablesCheck.h"
2021
#include "InitVariablesCheck.h"
@@ -50,6 +51,8 @@ class CppCoreGuidelinesModule : public ClangTidyModule {
5051
"cppcoreguidelines-avoid-c-arrays");
5152
CheckFactories.registerCheck<AvoidConstOrRefDataMembersCheck>(
5253
"cppcoreguidelines-avoid-const-or-ref-data-members");
54+
CheckFactories.registerCheck<AvoidDoWhileCheck>(
55+
"cppcoreguidelines-avoid-do-while");
5356
CheckFactories.registerCheck<AvoidGotoCheck>(
5457
"cppcoreguidelines-avoid-goto");
5558
CheckFactories.registerCheck<readability::MagicNumbersCheck>(

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,11 @@ New checks
110110

111111
Warns when a struct or class uses const or reference (lvalue or rvalue) data members.
112112

113+
- New :doc:`cppcoreguidelines-avoid-do-while
114+
<clang-tidy/checks/cppcoreguidelines/avoid-do-while>` check.
115+
116+
Warns when using ``do-while`` loops.
117+
113118
New check aliases
114119
^^^^^^^^^^^^^^^^^
115120

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
.. title:: clang-tidy - cppcoreguidelines-avoid-do-while
2+
3+
cppcoreguidelines-avoid-do-while
4+
================================
5+
6+
Warns when using ``do-while`` loops. They are less readable than plain ``while``
7+
loops, since the termination condition is at the end and the condition is not
8+
checked prior to the first iteration. This can lead to subtle bugs.
9+
10+
The check implements
11+
`rule ES.75 of C++ Core Guidelines <https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Res-do>`_.
12+
13+
Examples:
14+
15+
.. code-block:: c++
16+
17+
int x;
18+
do {
19+
std::cin >> x;
20+
// ...
21+
} while (x < 0);
22+
23+
Options
24+
-------
25+
26+
.. option:: IgnoreMacros
27+
28+
Ignore the check when analyzing macros. This is useful for safely defining function-like macros:
29+
30+
.. code-block:: c++
31+
32+
#define FOO_BAR(x) \
33+
do { \
34+
foo(x); \
35+
bar(x); \
36+
} while(0)
37+
38+
Defaults to `false`.

clang-tools-extra/docs/clang-tidy/checks/list.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ Clang-Tidy Checks
178178
`concurrency-mt-unsafe <concurrency/mt-unsafe.html>`_,
179179
`concurrency-thread-canceltype-asynchronous <concurrency/thread-canceltype-asynchronous.html>`_,
180180
`cppcoreguidelines-avoid-const-or-ref-data-members <cppcoreguidelines/avoid-const-or-ref-data-members.html>`_,
181+
`cppcoreguidelines-avoid-do-while <cppcoreguidelines/avoid-do-while.html>`_,
181182
`cppcoreguidelines-avoid-goto <cppcoreguidelines/avoid-goto.html>`_,
182183
`cppcoreguidelines-avoid-non-const-global-variables <cppcoreguidelines/avoid-non-const-global-variables.html>`_,
183184
`cppcoreguidelines-init-variables <cppcoreguidelines/init-variables.html>`_, "Yes"
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// RUN: %check_clang_tidy -check-suffixes=DEFAULT %s cppcoreguidelines-avoid-do-while %t
2+
// RUN: %check_clang_tidy -check-suffixes=IGNORE-MACROS %s cppcoreguidelines-avoid-do-while %t -- -config='{CheckOptions: [{key: cppcoreguidelines-avoid-do-while.IgnoreMacros, value: true}]}'
3+
4+
#define FOO(x) \
5+
do { \
6+
} while (x != 0)
7+
8+
#define BAR_0(x) \
9+
do { \
10+
bar(x); \
11+
} while (0)
12+
13+
#define BAR_FALSE(x) \
14+
do { \
15+
bar(x); \
16+
} while (false)
17+
18+
void bar(int);
19+
int baz();
20+
21+
void foo()
22+
{
23+
// CHECK-MESSAGES-IGNORE-MACROS: :[[@LINE+2]]:5: warning: avoid do-while loops [cppcoreguidelines-avoid-do-while]
24+
// CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: avoid do-while loops [cppcoreguidelines-avoid-do-while]
25+
do {
26+
27+
} while(0);
28+
29+
// CHECK-MESSAGES-IGNORE-MACROS: :[[@LINE+2]]:5: warning: avoid do-while loops
30+
// CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: avoid do-while loops
31+
do {
32+
33+
} while(1);
34+
35+
// CHECK-MESSAGES-IGNORE-MACROS: :[[@LINE+2]]:5: warning: avoid do-while loops
36+
// CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: avoid do-while loops
37+
do {
38+
39+
} while(-1);
40+
41+
// CHECK-MESSAGES-IGNORE-MACROS: :[[@LINE+2]]:5: warning: avoid do-while loops
42+
// CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: avoid do-while loops
43+
do {
44+
45+
} while(false);
46+
47+
// CHECK-MESSAGES-IGNORE-MACROS: :[[@LINE+2]]:5: warning: avoid do-while loops
48+
// CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: avoid do-while loops
49+
do {
50+
51+
} while(true);
52+
53+
// CHECK-MESSAGES-IGNORE-MACROS: :[[@LINE+3]]:5: warning: avoid do-while loops
54+
// CHECK-MESSAGES-DEFAULT: :[[@LINE+2]]:5: warning: avoid do-while loops
55+
int x1{0};
56+
do {
57+
x1 = baz();
58+
} while (x1 > 0);
59+
60+
// CHECK-MESSAGES-IGNORE-MACROS: :[[@LINE+2]]:5: warning: avoid do-while loops
61+
// CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: avoid do-while loops
62+
do {
63+
64+
} while (x1 != 0);
65+
66+
// CHECK-MESSAGES-IGNORE-MACROS: :[[@LINE+3]]:5: warning: avoid do-while loops
67+
// CHECK-MESSAGES-DEFAULT: :[[@LINE+2]]:5: warning: avoid do-while loops
68+
constexpr int x2{0};
69+
do {
70+
71+
} while (x2);
72+
73+
// CHECK-MESSAGES-IGNORE-MACROS: :[[@LINE+3]]:5: warning: avoid do-while loops
74+
// CHECK-MESSAGES-DEFAULT: :[[@LINE+2]]:5: warning: avoid do-while loops
75+
constexpr bool x3{false};
76+
do {
77+
78+
} while (x3);
79+
80+
// CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: avoid do-while loops
81+
FOO(x1);
82+
83+
// CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: avoid do-while loops
84+
BAR_0(x1);
85+
86+
// CHECK-MESSAGES-DEFAULT: :[[@LINE+1]]:5: warning: avoid do-while loops
87+
BAR_FALSE(x1);
88+
}

0 commit comments

Comments
 (0)