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(Name: "&" ), argumentCountIs(N: 1), isTypeDependent()), |
40 | unless(hasUnaryOperand(InnerMatcher: dependentScopeDeclRefExpr()))) |
41 | .bind(ID: "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 |
57 | |