aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/clang16/tools/extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp
diff options
context:
space:
mode:
authorthegeorg <thegeorg@yandex-team.com>2024-03-13 13:58:24 +0300
committerthegeorg <thegeorg@yandex-team.com>2024-03-13 14:11:53 +0300
commit11a895b7e15d1c5a1f52706396b82e3f9db953cb (patch)
treefabc6d883b0f946151f61ae7865cee9f529a1fdd /contrib/libs/clang16/tools/extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp
parent9685917341315774aad5733b1793b1e533a88bbb (diff)
downloadydb-11a895b7e15d1c5a1f52706396b82e3f9db953cb.tar.gz
Export clang-format16 via ydblib project
6e6be3a95868fde888d801b7590af4044049563f
Diffstat (limited to 'contrib/libs/clang16/tools/extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp')
-rw-r--r--contrib/libs/clang16/tools/extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp258
1 files changed, 258 insertions, 0 deletions
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp
new file mode 100644
index 0000000000..b7dcaf2016
--- /dev/null
+++ b/contrib/libs/clang16/tools/extra/clang-tidy/google/AvoidCStyleCastsCheck.cpp
@@ -0,0 +1,258 @@
+//===--- AvoidCStyleCastsCheck.cpp - clang-tidy -----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "AvoidCStyleCastsCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::google::readability {
+
+void AvoidCStyleCastsCheck::registerMatchers(
+ ast_matchers::MatchFinder *Finder) {
+ Finder->addMatcher(
+ cStyleCastExpr(
+ // Filter out (EnumType)IntegerLiteral construct, which is generated
+ // for non-type template arguments of enum types.
+ // FIXME: Remove this once this is fixed in the AST.
+ unless(hasParent(substNonTypeTemplateParmExpr())),
+ // Avoid matches in template instantiations.
+ unless(isInTemplateInstantiation()))
+ .bind("cast"),
+ this);
+ Finder->addMatcher(
+ cxxFunctionalCastExpr(unless(hasDescendant(cxxConstructExpr())),
+ unless(hasDescendant(initListExpr())))
+ .bind("cast"),
+ this);
+}
+
+static bool needsConstCast(QualType SourceType, QualType DestType) {
+ while ((SourceType->isPointerType() && DestType->isPointerType()) ||
+ (SourceType->isReferenceType() && DestType->isReferenceType())) {
+ SourceType = SourceType->getPointeeType();
+ DestType = DestType->getPointeeType();
+ if (SourceType.isConstQualified() && !DestType.isConstQualified()) {
+ return (SourceType->isPointerType() == DestType->isPointerType()) &&
+ (SourceType->isReferenceType() == DestType->isReferenceType());
+ }
+ }
+ return false;
+}
+
+static bool pointedUnqualifiedTypesAreEqual(QualType T1, QualType T2) {
+ while ((T1->isPointerType() && T2->isPointerType()) ||
+ (T1->isReferenceType() && T2->isReferenceType())) {
+ T1 = T1->getPointeeType();
+ T2 = T2->getPointeeType();
+ }
+ return T1.getUnqualifiedType() == T2.getUnqualifiedType();
+}
+
+static clang::CharSourceRange getReplaceRange(const ExplicitCastExpr *Expr) {
+ if (const auto *CastExpr = dyn_cast<CStyleCastExpr>(Expr)) {
+ return CharSourceRange::getCharRange(
+ CastExpr->getLParenLoc(),
+ CastExpr->getSubExprAsWritten()->getBeginLoc());
+ } else if (const auto *CastExpr = dyn_cast<CXXFunctionalCastExpr>(Expr)) {
+ return CharSourceRange::getCharRange(CastExpr->getBeginLoc(),
+ CastExpr->getLParenLoc());
+ } else
+ llvm_unreachable("Unsupported CastExpr");
+}
+
+static StringRef getDestTypeString(const SourceManager &SM,
+ const LangOptions &LangOpts,
+ const ExplicitCastExpr *Expr) {
+ SourceLocation BeginLoc;
+ SourceLocation EndLoc;
+
+ if (const auto *CastExpr = dyn_cast<CStyleCastExpr>(Expr)) {
+ BeginLoc = CastExpr->getLParenLoc().getLocWithOffset(1);
+ EndLoc = CastExpr->getRParenLoc().getLocWithOffset(-1);
+ } else if (const auto *CastExpr = dyn_cast<CXXFunctionalCastExpr>(Expr)) {
+ BeginLoc = CastExpr->getBeginLoc();
+ EndLoc = CastExpr->getLParenLoc().getLocWithOffset(-1);
+ } else
+ llvm_unreachable("Unsupported CastExpr");
+
+ return Lexer::getSourceText(CharSourceRange::getTokenRange(BeginLoc, EndLoc),
+ SM, LangOpts);
+}
+
+void AvoidCStyleCastsCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *CastExpr = Result.Nodes.getNodeAs<ExplicitCastExpr>("cast");
+
+ // Ignore casts in macros.
+ if (CastExpr->getExprLoc().isMacroID())
+ return;
+
+ // Casting to void is an idiomatic way to mute "unused variable" and similar
+ // warnings.
+ if (CastExpr->getCastKind() == CK_ToVoid)
+ return;
+
+ auto IsFunction = [](QualType T) {
+ T = T.getCanonicalType().getNonReferenceType();
+ return T->isFunctionType() || T->isFunctionPointerType() ||
+ T->isMemberFunctionPointerType();
+ };
+
+ const QualType DestTypeAsWritten =
+ CastExpr->getTypeAsWritten().getUnqualifiedType();
+ const QualType SourceTypeAsWritten =
+ CastExpr->getSubExprAsWritten()->getType().getUnqualifiedType();
+ const QualType SourceType = SourceTypeAsWritten.getCanonicalType();
+ const QualType DestType = DestTypeAsWritten.getCanonicalType();
+
+ CharSourceRange ReplaceRange = getReplaceRange(CastExpr);
+
+ bool FnToFnCast =
+ IsFunction(SourceTypeAsWritten) && IsFunction(DestTypeAsWritten);
+
+ const bool ConstructorCast = !CastExpr->getTypeAsWritten().hasQualifiers() &&
+ DestTypeAsWritten->isRecordType() &&
+ !DestTypeAsWritten->isElaboratedTypeSpecifier();
+
+ if (CastExpr->getCastKind() == CK_NoOp && !FnToFnCast) {
+ // Function pointer/reference casts may be needed to resolve ambiguities in
+ // case of overloaded functions, so detection of redundant casts is trickier
+ // in this case. Don't emit "redundant cast" warnings for function
+ // pointer/reference types.
+ QualType Src = SourceTypeAsWritten, Dst = DestTypeAsWritten;
+ if (const auto *ElTy = dyn_cast<ElaboratedType>(Src))
+ Src = ElTy->getNamedType();
+ if (const auto *ElTy = dyn_cast<ElaboratedType>(Dst))
+ Dst = ElTy->getNamedType();
+ if (Src == Dst) {
+ diag(CastExpr->getBeginLoc(), "redundant cast to the same type")
+ << FixItHint::CreateRemoval(ReplaceRange);
+ return;
+ }
+ }
+
+ // The rest of this check is only relevant to C++.
+ // We also disable it for Objective-C++.
+ if (!getLangOpts().CPlusPlus || getLangOpts().ObjC)
+ return;
+ // Ignore code inside extern "C" {} blocks.
+ if (!match(expr(hasAncestor(linkageSpecDecl())), *CastExpr, *Result.Context)
+ .empty())
+ return;
+ // Ignore code in .c files and headers included from them, even if they are
+ // compiled as C++.
+ if (getCurrentMainFile().endswith(".c"))
+ return;
+
+ SourceManager &SM = *Result.SourceManager;
+
+ // Ignore code in .c files #included in other files (which shouldn't be done,
+ // but people still do this for test and other purposes).
+ if (SM.getFilename(SM.getSpellingLoc(CastExpr->getBeginLoc())).endswith(".c"))
+ return;
+
+ // Leave type spelling exactly as it was (unlike
+ // getTypeAsWritten().getAsString() which would spell enum types 'enum X').
+ StringRef DestTypeString = getDestTypeString(SM, getLangOpts(), CastExpr);
+
+ auto Diag =
+ diag(CastExpr->getBeginLoc(), "C-style casts are discouraged; use %0");
+
+ auto ReplaceWithCast = [&](std::string CastText) {
+ const Expr *SubExpr = CastExpr->getSubExprAsWritten()->IgnoreImpCasts();
+ if (!isa<ParenExpr>(SubExpr) && !isa<CXXFunctionalCastExpr>(CastExpr)) {
+ CastText.push_back('(');
+ Diag << FixItHint::CreateInsertion(
+ Lexer::getLocForEndOfToken(SubExpr->getEndLoc(), 0, SM,
+ getLangOpts()),
+ ")");
+ }
+ Diag << FixItHint::CreateReplacement(ReplaceRange, CastText);
+ };
+ auto ReplaceWithNamedCast = [&](StringRef CastType) {
+ Diag << CastType;
+ ReplaceWithCast((CastType + "<" + DestTypeString + ">").str());
+ };
+ auto ReplaceWithConstructorCall = [&]() {
+ Diag << "constructor call syntax";
+ // FIXME: Validate DestTypeString, maybe.
+ ReplaceWithCast(DestTypeString.str());
+ };
+ // Suggest appropriate C++ cast. See [expr.cast] for cast notation semantics.
+ switch (CastExpr->getCastKind()) {
+ case CK_FunctionToPointerDecay:
+ ReplaceWithNamedCast("static_cast");
+ return;
+ case CK_ConstructorConversion:
+ if (ConstructorCast) {
+ ReplaceWithConstructorCall();
+ } else {
+ ReplaceWithNamedCast("static_cast");
+ }
+ return;
+ case CK_NoOp:
+ if (FnToFnCast) {
+ ReplaceWithNamedCast("static_cast");
+ return;
+ }
+ if (SourceType == DestType) {
+ Diag << "static_cast (if needed, the cast may be redundant)";
+ ReplaceWithCast(("static_cast<" + DestTypeString + ">").str());
+ return;
+ }
+ if (needsConstCast(SourceType, DestType) &&
+ pointedUnqualifiedTypesAreEqual(SourceType, DestType)) {
+ ReplaceWithNamedCast("const_cast");
+ return;
+ }
+ if (ConstructorCast) {
+ ReplaceWithConstructorCall();
+ return;
+ }
+ if (DestType->isReferenceType()) {
+ QualType Dest = DestType.getNonReferenceType();
+ QualType Source = SourceType.getNonReferenceType();
+ if (Source == Dest.withConst() ||
+ SourceType.getNonReferenceType() == DestType.getNonReferenceType()) {
+ ReplaceWithNamedCast("const_cast");
+ return;
+ }
+ break;
+ }
+ [[fallthrough]];
+ case clang::CK_IntegralCast:
+ // Convert integral and no-op casts between builtin types and enums to
+ // static_cast. A cast from enum to integer may be unnecessary, but it's
+ // still retained.
+ if ((SourceType->isBuiltinType() || SourceType->isEnumeralType()) &&
+ (DestType->isBuiltinType() || DestType->isEnumeralType())) {
+ ReplaceWithNamedCast("static_cast");
+ return;
+ }
+ break;
+ case CK_BitCast:
+ // FIXME: Suggest const_cast<...>(reinterpret_cast<...>(...)) replacement.
+ if (!needsConstCast(SourceType, DestType)) {
+ if (SourceType->isVoidPointerType())
+ ReplaceWithNamedCast("static_cast");
+ else
+ ReplaceWithNamedCast("reinterpret_cast");
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ Diag << "static_cast/const_cast/reinterpret_cast";
+}
+
+} // namespace clang::tidy::google::readability