Skip to content

Commit 01c1156

Browse files
committed
[clang-tidy] Add IgnoreTypes option to modernize-use-nullptr
New option added and configured in a way, so types related to std::strong_ordering would be ignored. Fixes: #63478 Reviewed By: ccotter Differential Revision: https://reviews.llvm.org/D158928
1 parent 4e52fd8 commit 01c1156

File tree

7 files changed

+104
-11
lines changed

7 files changed

+104
-11
lines changed

clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.cpp

+14-5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "UseNullptrCheck.h"
10+
#include "../utils/Matchers.h"
11+
#include "../utils/OptionsUtils.h"
1012
#include "clang/AST/ASTContext.h"
1113
#include "clang/AST/RecursiveASTVisitor.h"
1214
#include "clang/ASTMatchers/ASTMatchFinder.h"
@@ -33,11 +35,13 @@ AST_MATCHER(Type, sugaredNullptrType) {
3335
/// to null within.
3436
/// Finding sequences of explicit casts is necessary so that an entire sequence
3537
/// can be replaced instead of just the inner-most implicit cast.
36-
StatementMatcher makeCastSequenceMatcher() {
37-
StatementMatcher ImplicitCastToNull = implicitCastExpr(
38+
StatementMatcher makeCastSequenceMatcher(llvm::ArrayRef<StringRef> NameList) {
39+
auto ImplicitCastToNull = implicitCastExpr(
3840
anyOf(hasCastKind(CK_NullToPointer), hasCastKind(CK_NullToMemberPointer)),
3941
unless(hasImplicitDestinationType(qualType(substTemplateTypeParmType()))),
40-
unless(hasSourceExpression(hasType(sugaredNullptrType()))));
42+
unless(hasSourceExpression(hasType(sugaredNullptrType()))),
43+
unless(hasImplicitDestinationType(
44+
qualType(matchers::matchesAnyListedTypeName(NameList)))));
4145

4246
auto IsOrHasDescendant = [](auto InnerMatcher) {
4347
return anyOf(InnerMatcher, hasDescendant(InnerMatcher));
@@ -477,16 +481,21 @@ class CastSequenceVisitor : public RecursiveASTVisitor<CastSequenceVisitor> {
477481

478482
UseNullptrCheck::UseNullptrCheck(StringRef Name, ClangTidyContext *Context)
479483
: ClangTidyCheck(Name, Context),
480-
NullMacrosStr(Options.get("NullMacros", "NULL")) {
484+
NullMacrosStr(Options.get("NullMacros", "NULL")),
485+
IgnoredTypes(utils::options::parseStringList(Options.get(
486+
"IgnoredTypes",
487+
"std::_CmpUnspecifiedParam::;^std::__cmp_cat::__unspec"))) {
481488
StringRef(NullMacrosStr).split(NullMacros, ",");
482489
}
483490

484491
void UseNullptrCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
485492
Options.store(Opts, "NullMacros", NullMacrosStr);
493+
Options.store(Opts, "IgnoredTypes",
494+
utils::options::serializeStringList(IgnoredTypes));
486495
}
487496

488497
void UseNullptrCheck::registerMatchers(MatchFinder *Finder) {
489-
Finder->addMatcher(makeCastSequenceMatcher(), this);
498+
Finder->addMatcher(makeCastSequenceMatcher(IgnoredTypes), this);
490499
}
491500

492501
void UseNullptrCheck::check(const MatchFinder::MatchResult &Result) {

clang-tools-extra/clang-tidy/modernize/UseNullptrCheck.h

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ class UseNullptrCheck : public ClangTidyCheck {
2828
private:
2929
const StringRef NullMacrosStr;
3030
SmallVector<StringRef, 1> NullMacros;
31+
std::vector<StringRef> IgnoredTypes;
3132
};
3233

3334
} // namespace clang::tidy::modernize

clang-tools-extra/clang-tidy/utils/Matchers.cpp

+28
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,32 @@ bool NotIdenticalStatementsPredicate::operator()(
1717
Nodes.getNodeAs<Stmt>(ID), *Context);
1818
}
1919

20+
MatchesAnyListedTypeNameMatcher::MatchesAnyListedTypeNameMatcher(
21+
llvm::ArrayRef<StringRef> NameList)
22+
: NameMatchers(NameList.begin(), NameList.end()) {}
23+
24+
MatchesAnyListedTypeNameMatcher::~MatchesAnyListedTypeNameMatcher() = default;
25+
26+
bool MatchesAnyListedTypeNameMatcher::matches(
27+
const QualType &Node, ast_matchers::internal::ASTMatchFinder *Finder,
28+
ast_matchers::internal::BoundNodesTreeBuilder *Builder) const {
29+
30+
if (NameMatchers.empty())
31+
return false;
32+
33+
PrintingPolicy PrintingPolicyWithSuppressedTag(
34+
Finder->getASTContext().getLangOpts());
35+
PrintingPolicyWithSuppressedTag.PrintCanonicalTypes = true;
36+
PrintingPolicyWithSuppressedTag.SuppressElaboration = true;
37+
PrintingPolicyWithSuppressedTag.SuppressScope = false;
38+
PrintingPolicyWithSuppressedTag.SuppressTagKeyword = true;
39+
PrintingPolicyWithSuppressedTag.SuppressUnwrittenScope = true;
40+
std::string TypeName =
41+
Node.getUnqualifiedType().getAsString(PrintingPolicyWithSuppressedTag);
42+
43+
return llvm::any_of(NameMatchers, [&TypeName](const llvm::Regex &NM) {
44+
return NM.isValid() && NM.match(TypeName);
45+
});
46+
}
47+
2048
} // namespace clang::tidy::matchers

clang-tools-extra/clang-tidy/utils/Matchers.h

+22
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,28 @@ AST_MATCHER_P(Stmt, isStatementIdenticalToBoundNode, std::string, ID) {
158158
return Builder->removeBindings(Predicate);
159159
}
160160

161+
// A matcher implementation that matches a list of type name regular expressions
162+
// against a QualType.
163+
class MatchesAnyListedTypeNameMatcher
164+
: public ast_matchers::internal::MatcherInterface<QualType> {
165+
public:
166+
explicit MatchesAnyListedTypeNameMatcher(llvm::ArrayRef<StringRef> NameList);
167+
~MatchesAnyListedTypeNameMatcher() override;
168+
bool matches(
169+
const QualType &Node, ast_matchers::internal::ASTMatchFinder *Finder,
170+
ast_matchers::internal::BoundNodesTreeBuilder *Builder) const override;
171+
172+
private:
173+
std::vector<llvm::Regex> NameMatchers;
174+
};
175+
176+
// Returns a matcher that matches QualType against a list of provided regular.
177+
inline ::clang::ast_matchers::internal::Matcher<QualType>
178+
matchesAnyListedTypeName(llvm::ArrayRef<StringRef> NameList) {
179+
return ::clang::ast_matchers::internal::makeMatcher(
180+
new MatchesAnyListedTypeNameMatcher(NameList));
181+
}
182+
161183
} // namespace clang::tidy::matchers
162184

163185
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_MATCHERS_H

clang-tools-extra/docs/ReleaseNotes.rst

+5-1
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ Changes in existing checks
227227
`DeduplicateFindings` to output one finding per symbol occurrence.
228228

229229
- Improved :doc:`misc-include-cleaner
230-
<clang-tidy/checks/misc/include-cleaner>` check to avoid fixes insert
230+
<clang-tidy/checks/misc/include-cleaner>` check to avoid fixes insert
231231
same include header multiple times.
232232

233233
- Improved :doc:`misc-redundant-expression
@@ -242,6 +242,10 @@ Changes in existing checks
242242
<clang-tidy/checks/modernize/use-equals-delete>` check to ignore
243243
false-positives when special member function is actually used or implicit.
244244

245+
- Improved :doc:`modernize-use-nullptr
246+
<clang-tidy/checks/modernize/use-nullptr>` check by adding option
247+
`IgnoredTypes` that can be used to exclude some pointer types.
248+
245249
- Improved :doc:`modernize-use-std-print
246250
<clang-tidy/checks/modernize/use-std-print>` check to accurately generate
247251
fixes for reordering arguments.

clang-tools-extra/docs/clang-tidy/checks/modernize/use-nullptr.rst

+6
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ transforms to:
3939
Options
4040
-------
4141

42+
.. option:: IgnoredTypes
43+
44+
Semicolon-separated list of regular expressions to match pointer types for
45+
which implicit casts will be ignored. Default value:
46+
`std::_CmpUnspecifiedParam::;^std::__cmp_cat::__unspec`.
47+
4248
.. option:: NullMacros
4349

4450
Comma-separated list of macro names that will be transformed along with

clang-tools-extra/test/clang-tidy/checkers/modernize/use-nullptr-cxx20.cpp

+28-5
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,51 @@
1-
// RUN: %check_clang_tidy -std=c++20 %s modernize-use-nullptr %t
1+
// RUN: %check_clang_tidy -std=c++20 %s modernize-use-nullptr %t -- -- -DGCC
2+
// RUN: %check_clang_tidy -std=c++20 %s modernize-use-nullptr %t -- -- -DCLANG
23

34
namespace std {
45
class strong_ordering;
56

67
// Mock how STD defined unspecified parameters for the operators below.
8+
#ifdef CLANG
79
struct _CmpUnspecifiedParam {
810
consteval
911
_CmpUnspecifiedParam(int _CmpUnspecifiedParam::*) noexcept {}
1012
};
1113

14+
#define UNSPECIFIED_TYPE _CmpUnspecifiedParam
15+
#endif
16+
17+
#ifdef GCC
18+
namespace __cmp_cat {
19+
struct __unspec {
20+
constexpr __unspec(__unspec*) noexcept { }
21+
};
22+
}
23+
24+
#define UNSPECIFIED_TYPE __cmp_cat::__unspec
25+
#endif
26+
1227
struct strong_ordering {
1328
signed char value;
1429

1530
friend constexpr bool operator==(strong_ordering v,
16-
_CmpUnspecifiedParam) noexcept {
31+
UNSPECIFIED_TYPE) noexcept {
1732
return v.value == 0;
1833
}
1934
friend constexpr bool operator<(strong_ordering v,
20-
_CmpUnspecifiedParam) noexcept {
35+
UNSPECIFIED_TYPE) noexcept {
2136
return v.value < 0;
2237
}
2338
friend constexpr bool operator>(strong_ordering v,
24-
_CmpUnspecifiedParam) noexcept {
39+
UNSPECIFIED_TYPE) noexcept {
2540
return v.value > 0;
2641
}
2742
friend constexpr bool operator>=(strong_ordering v,
28-
_CmpUnspecifiedParam) noexcept {
43+
UNSPECIFIED_TYPE) noexcept {
2944
return v.value >= 0;
3045
}
3146
static const strong_ordering equal, greater, less;
3247
};
48+
3349
constexpr strong_ordering strong_ordering::equal = {0};
3450
constexpr strong_ordering strong_ordering::greater = {1};
3551
constexpr strong_ordering strong_ordering::less = {-1};
@@ -59,6 +75,13 @@ void test_cxx_rewritten_binary_ops() {
5975
// CHECK-FIXES: result = (a1 > ((a1 > (ptr == nullptr ? a1 : a2)) ? a1 : a2));
6076
}
6177

78+
void testValidZero() {
79+
A a1, a2;
80+
auto result = a1 <=> a2;
81+
if (result < 0) {}
82+
// CHECK-FIXES: if (result < 0) {}
83+
}
84+
6285
template<class T1, class T2>
6386
struct P {
6487
T1 x1;

0 commit comments

Comments
 (0)