|
| 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 |
0 commit comments