Skip to content

Commit a406fb8

Browse files
authored
[libc++] Clang-tidy operator& hijacker. (#128366)
Guards against introducing new places where operator& depends on a template type.
1 parent 01ec74d commit a406fb8

File tree

5 files changed

+82
-0
lines changed

5 files changed

+82
-0
lines changed

libcxx/test/libcxx/clang_tidy.gen.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#
77
# ===----------------------------------------------------------------------===##
88

9+
910
# Run our custom libc++ clang-tidy checks on all public headers.
1011

1112
# RUN: %{python} %s %{libcxx-dir}/utils
@@ -23,6 +24,9 @@
2324
2425
// REQUIRES: has-clang-tidy
2526
27+
// The frozen headers should not be updated to the latest libc++ style, so don't test.
28+
// UNSUPPORTED: FROZEN-CXX03-HEADERS-FIXME
29+
2630
// The GCC compiler flags are not always compatible with clang-tidy.
2731
// UNSUPPORTED: gcc
2832

libcxx/test/tools/clang_tidy_checks/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ set(SOURCES
9696
nodebug_on_aliases.cpp
9797
proper_version_checks.cpp
9898
robust_against_adl.cpp
99+
robust_against_operator_ampersand.cpp
99100
uglify_attributes.cpp
100101

101102
libcpp_module.cpp

libcxx/test/tools/clang_tidy_checks/libcpp_module.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "nodebug_on_aliases.hpp"
1717
#include "proper_version_checks.hpp"
1818
#include "robust_against_adl.hpp"
19+
#include "robust_against_operator_ampersand.hpp"
1920
#include "uglify_attributes.hpp"
2021

2122
namespace {
@@ -29,6 +30,8 @@ class LibcxxTestModule : public clang::tidy::ClangTidyModule {
2930
check_factories.registerCheck<libcpp::nodebug_on_aliases>("libcpp-nodebug-on-aliases");
3031
check_factories.registerCheck<libcpp::proper_version_checks>("libcpp-cpp-version-check");
3132
check_factories.registerCheck<libcpp::robust_against_adl_check>("libcpp-robust-against-adl");
33+
check_factories.registerCheck<libcpp::robust_against_operator_ampersand>(
34+
"libcpp-robust-against-operator-ampersand");
3235
check_factories.registerCheck<libcpp::uglify_attributes>("libcpp-uglify-attributes");
3336
}
3437
};
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//===----------------------------------------------------------------------===//
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 "clang-tidy/ClangTidyCheck.h"
10+
#include "clang-tidy/ClangTidyModuleRegistry.h"
11+
#include "clang/ASTMatchers/ASTMatchers.h"
12+
#include "clang/Tooling/FixIt.h"
13+
14+
#include "robust_against_operator_ampersand.hpp"
15+
16+
// This clang-tidy check ensures that we don't use operator& on dependant
17+
// types. If the type is user supplied it may call the type's operator&.
18+
// Instead use std::addressof.
19+
//
20+
// This is part of libc++'s policy
21+
// https://libcxx.llvm.org/CodingGuidelines.html#don-t-use-argument-dependent-lookup-unless-required-by-the-standard
22+
23+
// TODO(LLVM-21) Remove dependentScopeDeclRefExpr
24+
// dependentScopeDeclRefExpr requires Clang 20, this uses the same definition as Clang
25+
#if defined(__clang_major__) && __clang_major__ < 20
26+
namespace clang::ast_matchers {
27+
const internal::VariadicDynCastAllOfMatcher<Stmt, DependentScopeDeclRefExpr> dependentScopeDeclRefExpr;
28+
} // namespace clang::ast_matchers
29+
#endif
30+
31+
namespace libcpp {
32+
robust_against_operator_ampersand::robust_against_operator_ampersand(
33+
llvm::StringRef name, clang::tidy::ClangTidyContext* context)
34+
: clang::tidy::ClangTidyCheck(name, context) {}
35+
36+
void robust_against_operator_ampersand::registerMatchers(clang::ast_matchers::MatchFinder* finder) {
37+
using namespace clang::ast_matchers;
38+
finder->addMatcher(
39+
cxxOperatorCallExpr(allOf(hasOperatorName("&"), argumentCountIs(1), isTypeDependent()),
40+
unless(hasUnaryOperand(dependentScopeDeclRefExpr())))
41+
.bind("match"),
42+
this);
43+
}
44+
45+
void robust_against_operator_ampersand::check(const clang::ast_matchers::MatchFinder::MatchResult& result) {
46+
if (const auto* call = result.Nodes.getNodeAs< clang::CXXOperatorCallExpr >("match"); call != nullptr) {
47+
diag(call->getBeginLoc(), "Guard against user provided operator& for dependent types.")
48+
<< clang::FixItHint::CreateReplacement(
49+
call->getSourceRange(),
50+
(llvm::Twine(
51+
"std::addressof(" + clang::tooling::fixit::getText(*call->getArg(0), *result.Context) + ")"))
52+
.str());
53+
}
54+
}
55+
56+
} // namespace libcpp
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//===----------------------------------------------------------------------===//
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 "clang-tidy/ClangTidyCheck.h"
10+
11+
namespace libcpp {
12+
class robust_against_operator_ampersand : public clang::tidy::ClangTidyCheck {
13+
public:
14+
robust_against_operator_ampersand(llvm::StringRef, clang::tidy::ClangTidyContext*);
15+
void registerMatchers(clang::ast_matchers::MatchFinder*) override;
16+
void check(const clang::ast_matchers::MatchFinder::MatchResult&) override;
17+
};
18+
} // namespace libcpp

0 commit comments

Comments
 (0)