Skip to content

Commit 3ed940a

Browse files
authored
[clang-tidy] Add check hicpp-ignored-remove-result (#73119)
This check implements the [rule 17.5.1](https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/standard-library) of the HICPP standard which states: - Do not ignore the result of std::remove, std::remove_if or std::unique The mutating algorithms std::remove, std::remove_if and both overloads of std::unique operate by swapping or moving elements of the range they are operating over. On completion, they return an iterator to the last valid element. In the majority of cases the correct behavior is to use this result as the first operand in a call to std::erase. This check is based on `bugprone-unused-return-value` but with a fixed set of functions. Suppressing issues by casting to `void` is enabled by default, but can be disabled by setting `AllowCastToVoid` option to `false`.
1 parent 4e80bc7 commit 3ed940a

File tree

10 files changed

+182
-1
lines changed

10 files changed

+182
-1
lines changed

clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,21 @@ UnusedReturnValueCheck::UnusedReturnValueCheck(llvm::StringRef Name,
133133
"::boost::system::error_code"))),
134134
AllowCastToVoid(Options.get("AllowCastToVoid", false)) {}
135135

136+
UnusedReturnValueCheck::UnusedReturnValueCheck(llvm::StringRef Name,
137+
ClangTidyContext *Context,
138+
std::string CheckedFunctions)
139+
: UnusedReturnValueCheck(Name, Context, std::move(CheckedFunctions), {},
140+
false) {}
141+
142+
UnusedReturnValueCheck::UnusedReturnValueCheck(
143+
llvm::StringRef Name, ClangTidyContext *Context,
144+
std::string CheckedFunctions, std::vector<StringRef> CheckedReturnTypes,
145+
bool AllowCastToVoid)
146+
: ClangTidyCheck(Name, Context),
147+
CheckedFunctions(std::move(CheckedFunctions)),
148+
CheckedReturnTypes(std::move(CheckedReturnTypes)),
149+
AllowCastToVoid(AllowCastToVoid) {}
150+
136151
void UnusedReturnValueCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
137152
Options.store(Opts, "CheckedFunctions", CheckedFunctions);
138153
Options.store(Opts, "CheckedReturnTypes",

clang-tools-extra/clang-tidy/bugprone/UnusedReturnValueCheck.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,15 @@ class UnusedReturnValueCheck : public ClangTidyCheck {
3131
private:
3232
std::string CheckedFunctions;
3333
const std::vector<StringRef> CheckedReturnTypes;
34-
const bool AllowCastToVoid;
34+
35+
protected:
36+
UnusedReturnValueCheck(StringRef Name, ClangTidyContext *Context,
37+
std::string CheckedFunctions);
38+
UnusedReturnValueCheck(StringRef Name, ClangTidyContext *Context,
39+
std::string CheckedFunctions,
40+
std::vector<StringRef> CheckedReturnTypes,
41+
bool AllowCastToVoid);
42+
bool AllowCastToVoid;
3543
};
3644

3745
} // namespace clang::tidy::bugprone

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS
66
add_clang_library(clangTidyHICPPModule
77
ExceptionBaseclassCheck.cpp
88
HICPPTidyModule.cpp
9+
IgnoredRemoveResultCheck.cpp
910
MultiwayPathsCoveredCheck.cpp
1011
NoAssemblerCheck.cpp
1112
SignedBitwiseCheck.cpp

clang-tools-extra/clang-tidy/hicpp/HICPPTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include "../readability/NamedParameterCheck.h"
3838
#include "../readability/UppercaseLiteralSuffixCheck.h"
3939
#include "ExceptionBaseclassCheck.h"
40+
#include "IgnoredRemoveResultCheck.h"
4041
#include "MultiwayPathsCoveredCheck.h"
4142
#include "NoAssemblerCheck.h"
4243
#include "SignedBitwiseCheck.h"
@@ -57,6 +58,8 @@ class HICPPModule : public ClangTidyModule {
5758
"hicpp-deprecated-headers");
5859
CheckFactories.registerCheck<ExceptionBaseclassCheck>(
5960
"hicpp-exception-baseclass");
61+
CheckFactories.registerCheck<IgnoredRemoveResultCheck>(
62+
"hicpp-ignored-remove-result");
6063
CheckFactories.registerCheck<MultiwayPathsCoveredCheck>(
6164
"hicpp-multiway-paths-covered");
6265
CheckFactories.registerCheck<SignedBitwiseCheck>("hicpp-signed-bitwise");
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//===--- IgnoredRemoveResultCheck.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 "IgnoredRemoveResultCheck.h"
10+
11+
namespace clang::tidy::hicpp {
12+
13+
IgnoredRemoveResultCheck::IgnoredRemoveResultCheck(llvm::StringRef Name,
14+
ClangTidyContext *Context)
15+
: UnusedReturnValueCheck(Name, Context,
16+
"::std::remove;"
17+
"::std::remove_if;"
18+
"::std::unique") {
19+
// The constructor for ClangTidyCheck needs to have been called
20+
// before we can access options via Options.get().
21+
AllowCastToVoid = Options.get("AllowCastToVoid", true);
22+
}
23+
24+
void IgnoredRemoveResultCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
25+
Options.store(Opts, "AllowCastToVoid", AllowCastToVoid);
26+
}
27+
28+
} // namespace clang::tidy::hicpp
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//===--- IgnoredRemoveResultCheck.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_HICPP_IGNOREDREMOVERESULTCHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_HICPP_IGNOREDREMOVERESULTCHECK_H
11+
12+
#include "../bugprone/UnusedReturnValueCheck.h"
13+
14+
namespace clang::tidy::hicpp {
15+
16+
/// Ensure that the result of std::remove, std::remove_if and std::unique
17+
/// are not ignored according to rule 17.5.1.
18+
///
19+
/// For the user-facing documentation see:
20+
/// http://clang.llvm.org/extra/clang-tidy/checks/hicpp/ignored-remove-result.html
21+
class IgnoredRemoveResultCheck : public bugprone::UnusedReturnValueCheck {
22+
public:
23+
IgnoredRemoveResultCheck(StringRef Name, ClangTidyContext *Context);
24+
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
25+
};
26+
27+
} // namespace clang::tidy::hicpp
28+
29+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_HICPP_IGNOREDREMOVERESULTCHECK_H

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,12 @@ New checks
174174
Flags coroutines that suspend while a lock guard is in scope at the
175175
suspension point.
176176

