aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/clang16/tools/extra/clang-tidy/misc/ThrowByValueCatchByReferenceCheck.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/misc/ThrowByValueCatchByReferenceCheck.cpp
parent9685917341315774aad5733b1793b1e533a88bbb (diff)
downloadydb-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.cpp171
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