aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit
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/lib/StaticAnalyzer/Checkers/WebKit
parent9685917341315774aad5733b1793b1e533a88bbb (diff)
downloadydb-11a895b7e15d1c5a1f52706396b82e3f9db953cb.tar.gz
Export clang-format16 via ydblib project
6e6be3a95868fde888d801b7590af4044049563f
Diffstat (limited to 'contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit')
-rw-r--r--contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp94
-rw-r--r--contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h80
-rw-r--r--contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/DiagOutputUtils.h36
-rw-r--r--contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp159
-rw-r--r--contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp197
-rw-r--r--contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h62
-rw-r--r--contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp163
-rw-r--r--contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp196
-rw-r--r--contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp108
-rw-r--r--contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp252
10 files changed, 1347 insertions, 0 deletions
diff --git a/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
new file mode 100644
index 0000000000..64028b2770
--- /dev/null
+++ b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -0,0 +1,94 @@
+//=======- ASTUtils.cpp ------------------------------------------*- 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 "ASTUtils.h"
+#include "PtrTypesSemantics.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+#include <optional>
+
+namespace clang {
+
+std::pair<const Expr *, bool>
+tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
+ while (E) {
+ if (auto *cast = dyn_cast<CastExpr>(E)) {
+ if (StopAtFirstRefCountedObj) {
+ if (auto *ConversionFunc =
+ dyn_cast_or_null<FunctionDecl>(cast->getConversionFunction())) {
+ if (isCtorOfRefCounted(ConversionFunc))
+ return {E, true};
+ }
+ }
+ // FIXME: This can give false "origin" that would lead to false negatives
+ // in checkers. See https://reviews.llvm.org/D37023 for reference.
+ E = cast->getSubExpr();
+ continue;
+ }
+ if (auto *call = dyn_cast<CallExpr>(E)) {
+ if (auto *memberCall = dyn_cast<CXXMemberCallExpr>(call)) {
+ std::optional<bool> IsGetterOfRefCt = isGetterOfRefCounted(memberCall->getMethodDecl());
+ if (IsGetterOfRefCt && *IsGetterOfRefCt) {
+ E = memberCall->getImplicitObjectArgument();
+ if (StopAtFirstRefCountedObj) {
+ return {E, true};
+ }
+ continue;
+ }
+ }
+
+ if (auto *operatorCall = dyn_cast<CXXOperatorCallExpr>(E)) {
+ if (operatorCall->getNumArgs() == 1) {
+ E = operatorCall->getArg(0);
+ continue;
+ }
+ }
+
+ if (auto *callee = call->getDirectCallee()) {
+ if (isCtorOfRefCounted(callee)) {
+ if (StopAtFirstRefCountedObj)
+ return {E, true};
+
+ E = call->getArg(0);
+ continue;
+ }
+
+ if (isPtrConversion(callee)) {
+ E = call->getArg(0);
+ continue;
+ }
+ }
+ }
+ if (auto *unaryOp = dyn_cast<UnaryOperator>(E)) {
+ // FIXME: Currently accepts ANY unary operator. Is it OK?
+ E = unaryOp->getSubExpr();
+ continue;
+ }
+
+ break;
+ }
+ // Some other expression.
+ return {E, false};
+}
+
+bool isASafeCallArg(const Expr *E) {
+ assert(E);
+ if (auto *Ref = dyn_cast<DeclRefExpr>(E)) {
+ if (auto *D = dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
+ if (isa<ParmVarDecl>(D) || D->isLocalVarDecl())
+ return true;
+ }
+ }
+
+ // TODO: checker for method calls on non-refcounted objects
+ return isa<CXXThisExpr>(E);
+}
+
+} // namespace clang
diff --git a/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
new file mode 100644
index 0000000000..e35ea4ef05
--- /dev/null
+++ b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
@@ -0,0 +1,80 @@
+//=======- ASTUtis.h ---------------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYZER_WEBKIT_ASTUTILS_H
+#define LLVM_CLANG_ANALYZER_WEBKIT_ASTUTILS_H
+
+#include "clang/AST/Decl.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/Support/Casting.h"
+
+#include <string>
+#include <utility>
+
+namespace clang {
+class Expr;
+
+/// This function de-facto defines a set of transformations that we consider
+/// safe (in heuristical sense). These transformation if passed a safe value as
+/// an input should provide a safe value (or an object that provides safe
+/// values).
+///
+/// For more context see Static Analyzer checkers documentation - specifically
+/// webkit.UncountedCallArgsChecker checker. Allowed list of transformations:
+/// - constructors of ref-counted types (including factory methods)
+/// - getters of ref-counted types
+/// - member overloaded operators
+/// - casts
+/// - unary operators like ``&`` or ``*``
+///
+/// If passed expression is of type uncounted pointer/reference we try to find
+/// the "origin" of the pointer value.
+/// Origin can be for example a local variable, nullptr, constant or
+/// this-pointer.
+///
+/// Certain subexpression nodes represent transformations that don't affect
+/// where the memory address originates from. We try to traverse such
+/// subexpressions to get to the relevant child nodes. Whenever we encounter a
+/// subexpression that either can't be ignored, we don't model its semantics or
+/// that has multiple children we stop.
+///
+/// \p E is an expression of uncounted pointer/reference type.
+/// If \p StopAtFirstRefCountedObj is true and we encounter a subexpression that
+/// represents ref-counted object during the traversal we return relevant
+/// sub-expression and true.
+///
+/// \returns subexpression that we traversed to and if \p
+/// StopAtFirstRefCountedObj is true we also return whether we stopped early.
+std::pair<const clang::Expr *, bool>
+tryToFindPtrOrigin(const clang::Expr *E, bool StopAtFirstRefCountedObj);
+
+/// For \p E referring to a ref-countable/-counted pointer/reference we return
+/// whether it's a safe call argument. Examples: function parameter or
+/// this-pointer. The logic relies on the set of recursive rules we enforce for
+/// WebKit codebase.
+///
+/// \returns Whether \p E is a safe call arugment.
+bool isASafeCallArg(const clang::Expr *E);
+
+/// \returns name of AST node or empty string.
+template <typename T> std::string safeGetName(const T *ASTNode) {
+ const auto *const ND = llvm::dyn_cast_or_null<clang::NamedDecl>(ASTNode);
+ if (!ND)
+ return "";
+
+ // In case F is for example "operator|" the getName() method below would
+ // assert.
+ if (!ND->getDeclName().isIdentifier())
+ return "";
+
+ return ND->getName().str();
+}
+
+} // namespace clang
+
+#endif
diff --git a/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/DiagOutputUtils.h b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/DiagOutputUtils.h
new file mode 100644
index 0000000000..781a8d7460
--- /dev/null
+++ b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/DiagOutputUtils.h
@@ -0,0 +1,36 @@
+//=======- DiagOutputUtils.h -------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYZER_WEBKIT_DIAGPRINTUTILS_H
+#define LLVM_CLANG_ANALYZER_WEBKIT_DIAGPRINTUTILS_H
+
+#include "clang/AST/Decl.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+
+template <typename NamedDeclDerivedT>
+void printQuotedQualifiedName(llvm::raw_ostream &Os,
+ const NamedDeclDerivedT &D) {
+ Os << "'";
+ D->getNameForDiagnostic(Os, D->getASTContext().getPrintingPolicy(),
+ /*Qualified=*/true);
+ Os << "'";
+}
+
+template <typename NamedDeclDerivedT>
+void printQuotedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D) {
+ Os << "'";
+ D->getNameForDiagnostic(Os, D->getASTContext().getPrintingPolicy(),
+ /*Qualified=*/false);
+ Os << "'";
+}
+
+} // namespace clang
+
+#endif
diff --git a/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp
new file mode 100644
index 0000000000..66d8588e25
--- /dev/null
+++ b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp
@@ -0,0 +1,159 @@
+//=======- NoUncountedMembersChecker.cpp -------------------------*- 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 "ASTUtils.h"
+#include "DiagOutputUtils.h"
+#include "PtrTypesSemantics.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/Support/Casting.h"
+#include <optional>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class NoUncountedMemberChecker
+ : public Checker<check::ASTDecl<TranslationUnitDecl>> {
+private:
+ BugType Bug;
+ mutable BugReporter *BR;
+
+public:
+ NoUncountedMemberChecker()
+ : Bug(this,
+ "Member variable is a raw-poiner/reference to reference-countable "
+ "type",
+ "WebKit coding guidelines") {}
+
+ void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
+ BugReporter &BRArg) const {
+ BR = &BRArg;
+
+ // The calls to checkAST* from AnalysisConsumer don't
+ // visit template instantiations or lambda classes. We
+ // want to visit those, so we make our own RecursiveASTVisitor.
+ struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
+ const NoUncountedMemberChecker *Checker;
+ explicit LocalVisitor(const NoUncountedMemberChecker *Checker)
+ : Checker(Checker) {
+ assert(Checker);
+ }
+
+ bool shouldVisitTemplateInstantiations() const { return true; }
+ bool shouldVisitImplicitCode() const { return false; }
+
+ bool VisitRecordDecl(const RecordDecl *RD) {
+ Checker->visitRecordDecl(RD);
+ return true;
+ }
+ };
+
+ LocalVisitor visitor(this);
+ visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
+ }
+
+ void visitRecordDecl(const RecordDecl *RD) const {
+ if (shouldSkipDecl(RD))
+ return;
+
+ for (auto *Member : RD->fields()) {
+ const Type *MemberType = Member->getType().getTypePtrOrNull();
+ if (!MemberType)
+ continue;
+
+ if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) {
+ // If we don't see the definition we just don't know.
+ if (MemberCXXRD->hasDefinition()) {
+ std::optional<bool> isRCAble = isRefCountable(MemberCXXRD);
+ if (isRCAble && *isRCAble)
+ reportBug(Member, MemberType, MemberCXXRD, RD);
+ }
+ }
+ }
+ }
+
+ bool shouldSkipDecl(const RecordDecl *RD) const {
+ if (!RD->isThisDeclarationADefinition())
+ return true;
+
+ if (RD->isImplicit())
+ return true;
+
+ if (RD->isLambda())
+ return true;
+
+ // If the construct doesn't have a source file, then it's not something
+ // we want to diagnose.
+ const auto RDLocation = RD->getLocation();
+ if (!RDLocation.isValid())
+ return true;
+
+ const auto Kind = RD->getTagKind();
+ // FIMXE: Should we check union members too?
+ if (Kind != TTK_Struct && Kind != TTK_Class)
+ return true;
+
+ // Ignore CXXRecords that come from system headers.
+ if (BR->getSourceManager().isInSystemHeader(RDLocation))
+ return true;
+
+ // Ref-counted smartpointers actually have raw-pointer to uncounted type as
+ // a member but we trust them to handle it correctly.
+ auto CXXRD = llvm::dyn_cast_or_null<CXXRecordDecl>(RD);
+ if (CXXRD)
+ return isRefCounted(CXXRD);
+
+ return false;
+ }
+
+ void reportBug(const FieldDecl *Member, const Type *MemberType,
+ const CXXRecordDecl *MemberCXXRD,
+ const RecordDecl *ClassCXXRD) const {
+ assert(Member);
+ assert(MemberType);
+ assert(MemberCXXRD);
+
+ SmallString<100> Buf;
+ llvm::raw_svector_ostream Os(Buf);
+
+ Os << "Member variable ";
+ printQuotedName(Os, Member);
+ Os << " in ";
+ printQuotedQualifiedName(Os, ClassCXXRD);
+ Os << " is a "
+ << (isa<PointerType>(MemberType) ? "raw pointer" : "reference")
+ << " to ref-countable type ";
+ printQuotedQualifiedName(Os, MemberCXXRD);
+ Os << "; member variables must be ref-counted.";
+
+ PathDiagnosticLocation BSLoc(Member->getSourceRange().getBegin(),
+ BR->getSourceManager());
+ auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+ Report->addRange(Member->getSourceRange());
+ BR->emitReport(std::move(Report));
+ }
+};
+} // namespace
+
+void ento::registerNoUncountedMemberChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<NoUncountedMemberChecker>();
+}
+
+bool ento::shouldRegisterNoUncountedMemberChecker(
+ const CheckerManager &Mgr) {
+ return true;
+}
diff --git a/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
new file mode 100644
index 0000000000..9b1d7ae3e6
--- /dev/null
+++ b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -0,0 +1,197 @@
+//=======- PtrTypesSemantics.cpp ---------------------------------*- 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 "PtrTypesSemantics.h"
+#include "ASTUtils.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+#include <optional>
+
+using namespace clang;
+
+namespace {
+
+bool hasPublicRefAndDeref(const CXXRecordDecl *R) {
+ assert(R);
+ assert(R->hasDefinition());
+
+ bool hasRef = false;
+ bool hasDeref = false;
+ for (const CXXMethodDecl *MD : R->methods()) {
+ const auto MethodName = safeGetName(MD);
+
+ if (MethodName == "ref" && MD->getAccess() == AS_public) {
+ if (hasDeref)
+ return true;
+ hasRef = true;
+ } else if (MethodName == "deref" && MD->getAccess() == AS_public) {
+ if (hasRef)
+ return true;
+ hasDeref = true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+namespace clang {
+
+std::optional<const clang::CXXRecordDecl*>
+isRefCountable(const CXXBaseSpecifier* Base)
+{
+ assert(Base);
+
+ const Type *T = Base->getType().getTypePtrOrNull();
+ if (!T)
+ return std::nullopt;
+
+ const CXXRecordDecl *R = T->getAsCXXRecordDecl();
+ if (!R)
+ return std::nullopt;
+ if (!R->hasDefinition())
+ return std::nullopt;
+
+ return hasPublicRefAndDeref(R) ? R : nullptr;
+}
+
+std::optional<bool> isRefCountable(const CXXRecordDecl* R)
+{
+ assert(R);
+
+ R = R->getDefinition();
+ if (!R)
+ return std::nullopt;
+
+ if (hasPublicRefAndDeref(R))
+ return true;
+
+ CXXBasePaths Paths;
+ Paths.setOrigin(const_cast<CXXRecordDecl *>(R));
+
+ bool AnyInconclusiveBase = false;
+ const auto isRefCountableBase =
+ [&AnyInconclusiveBase](const CXXBaseSpecifier* Base, CXXBasePath&) {
+ std::optional<const clang::CXXRecordDecl*> IsRefCountable = clang::isRefCountable(Base);
+ if (!IsRefCountable) {
+ AnyInconclusiveBase = true;
+ return false;
+ }
+ return (*IsRefCountable) != nullptr;
+ };
+
+ bool BasesResult = R->lookupInBases(isRefCountableBase, Paths,
+ /*LookupInDependent =*/true);
+ if (AnyInconclusiveBase)
+ return std::nullopt;
+
+ return BasesResult;
+}
+
+bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
+ assert(F);
+ const auto &FunctionName = safeGetName(F);
+
+ return FunctionName == "Ref" || FunctionName == "makeRef"
+
+ || FunctionName == "RefPtr" || FunctionName == "makeRefPtr"
+
+ || FunctionName == "UniqueRef" || FunctionName == "makeUniqueRef" ||
+ FunctionName == "makeUniqueRefWithoutFastMallocCheck"
+
+ || FunctionName == "String" || FunctionName == "AtomString" ||
+ FunctionName == "UniqueString"
+ // FIXME: Implement as attribute.
+ || FunctionName == "Identifier";
+}
+
+std::optional<bool> isUncounted(const CXXRecordDecl* Class)
+{
+ // Keep isRefCounted first as it's cheaper.
+ if (isRefCounted(Class))
+ return false;
+
+ std::optional<bool> IsRefCountable = isRefCountable(Class);
+ if (!IsRefCountable)
+ return std::nullopt;
+
+ return (*IsRefCountable);
+}
+
+std::optional<bool> isUncountedPtr(const Type* T)
+{
+ assert(T);
+
+ if (T->isPointerType() || T->isReferenceType()) {
+ if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
+ return isUncounted(CXXRD);
+ }
+ }
+ return false;
+}
+
+std::optional<bool> isGetterOfRefCounted(const CXXMethodDecl* M)
+{
+ assert(M);
+
+ if (isa<CXXMethodDecl>(M)) {
+ const CXXRecordDecl *calleeMethodsClass = M->getParent();
+ auto className = safeGetName(calleeMethodsClass);
+ auto methodName = safeGetName(M);
+
+ if (((className == "Ref" || className == "RefPtr") &&
+ methodName == "get") ||
+ ((className == "String" || className == "AtomString" ||
+ className == "AtomStringImpl" || className == "UniqueString" ||
+ className == "UniqueStringImpl" || className == "Identifier") &&
+ methodName == "impl"))
+ return true;
+
+ // Ref<T> -> T conversion
+ // FIXME: Currently allowing any Ref<T> -> whatever cast.
+ if (className == "Ref" || className == "RefPtr") {
+ if (auto *maybeRefToRawOperator = dyn_cast<CXXConversionDecl>(M)) {
+ if (auto *targetConversionType =
+ maybeRefToRawOperator->getConversionType().getTypePtrOrNull()) {
+ return isUncountedPtr(targetConversionType);
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool isRefCounted(const CXXRecordDecl *R) {
+ assert(R);
+ if (auto *TmplR = R->getTemplateInstantiationPattern()) {
+ // FIXME: String/AtomString/UniqueString
+ const auto &ClassName = safeGetName(TmplR);
+ return ClassName == "RefPtr" || ClassName == "Ref";
+ }
+ return false;
+}
+
+bool isPtrConversion(const FunctionDecl *F) {
+ assert(F);
+ if (isCtorOfRefCounted(F))
+ return true;
+
+ // FIXME: check # of params == 1
+ const auto FunctionName = safeGetName(F);
+ if (FunctionName == "getPtr" || FunctionName == "WeakPtr" ||
+ FunctionName == "makeWeakPtr"
+
+ || FunctionName == "downcast" || FunctionName == "bitwise_cast")
+ return true;
+
+ return false;
+}
+
+} // namespace clang
diff --git a/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
new file mode 100644
index 0000000000..91e3ccf2ec
--- /dev/null
+++ b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ -0,0 +1,62 @@
+//=======- PtrTypesSemantics.cpp ---------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYZER_WEBKIT_PTRTYPESEMANTICS_H
+#define LLVM_CLANG_ANALYZER_WEBKIT_PTRTYPESEMANTICS_H
+
+#include "llvm/ADT/APInt.h"
+#include <optional>
+
+namespace clang {
+class CXXBaseSpecifier;
+class CXXMethodDecl;
+class CXXRecordDecl;
+class FunctionDecl;
+class Type;
+
+// Ref-countability of a type is implicitly defined by Ref<T> and RefPtr<T>
+// implementation. It can be modeled as: type T having public methods ref() and
+// deref()
+
+// In WebKit there are two ref-counted templated smart pointers: RefPtr<T> and
+// Ref<T>.
+
+/// \returns CXXRecordDecl of the base if the type is ref-countable, nullptr if
+/// not, std::nullopt if inconclusive.
+std::optional<const clang::CXXRecordDecl*>
+isRefCountable(const clang::CXXBaseSpecifier* Base);
+
+/// \returns true if \p Class is ref-countable, false if not, std::nullopt if
+/// inconclusive.
+std::optional<bool> isRefCountable(const clang::CXXRecordDecl* Class);
+
+/// \returns true if \p Class is ref-counted, false if not.
+bool isRefCounted(const clang::CXXRecordDecl *Class);
+
+/// \returns true if \p Class is ref-countable AND not ref-counted, false if
+/// not, std::nullopt if inconclusive.
+std::optional<bool> isUncounted(const clang::CXXRecordDecl* Class);
+
+/// \returns true if \p T is either a raw pointer or reference to an uncounted
+/// class, false if not, std::nullopt if inconclusive.
+std::optional<bool> isUncountedPtr(const clang::Type* T);
+
+/// \returns true if \p F creates ref-countable object from uncounted parameter,
+/// false if not.
+bool isCtorOfRefCounted(const clang::FunctionDecl *F);
+
+/// \returns true if \p M is getter of a ref-counted class, false if not.
+std::optional<bool> isGetterOfRefCounted(const clang::CXXMethodDecl* Method);
+
+/// \returns true if \p F is a conversion between ref-countable or ref-counted
+/// pointer types.
+bool isPtrConversion(const FunctionDecl *F);
+
+} // namespace clang
+
+#endif
diff --git a/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
new file mode 100644
index 0000000000..48dcfc4a3c
--- /dev/null
+++ b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
@@ -0,0 +1,163 @@
+//=======- RefCntblBaseVirtualDtor.cpp ---------------------------*- 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 "DiagOutputUtils.h"
+#include "PtrTypesSemantics.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include <optional>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class RefCntblBaseVirtualDtorChecker
+ : public Checker<check::ASTDecl<TranslationUnitDecl>> {
+private:
+ BugType Bug;
+ mutable BugReporter *BR;
+
+public:
+ RefCntblBaseVirtualDtorChecker()
+ : Bug(this,
+ "Reference-countable base class doesn't have virtual destructor",
+ "WebKit coding guidelines") {}
+
+ void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
+ BugReporter &BRArg) const {
+ BR = &BRArg;
+
+ // The calls to checkAST* from AnalysisConsumer don't
+ // visit template instantiations or lambda classes. We
+ // want to visit those, so we make our own RecursiveASTVisitor.
+ struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
+ const RefCntblBaseVirtualDtorChecker *Checker;
+ explicit LocalVisitor(const RefCntblBaseVirtualDtorChecker *Checker)
+ : Checker(Checker) {
+ assert(Checker);
+ }
+
+ bool shouldVisitTemplateInstantiations() const { return true; }
+ bool shouldVisitImplicitCode() const { return false; }
+
+ bool VisitCXXRecordDecl(const CXXRecordDecl *RD) {
+ Checker->visitCXXRecordDecl(RD);
+ return true;
+ }
+ };
+
+ LocalVisitor visitor(this);
+ visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
+ }
+
+ void visitCXXRecordDecl(const CXXRecordDecl *RD) const {
+ if (shouldSkipDecl(RD))
+ return;
+
+ CXXBasePaths Paths;
+ Paths.setOrigin(RD);
+
+ const CXXBaseSpecifier *ProblematicBaseSpecifier = nullptr;
+ const CXXRecordDecl *ProblematicBaseClass = nullptr;
+
+ const auto IsPublicBaseRefCntblWOVirtualDtor =
+ [RD, &ProblematicBaseSpecifier,
+ &ProblematicBaseClass](const CXXBaseSpecifier *Base, CXXBasePath &) {
+ const auto AccSpec = Base->getAccessSpecifier();
+ if (AccSpec == AS_protected || AccSpec == AS_private ||
+ (AccSpec == AS_none && RD->isClass()))
+ return false;
+
+ std::optional<const CXXRecordDecl*> RefCntblBaseRD = isRefCountable(Base);
+ if (!RefCntblBaseRD || !(*RefCntblBaseRD))
+ return false;
+
+ const auto *Dtor = (*RefCntblBaseRD)->getDestructor();
+ if (!Dtor || !Dtor->isVirtual()) {
+ ProblematicBaseSpecifier = Base;
+ ProblematicBaseClass = *RefCntblBaseRD;
+ return true;
+ }
+
+ return false;
+ };
+
+ if (RD->lookupInBases(IsPublicBaseRefCntblWOVirtualDtor, Paths,
+ /*LookupInDependent =*/true)) {
+ reportBug(RD, ProblematicBaseSpecifier, ProblematicBaseClass);
+ }
+ }
+
+ bool shouldSkipDecl(const CXXRecordDecl *RD) const {
+ if (!RD->isThisDeclarationADefinition())
+ return true;
+
+ if (RD->isImplicit())
+ return true;
+
+ if (RD->isLambda())
+ return true;
+
+ // If the construct doesn't have a source file, then it's not something
+ // we want to diagnose.
+ const auto RDLocation = RD->getLocation();
+ if (!RDLocation.isValid())
+ return true;
+
+ const auto Kind = RD->getTagKind();
+ if (Kind != TTK_Struct && Kind != TTK_Class)
+ return true;
+
+ // Ignore CXXRecords that come from system headers.
+ if (BR->getSourceManager().getFileCharacteristic(RDLocation) !=
+ SrcMgr::C_User)
+ return true;
+
+ return false;
+ }
+
+ void reportBug(const CXXRecordDecl *DerivedClass,
+ const CXXBaseSpecifier *BaseSpec,
+ const CXXRecordDecl *ProblematicBaseClass) const {
+ assert(DerivedClass);
+ assert(BaseSpec);
+ assert(ProblematicBaseClass);
+
+ SmallString<100> Buf;
+ llvm::raw_svector_ostream Os(Buf);
+
+ Os << (ProblematicBaseClass->isClass() ? "Class" : "Struct") << " ";
+ printQuotedQualifiedName(Os, ProblematicBaseClass);
+
+ Os << " is used as a base of "
+ << (DerivedClass->isClass() ? "class" : "struct") << " ";
+ printQuotedQualifiedName(Os, DerivedClass);
+
+ Os << " but doesn't have virtual destructor";
+
+ PathDiagnosticLocation BSLoc(BaseSpec->getSourceRange().getBegin(),
+ BR->getSourceManager());
+ auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+ Report->addRange(BaseSpec->getSourceRange());
+ BR->emitReport(std::move(Report));
+ }
+};
+} // namespace
+
+void ento::registerRefCntblBaseVirtualDtorChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<RefCntblBaseVirtualDtorChecker>();
+}
+
+bool ento::shouldRegisterRefCntblBaseVirtualDtorChecker(
+ const CheckerManager &mgr) {
+ return true;
+}
diff --git a/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
new file mode 100644
index 0000000000..4ae8c442fa
--- /dev/null
+++ b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -0,0 +1,196 @@
+//=======- UncountedCallArgsChecker.cpp --------------------------*- 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 "ASTUtils.h"
+#include "DiagOutputUtils.h"
+#include "PtrTypesSemantics.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "llvm/ADT/DenseSet.h"
+#include <optional>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class UncountedCallArgsChecker
+ : public Checker<check::ASTDecl<TranslationUnitDecl>> {
+ BugType Bug{this,
+ "Uncounted call argument for a raw pointer/reference parameter",
+ "WebKit coding guidelines"};
+ mutable BugReporter *BR;
+
+public:
+
+ void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
+ BugReporter &BRArg) const {
+ BR = &BRArg;
+
+ // The calls to checkAST* from AnalysisConsumer don't
+ // visit template instantiations or lambda classes. We
+ // want to visit those, so we make our own RecursiveASTVisitor.
+ struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
+ const UncountedCallArgsChecker *Checker;
+ explicit LocalVisitor(const UncountedCallArgsChecker *Checker)
+ : Checker(Checker) {
+ assert(Checker);
+ }
+
+ bool shouldVisitTemplateInstantiations() const { return true; }
+ bool shouldVisitImplicitCode() const { return false; }
+
+ bool VisitCallExpr(const CallExpr *CE) {
+ Checker->visitCallExpr(CE);
+ return true;
+ }
+ };
+
+ LocalVisitor visitor(this);
+ visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
+ }
+
+ void visitCallExpr(const CallExpr *CE) const {
+ if (shouldSkipCall(CE))
+ return;
+
+ if (auto *F = CE->getDirectCallee()) {
+ // Skip the first argument for overloaded member operators (e. g. lambda
+ // or std::function call operator).
+ unsigned ArgIdx = isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
+
+ for (auto P = F->param_begin();
+ // FIXME: Also check variadic function parameters.
+ // FIXME: Also check default function arguments. Probably a different
+ // checker. In case there are default arguments the call can have
+ // fewer arguments than the callee has parameters.
+ P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
+ // TODO: attributes.
+ // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>())
+ // continue;
+
+ const auto *ArgType = (*P)->getType().getTypePtrOrNull();
+ if (!ArgType)
+ continue; // FIXME? Should we bail?
+
+ // FIXME: more complex types (arrays, references to raw pointers, etc)
+ std::optional<bool> IsUncounted = isUncountedPtr(ArgType);
+ if (!IsUncounted || !(*IsUncounted))
+ continue;
+
+ const auto *Arg = CE->getArg(ArgIdx);
+
+ std::pair<const clang::Expr *, bool> ArgOrigin =
+ tryToFindPtrOrigin(Arg, true);
+
+ // Temporary ref-counted object created as part of the call argument
+ // would outlive the call.
+ if (ArgOrigin.second)
+ continue;
+
+ if (isa<CXXNullPtrLiteralExpr>(ArgOrigin.first)) {
+ // foo(nullptr)
+ continue;
+ }
+ if (isa<IntegerLiteral>(ArgOrigin.first)) {
+ // FIXME: Check the value.
+ // foo(NULL)
+ continue;
+ }
+
+ if (isASafeCallArg(ArgOrigin.first))
+ continue;
+
+ reportBug(Arg, *P);
+ }
+ }
+ }
+
+ bool shouldSkipCall(const CallExpr *CE) const {
+ if (CE->getNumArgs() == 0)
+ return false;
+
+ // If an assignment is problematic we should warn about the sole existence
+ // of object on LHS.
+ if (auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
+ // Note: assignemnt to built-in type isn't derived from CallExpr.
+ if (MemberOp->isAssignmentOp())
+ return false;
+ }
+
+ const auto *Callee = CE->getDirectCallee();
+ if (!Callee)
+ return false;
+
+ auto overloadedOperatorType = Callee->getOverloadedOperator();
+ if (overloadedOperatorType == OO_EqualEqual ||
+ overloadedOperatorType == OO_ExclaimEqual ||
+ overloadedOperatorType == OO_LessEqual ||
+ overloadedOperatorType == OO_GreaterEqual ||
+ overloadedOperatorType == OO_Spaceship ||
+ overloadedOperatorType == OO_AmpAmp ||
+ overloadedOperatorType == OO_PipePipe)
+ return true;
+
+ if (isCtorOfRefCounted(Callee))
+ return true;
+
+ auto name = safeGetName(Callee);
+ if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
+ name == "makeWeakPtr" || name == "downcast" || name == "bitwise_cast" ||
+ name == "is" || name == "equal" || name == "hash" ||
+ name == "isType"
+ // FIXME: Most/all of these should be implemented via attributes.
+ || name == "equalIgnoringASCIICase" ||
+ name == "equalIgnoringASCIICaseCommon" ||
+ name == "equalIgnoringNullity")
+ return true;
+
+ return false;
+ }
+
+ void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const {
+ assert(CallArg);
+
+ SmallString<100> Buf;
+ llvm::raw_svector_ostream Os(Buf);
+
+ const std::string paramName = safeGetName(Param);
+ Os << "Call argument";
+ if (!paramName.empty()) {
+ Os << " for parameter ";
+ printQuotedQualifiedName(Os, Param);
+ }
+ Os << " is uncounted and unsafe.";
+
+ const SourceLocation SrcLocToReport =
+ isa<CXXDefaultArgExpr>(CallArg) ? Param->getDefaultArg()->getExprLoc()
+ : CallArg->getSourceRange().getBegin();
+
+ PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
+ auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+ Report->addRange(CallArg->getSourceRange());
+ BR->emitReport(std::move(Report));
+ }
+};
+} // namespace
+
+void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<UncountedCallArgsChecker>();
+}
+
+bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) {
+ return true;
+}
diff --git a/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
new file mode 100644
index 0000000000..004b0b9d39
--- /dev/null
+++ b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/UncountedLambdaCapturesChecker.cpp
@@ -0,0 +1,108 @@
+//=======- UncountedLambdaCapturesChecker.cpp --------------------*- 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 "DiagOutputUtils.h"
+#include "PtrTypesSemantics.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include <optional>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class UncountedLambdaCapturesChecker
+ : public Checker<check::ASTDecl<TranslationUnitDecl>> {
+private:
+ BugType Bug{this, "Lambda capture of uncounted variable",
+ "WebKit coding guidelines"};
+ mutable BugReporter *BR;
+
+public:
+ void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
+ BugReporter &BRArg) const {
+ BR = &BRArg;
+
+ // The calls to checkAST* from AnalysisConsumer don't
+ // visit template instantiations or lambda classes. We
+ // want to visit those, so we make our own RecursiveASTVisitor.
+ struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
+ const UncountedLambdaCapturesChecker *Checker;
+ explicit LocalVisitor(const UncountedLambdaCapturesChecker *Checker)
+ : Checker(Checker) {
+ assert(Checker);
+ }
+
+ bool shouldVisitTemplateInstantiations() const { return true; }
+ bool shouldVisitImplicitCode() const { return false; }
+
+ bool VisitLambdaExpr(LambdaExpr *L) {
+ Checker->visitLambdaExpr(L);
+ return true;
+ }
+ };
+
+ LocalVisitor visitor(this);
+ visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
+ }
+
+ void visitLambdaExpr(LambdaExpr *L) const {
+ for (const LambdaCapture &C : L->captures()) {
+ if (C.capturesVariable()) {
+ ValueDecl *CapturedVar = C.getCapturedVar();
+ if (auto *CapturedVarType = CapturedVar->getType().getTypePtrOrNull()) {
+ std::optional<bool> IsUncountedPtr = isUncountedPtr(CapturedVarType);
+ if (IsUncountedPtr && *IsUncountedPtr) {
+ reportBug(C, CapturedVar, CapturedVarType);
+ }
+ }
+ }
+ }
+ }
+
+ void reportBug(const LambdaCapture &Capture, ValueDecl *CapturedVar,
+ const Type *T) const {
+ assert(CapturedVar);
+
+ SmallString<100> Buf;
+ llvm::raw_svector_ostream Os(Buf);
+
+ if (Capture.isExplicit()) {
+ Os << "Captured ";
+ } else {
+ Os << "Implicitly captured ";
+ }
+ if (T->isPointerType()) {
+ Os << "raw-pointer ";
+ } else {
+ assert(T->isReferenceType());
+ Os << "reference ";
+ }
+
+ printQuotedQualifiedName(Os, Capture.getCapturedVar());
+ Os << " to uncounted type is unsafe.";
+
+ PathDiagnosticLocation BSLoc(Capture.getLocation(), BR->getSourceManager());
+ auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+ BR->emitReport(std::move(Report));
+ }
+};
+} // namespace
+
+void ento::registerUncountedLambdaCapturesChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<UncountedLambdaCapturesChecker>();
+}
+
+bool ento::shouldRegisterUncountedLambdaCapturesChecker(
+ const CheckerManager &mgr) {
+ return true;
+}
diff --git a/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp
new file mode 100644
index 0000000000..fa74759349
--- /dev/null
+++ b/contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit/UncountedLocalVarsChecker.cpp
@@ -0,0 +1,252 @@
+//=======- UncountedLocalVarsChecker.cpp -------------------------*- 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 "ASTUtils.h"
+#include "DiagOutputUtils.h"
+#include "PtrTypesSemantics.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ParentMapContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "llvm/ADT/DenseSet.h"
+#include <optional>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+// for ( int a = ...) ... true
+// for ( int a : ...) ... true
+// if ( int* a = ) ... true
+// anything else ... false
+bool isDeclaredInForOrIf(const VarDecl *Var) {
+ assert(Var);
+ auto &ASTCtx = Var->getASTContext();
+ auto parent = ASTCtx.getParents(*Var);
+
+ if (parent.size() == 1) {
+ if (auto *DS = parent.begin()->get<DeclStmt>()) {
+ DynTypedNodeList grandParent = ASTCtx.getParents(*DS);
+ if (grandParent.size() == 1) {
+ return grandParent.begin()->get<ForStmt>() ||
+ grandParent.begin()->get<IfStmt>() ||
+ grandParent.begin()->get<CXXForRangeStmt>();
+ }
+ }
+ }
+ return false;
+}
+
+// FIXME: should be defined by anotations in the future
+bool isRefcountedStringsHack(const VarDecl *V) {
+ assert(V);
+ auto safeClass = [](const std::string &className) {
+ return className == "String" || className == "AtomString" ||
+ className == "UniquedString" || className == "Identifier";
+ };
+ QualType QT = V->getType();
+ auto *T = QT.getTypePtr();
+ if (auto *CXXRD = T->getAsCXXRecordDecl()) {
+ if (safeClass(safeGetName(CXXRD)))
+ return true;
+ }
+ if (T->isPointerType() || T->isReferenceType()) {
+ if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
+ if (safeClass(safeGetName(CXXRD)))
+ return true;
+ }
+ }
+ return false;
+}
+
+bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,
+ const VarDecl *MaybeGuardian) {
+ assert(Guarded);
+ assert(MaybeGuardian);
+
+ if (!MaybeGuardian->isLocalVarDecl())
+ return false;
+
+ const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr;
+
+ ASTContext &ctx = MaybeGuardian->getASTContext();
+
+ for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian);
+ !guardianAncestors.empty();
+ guardianAncestors = ctx.getParents(
+ *guardianAncestors
+ .begin()) // FIXME - should we handle all of the parents?
+ ) {
+ for (auto &guardianAncestor : guardianAncestors) {
+ if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) {
+ guardiansClosestCompStmtAncestor = CStmtParentAncestor;
+ break;
+ }
+ }
+ if (guardiansClosestCompStmtAncestor)
+ break;
+ }
+
+ if (!guardiansClosestCompStmtAncestor)
+ return false;
+
+ // We need to skip the first CompoundStmt to avoid situation when guardian is
+ // defined in the same scope as guarded variable.
+ bool HaveSkippedFirstCompoundStmt = false;
+ for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded);
+ !guardedVarAncestors.empty();
+ guardedVarAncestors = ctx.getParents(
+ *guardedVarAncestors
+ .begin()) // FIXME - should we handle all of the parents?
+ ) {
+ for (auto &guardedVarAncestor : guardedVarAncestors) {
+ if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) {
+ if (!HaveSkippedFirstCompoundStmt) {
+ HaveSkippedFirstCompoundStmt = true;
+ continue;
+ }
+ if (CStmtAncestor == guardiansClosestCompStmtAncestor)
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+class UncountedLocalVarsChecker
+ : public Checker<check::ASTDecl<TranslationUnitDecl>> {
+ BugType Bug{this,
+ "Uncounted raw pointer or reference not provably backed by "
+ "ref-counted variable",
+ "WebKit coding guidelines"};
+ mutable BugReporter *BR;
+
+public:
+ void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
+ BugReporter &BRArg) const {
+ BR = &BRArg;
+
+ // The calls to checkAST* from AnalysisConsumer don't
+ // visit template instantiations or lambda classes. We
+ // want to visit those, so we make our own RecursiveASTVisitor.
+ struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
+ const UncountedLocalVarsChecker *Checker;
+ explicit LocalVisitor(const UncountedLocalVarsChecker *Checker)
+ : Checker(Checker) {
+ assert(Checker);
+ }
+
+ bool shouldVisitTemplateInstantiations() const { return true; }
+ bool shouldVisitImplicitCode() const { return false; }
+
+ bool VisitVarDecl(VarDecl *V) {
+ Checker->visitVarDecl(V);
+ return true;
+ }
+ };
+
+ LocalVisitor visitor(this);
+ visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
+ }
+
+ void visitVarDecl(const VarDecl *V) const {
+ if (shouldSkipVarDecl(V))
+ return;
+
+ const auto *ArgType = V->getType().getTypePtr();
+ if (!ArgType)
+ return;
+
+ std::optional<bool> IsUncountedPtr = isUncountedPtr(ArgType);
+ if (IsUncountedPtr && *IsUncountedPtr) {
+ const Expr *const InitExpr = V->getInit();
+ if (!InitExpr)
+ return; // FIXME: later on we might warn on uninitialized vars too
+
+ const clang::Expr *const InitArgOrigin =
+ tryToFindPtrOrigin(InitExpr, /*StopAtFirstRefCountedObj=*/false)
+ .first;
+ if (!InitArgOrigin)
+ return;
+
+ if (isa<CXXThisExpr>(InitArgOrigin))
+ return;
+
+ if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
+ if (auto *MaybeGuardian =
+ dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
+ const auto *MaybeGuardianArgType =
+ MaybeGuardian->getType().getTypePtr();
+ if (!MaybeGuardianArgType)
+ return;
+ const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
+ MaybeGuardianArgType->getAsCXXRecordDecl();
+ if (!MaybeGuardianArgCXXRecord)
+ return;
+
+ if (MaybeGuardian->isLocalVarDecl() &&
+ (isRefCounted(MaybeGuardianArgCXXRecord) ||
+ isRefcountedStringsHack(MaybeGuardian)) &&
+ isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian)) {
+ return;
+ }
+
+ // Parameters are guaranteed to be safe for the duration of the call
+ // by another checker.
+ if (isa<ParmVarDecl>(MaybeGuardian))
+ return;
+ }
+ }
+
+ reportBug(V);
+ }
+ }
+
+ bool shouldSkipVarDecl(const VarDecl *V) const {
+ assert(V);
+ if (!V->isLocalVarDecl())
+ return true;
+
+ if (isDeclaredInForOrIf(V))
+ return true;
+
+ return false;
+ }
+
+ void reportBug(const VarDecl *V) const {
+ assert(V);
+ SmallString<100> Buf;
+ llvm::raw_svector_ostream Os(Buf);
+
+ Os << "Local variable ";
+ printQuotedQualifiedName(Os, V);
+ Os << " is uncounted and unsafe.";
+
+ PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());
+ auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+ Report->addRange(V->getSourceRange());
+ BR->emitReport(std::move(Report));
+ }
+};
+} // namespace
+
+void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<UncountedLocalVarsChecker>();
+}
+
+bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) {
+ return true;
+}