177+
- New :doc:`hicpp-ignored-remove-result
178+
<clang-tidy/checks/hicpp/ignored-remove-result>` check.
179+
180+
Ensure that the result of ``std::remove``, ``std::remove_if`` and
181+
``std::unique`` are not ignored according to rule 17.5.1.
182+
177183
- New :doc:`misc-coroutine-hostile-raii
178184
<clang-tidy/checks/misc/coroutine-hostile-raii>` check.
179185

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
.. title:: clang-tidy - hicpp-ignored-remove-result
2+
3+
hicpp-ignored-remove-result
4+
===========================
5+
6+
Ensure that the result of ``std::remove``, ``std::remove_if`` and ``std::unique``
7+
are not ignored according to
8+
`rule 17.5.1 <https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard/standard-library>`_.
9+
10+
The mutating algorithms ``std::remove``, ``std::remove_if`` and both overloads
11+
of ``std::unique`` operate by swapping or moving elements of the range they are
12+
operating over. On completion, they return an iterator to the last valid
13+
element. In the majority of cases the correct behavior is to use this result as
14+
the first operand in a call to ``std::erase``.
15+
16+
This check is a subset of :doc:`bugprone-unused-return-value <../bugprone/unused-return-value>`
17+
and depending on used options it can be superfluous to enable both checks.
18+
19+
Options
20+
-------
21+
22+
.. option:: AllowCastToVoid
23+
24+
Controls whether casting return values to ``void`` is permitted. Default: `true`.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ Clang-Tidy Checks
226226
:doc:`google-runtime-operator <google/runtime-operator>`,
227227
:doc:`google-upgrade-googletest-case <google/upgrade-googletest-case>`, "Yes"
228228
:doc:`hicpp-exception-baseclass <hicpp/exception-baseclass>`,
229+
:doc:`hicpp-ignored-remove-result <hicpp/ignored-remove-result>`,
229230
:doc:`hicpp-multiway-paths-covered <hicpp/multiway-paths-covered>`,
230231
:doc:`hicpp-no-assembler <hicpp/no-assembler>`,
231232
:doc:`hicpp-signed-bitwise <hicpp/signed-bitwise>`,
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// RUN: %check_clang_tidy %s hicpp-ignored-remove-result %t
2+
// RUN: %check_clang_tidy -check-suffixes=NOCAST %s hicpp-ignored-remove-result %t -- -config='{CheckOptions: {hicpp-ignored-remove-result.AllowCastToVoid: false}}'
3+
4+
namespace std {
5+
6+
template <typename ForwardIt, typename T>
7+
ForwardIt remove(ForwardIt, ForwardIt, const T &);
8+
9+
template <typename ForwardIt, typename UnaryPredicate>
10+
ForwardIt remove_if(ForwardIt, ForwardIt, UnaryPredicate);
11+
12+
template <typename ForwardIt>
13+
ForwardIt unique(ForwardIt, ForwardIt);
14+
15+
template <class InputIt, class T>
16+
InputIt find(InputIt, InputIt, const T&);
17+
18+
class error_code {
19+
};
20+
21+
} // namespace std
22+
23+
std::error_code errorFunc() {
24+
return std::error_code();
25+
}
26+
27+
void warning() {
28+
std::remove(nullptr, nullptr, 1);
29+
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should not be disregarded; neglecting it may lead to errors
30+
// CHECK-MESSAGES: [[@LINE-2]]:3: note: cast the expression to void to silence this warning
31+
// CHECK-MESSAGES-NOCAST: [[@LINE-3]]:3: warning: the value returned by this function should not be disregarded; neglecting it may lead to errors
32+
33+
std::remove_if(nullptr, nullptr, nullptr);
34+
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should not be disregarded; neglecting it may lead to errors
35+
// CHECK-MESSAGES: [[@LINE-2]]:3: note: cast the expression to void to silence this warning
36+
// CHECK-MESSAGES-NOCAST: [[@LINE-3]]:3: warning: the value returned by this function should not be disregarded; neglecting it may lead to errors
37+
38+
std::unique(nullptr, nullptr);
39+
// CHECK-MESSAGES: [[@LINE-1]]:3: warning: the value returned by this function should not be disregarded; neglecting it may lead to errors
40+
// CHECK-MESSAGES: [[@LINE-2]]:3: note: cast the expression to void to silence this warning
41+
// CHECK-MESSAGES-NOCAST: [[@LINE-3]]:3: warning: the value returned by this function should not be disregarded; neglecting it may lead to errors
42+
}
43+
44+
void optionalWarning() {
45+
// No warning unless AllowCastToVoid=false
46+
(void)std::remove(nullptr, nullptr, 1);
47+
// CHECK-MESSAGES-NOCAST: [[@LINE-1]]:9: warning: the value returned by this function should not be disregarded; neglecting it may lead to errors
48+
}
49+
50+
void noWarning() {
51+
52+
auto RemoveRetval = std::remove(nullptr, nullptr, 1);
53+
54+
auto RemoveIfRetval = std::remove_if(nullptr, nullptr, nullptr);
55+
56+
auto UniqueRetval = std::unique(nullptr, nullptr);
57+
58+
// Verify that other checks in the baseclass are not used.
59+
// - no warning on std::find since the checker overrides
60+
// bugprone-unused-return-value's checked functions.
61+
std::find(nullptr, nullptr, 1);
62+
// - no warning on return types since the checker disable
63+
// bugprone-unused-return-value's checked return types.
64+
errorFunc();
65+
(void) errorFunc();
66+
}

0 commit comments

Comments
 (0)