diff options
author | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 13:58:24 +0300 |
---|---|---|
committer | thegeorg <thegeorg@yandex-team.com> | 2024-03-13 14:11:53 +0300 |
commit | 11a895b7e15d1c5a1f52706396b82e3f9db953cb (patch) | |
tree | fabc6d883b0f946151f61ae7865cee9f529a1fdd /contrib/libs/clang16/tools/extra/clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp | |
parent | 9685917341315774aad5733b1793b1e533a88bbb (diff) | |
download | ydb-11a895b7e15d1c5a1f52706396b82e3f9db953cb.tar.gz |
Export clang-format16 via ydblib project
6e6be3a95868fde888d801b7590af4044049563f
Diffstat (limited to 'contrib/libs/clang16/tools/extra/clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp')
-rw-r--r-- | contrib/libs/clang16/tools/extra/clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp | 171 |
1 files changed, 171 insertions, 0 deletions
diff --git a/contrib/libs/clang16/tools/extra/clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp b/contrib/libs/clang16/tools/extra/clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp new file mode 100644 index 0000000000..abe7bed9df --- /dev/null +++ b/contrib/libs/clang16/tools/extra/clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp @@ -0,0 +1,171 @@ +//===--- ThrowByValueCatchByReferenceCheck.cpp - clang-tidy----------------===// +// +// 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 "ThrowByValueCatchByReferenceCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/OperationKinds.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::misc { + +ThrowByValueCatchByReferenceCheck::ThrowByValueCatchByReferenceCheck( + StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + CheckAnonymousTemporaries(Options.get("CheckThrowTemporaries", true)), + WarnOnLargeObject(Options.get("WarnOnLargeObject", false)), + // Cannot access `ASTContext` from here so set it to an extremal value. + MaxSizeOptions( + Options.get("MaxSize", std::numeric_limits<uint64_t>::max())), + MaxSize(MaxSizeOptions) {} + +void ThrowByValueCatchByReferenceCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(cxxThrowExpr().bind("throw"), this); + Finder->addMatcher(cxxCatchStmt().bind("catch"), this); +} + +void ThrowByValueCatchByReferenceCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "CheckThrowTemporaries", true); + Options.store(Opts, "WarnOnLargeObjects", WarnOnLargeObject); + Options.store(Opts, "MaxSize", MaxSizeOptions); +} + +void ThrowByValueCatchByReferenceCheck::check( + const MatchFinder::MatchResult &Result) { + diagnoseThrowLocations(Result.Nodes.getNodeAs<CXXThrowExpr>("throw")); + diagnoseCatchLocations(Result.Nodes.getNodeAs<CXXCatchStmt>("catch"), + *Result.Context); +} + +bool ThrowByValueCatchByReferenceCheck::isFunctionParameter( + const DeclRefExpr *DeclRefExpr) { + return isa<ParmVarDecl>(DeclRefExpr->getDecl()); +} + +bool ThrowByValueCatchByReferenceCheck::isCatchVariable( + const DeclRefExpr *DeclRefExpr) { + auto *ValueDecl = DeclRefExpr->getDecl(); + if (auto *VarDecl = dyn_cast<clang::VarDecl>(ValueDecl)) + return VarDecl->isExceptionVariable(); + return false; +} + +bool ThrowByValueCatchByReferenceCheck::isFunctionOrCatchVar( + const DeclRefExpr *DeclRefExpr) { + return isFunctionParameter(DeclRefExpr) || isCatchVariable(DeclRefExpr); +} + +void ThrowByValueCatchByReferenceCheck::diagnoseThrowLocations( + const CXXThrowExpr *ThrowExpr) { + if (!ThrowExpr) + return; + auto *SubExpr = ThrowExpr->getSubExpr(); + if (!SubExpr) + return; + auto QualType = SubExpr->getType(); + if (QualType->isPointerType()) { + // The code is throwing a pointer. + // In case it is string literal, it is safe and we return. + auto *Inner = SubExpr->IgnoreParenImpCasts(); + if (isa<StringLiteral>(Inner)) + return; + // If it's a variable from a catch statement, we return as well. + auto *DeclRef = dyn_cast<DeclRefExpr>(Inner); + if (DeclRef && isCatchVariable(DeclRef)) { + return; + } + diag(SubExpr->getBeginLoc(), "throw expression throws a pointer; it should " + "throw a non-pointer value instead"); + } + // If the throw statement does not throw by pointer then it throws by value + // which is ok. + // There are addition checks that emit diagnosis messages if the thrown value + // is not an RValue. See: + // https://www.securecoding.cert.org/confluence/display/cplusplus/ERR09-CPP.+Throw+anonymous+temporaries + // This behavior can be influenced by an option. + + // If we encounter a CXXThrowExpr, we move through all casts until you either + // encounter a DeclRefExpr or a CXXConstructExpr. + // If it's a DeclRefExpr, we emit a message if the referenced variable is not + // a catch variable or function parameter. + // When encountering a CopyOrMoveConstructor: emit message if after casts, + // the expression is a LValue + if (CheckAnonymousTemporaries) { + bool Emit = false; + auto *CurrentSubExpr = SubExpr->IgnoreImpCasts(); + const auto *VariableReference = dyn_cast<DeclRefExpr>(CurrentSubExpr); + const auto *ConstructorCall = dyn_cast<CXXConstructExpr>(CurrentSubExpr); + // If we have a DeclRefExpr, we flag for emitting a diagnosis message in + // case the referenced variable is neither a function parameter nor a + // variable declared in the catch statement. + if (VariableReference) + Emit = !isFunctionOrCatchVar(VariableReference); + else if (ConstructorCall && + ConstructorCall->getConstructor()->isCopyOrMoveConstructor()) { + // If we have a copy / move construction, we emit a diagnosis message if + // the object that we copy construct from is neither a function parameter + // nor a variable declared in a catch statement + auto ArgIter = + ConstructorCall + ->arg_begin(); // there's only one for copy constructors + auto *CurrentSubExpr = (*ArgIter)->IgnoreImpCasts(); + if (CurrentSubExpr->isLValue()) { + if (auto *Tmp = dyn_cast<DeclRefExpr>(CurrentSubExpr)) + Emit = !isFunctionOrCatchVar(Tmp); + else if (isa<CallExpr>(CurrentSubExpr)) + Emit = true; + } + } + if (Emit) + diag(SubExpr->getBeginLoc(), + "throw expression should throw anonymous temporary values instead"); + } +} + +void ThrowByValueCatchByReferenceCheck::diagnoseCatchLocations( + const CXXCatchStmt *CatchStmt, ASTContext &Context) { + if (!CatchStmt) + return; + auto CaughtType = CatchStmt->getCaughtType(); + if (CaughtType.isNull()) + return; + auto *VarDecl = CatchStmt->getExceptionDecl(); + if (const auto *PT = CaughtType.getCanonicalType()->getAs<PointerType>()) { + const char *DiagMsgCatchReference = + "catch handler catches a pointer value; " + "should throw a non-pointer value and " + "catch by reference instead"; + // We do not diagnose when catching pointer to strings since we also allow + // throwing string literals. + if (!PT->getPointeeType()->isAnyCharacterType()) + diag(VarDecl->getBeginLoc(), DiagMsgCatchReference); + } else if (!CaughtType->isReferenceType()) { + const char *DiagMsgCatchReference = "catch handler catches by value; " + "should catch by reference instead"; + // If it's not a pointer and not a reference then it must be caught "by + // value". In this case we should emit a diagnosis message unless the type + // is trivial. + if (!CaughtType.isTrivialType(Context)) { + diag(VarDecl->getBeginLoc(), DiagMsgCatchReference); + } else if (WarnOnLargeObject) { + // If the type is trivial, then catching it by reference is not dangerous. + // However, catching large objects by value decreases the performance. + + // We can now access `ASTContext` so if `MaxSize` is an extremal value + // then set it to the size of `size_t`. + if (MaxSize == std::numeric_limits<uint64_t>::max()) + MaxSize = Context.getTypeSize(Context.getSizeType()); + if (Context.getTypeSize(CaughtType) > MaxSize) + diag(VarDecl->getBeginLoc(), DiagMsgCatchReference); + } + } +} + +} // namespace clang::tidy::misc |