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/lib/StaticAnalyzer/Checkers/WebKit | |
parent | 9685917341315774aad5733b1793b1e533a88bbb (diff) | |
download | ydb-11a895b7e15d1c5a1f52706396b82e3f9db953cb.tar.gz |
Export clang-format16 via ydblib project
6e6be3a95868fde888d801b7590af4044049563f
Diffstat (limited to 'contrib/libs/clang16/lib/StaticAnalyzer/Checkers/WebKit')
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; +} |