https://github.com/ziqingluo-90 created https://github.com/llvm/llvm-project/pull/218196
A DeclPointerLevel differs from an EntityPointerLevel only in that it retains the AST Decl node instead of abstracting it to an Entity. The AST node carries more information, such as types, that can be used by extractors before converting DeclPointerLevels to EntityPointerLevels. E.g., PointerFlowExtractor can use it to compute the upper bound on pointer levels for an entity. It is needed for fixing the bug reported in rdar://183529483. First step for: rdar://183529483 >From dfc3dc14a18d11bfdea5c0ebefb5037de636dff7 Mon Sep 17 00:00:00 2001 From: Ziqing Luo <[email protected]> Date: Fri, 21 Aug 2026 13:28:47 -0700 Subject: [PATCH] [SSAF][EntityPointerLevel] Add an intermediate data structure DeclPointerLevel A DeclPointerLevel differs from an EntityPointerLevel only in that it retains the AST Decl node instead of abstracting it to an Entity. The AST node carries more information, such as types, that can be used by extractors before converting DeclPointerLevels to EntityPointerLevels. E.g., PointerFlowExtractor can use it to compute the upper bound on pointer levels for an entity. It is needed for fixing the bug reported in rdar://183529483. First step for: rdar://183529483 --- .../EntityPointerLevel/EntityPointerLevel.h | 48 +++- .../EntityPointerLevel/EntityPointerLevel.cpp | 214 ++++++++++++------ .../EntityPointerLevelTest.cpp | 102 +++++++++ .../ScalableStaticAnalysis/CMakeLists.txt | 1 + 4 files changed, 289 insertions(+), 76 deletions(-) create mode 100644 clang/unittests/ScalableStaticAnalysis/Analyses/EntityPointerLevel/EntityPointerLevelTest.cpp diff --git a/clang/include/clang/ScalableStaticAnalysis/Analyses/EntityPointerLevel/EntityPointerLevel.h b/clang/include/clang/ScalableStaticAnalysis/Analyses/EntityPointerLevel/EntityPointerLevel.h index c296016258f8d..1a85003debcb3 100644 --- a/clang/include/clang/ScalableStaticAnalysis/Analyses/EntityPointerLevel/EntityPointerLevel.h +++ b/clang/include/clang/ScalableStaticAnalysis/Analyses/EntityPointerLevel/EntityPointerLevel.h @@ -12,10 +12,21 @@ #include "clang/AST/Expr.h" #include "clang/ScalableStaticAnalysis/Core/Model/EntityId.h" #include <set> +#include <vector> namespace clang::ssaf { class TUSummaryExtractor; +// Intermediate data structure that can be converted to a `EntityPointerLevel`, +// which abstracts away all the additional information carried in the NamedDecl. +struct DeclPointerLevel { + const NamedDecl *Decl; + unsigned PointerLevel; + bool IsReturn; +}; + +using DeclPointerLevels = std::vector<DeclPointerLevel>; + /// An EntityPointerLevel is associated with a level of the declared /// pointer/array type of an entity. In the fully-expanded spelling of the /// declared type, a EntityPointerLevel is associated with a '*' (or a '[]`) in @@ -90,16 +101,43 @@ using EntityPointerLevelSet = /// /// \param E the pointer expression to be translated /// \param Ctx the AST context of `E` -/// \param AddEntity the callback provided by the caller to convert EntityNames -/// to EntityIds. +/// \param Extractor the TUSummaryExtractor used to convert NamedDecls to +/// EntityIds. llvm::Expected<EntityPointerLevelSet> translateEntityPointerLevel(const Expr *E, ASTContext &Ctx, TUSummaryExtractor &Extractor); +/// Same as `translateEntityPointerLevel`, except it returns raw +/// `(NamedDecl *, pointer level, is-return)` tuples (a.k.a. DeclPointerLevels) +/// instead of assembling an `EntityPointerLevelSet` directly. +llvm::Expected<DeclPointerLevels> +translateDeclPointerLevel(const Expr *E, ASTContext &Ctx, + TUSummaryExtractor &Extractor); + +/// Assemble `(NamedDecl *, pointer level, is-return)` tuples (a.k.a. +/// DeclPointerLevels) to `EntityPointerLevelSet`. +Expected<EntityPointerLevelSet> +toEntityPointerLevels(const DeclPointerLevels &DPLs, ASTContext &Ctx, + TUSummaryExtractor &Extractor); + +/// Convert a single `(NamedDecl *, pointer level, is-return)` tuple (a.k.a. +/// DeclPointerLevel) to an `EntityPointerLevel`. +Expected<EntityPointerLevel> +toEntityPointerLevel(const DeclPointerLevel &DPL, ASTContext &Ctx, + TUSummaryExtractor &Extractor); + /// Creates a `EntityPointerLevel` from a pair of an EntityId and a pointer /// level: EntityPointerLevel buildEntityPointerLevel(EntityId, unsigned); +/// Create an DeclPointerLevel (DPL) from a NamedDecl of a pointer/array type. +/// +/// \param ND the NamedDecl of a pointer/array type. +/// \param IsFunRet true iff the created DPL is associated with the return type +/// of a function entity. +DeclPointerLevel createDeclPointerLevel(const NamedDecl *ND, + bool IsFunRet = false); + /// Create an EntityPointerLevel (EPL) from a NamedDecl of a pointer/array type. /// /// \param ND the NamedDecl of a pointer/array type. @@ -111,6 +149,12 @@ llvm::Expected<EntityPointerLevel> createEntityPointerLevel(const NamedDecl *ND, TUSummaryExtractor &Extractor, bool IsFunRet = false); +/// \return an exhaustive vector of DeclPointerLevels, sorted in ascending order +/// of pointer level, such that every element is identical to \p DPL except with +/// a no-lower-than pointer level. The pointer level of each element is bounded +/// by the type of the NamedDecl of \p DPL. +DeclPointerLevels elaborateHigherDeclPointerLevels(const DeclPointerLevel &DPL); + /// Creates a new EntityPointerLevel (EPL) from `E` by incrementing `E`'s /// pointer level. /// \return the EPL that is associated with the pointee (or array element) type diff --git a/clang/lib/ScalableStaticAnalysis/Analyses/EntityPointerLevel/EntityPointerLevel.cpp b/clang/lib/ScalableStaticAnalysis/Analyses/EntityPointerLevel/EntityPointerLevel.cpp index eb8dfdc5d9ace..d7f447fb093a3 100644 --- a/clang/lib/ScalableStaticAnalysis/Analyses/EntityPointerLevel/EntityPointerLevel.cpp +++ b/clang/lib/ScalableStaticAnalysis/Analyses/EntityPointerLevel/EntityPointerLevel.cpp @@ -14,7 +14,9 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/StmtVisitor.h" #include "clang/ScalableStaticAnalysis/Core/TUSummary/TUSummaryExtractor.h" +#include "llvm/ADT/STLExtras.h" #include <optional> +#include <vector> using namespace clang; using namespace ssaf; @@ -41,11 +43,11 @@ namespace clang::ssaf { // Translate(&arr[5]) -> {(arr, 1)} class EntityPointerLevelTranslator : ConstStmtVisitor<EntityPointerLevelTranslator, - Expected<EntityPointerLevelSet>> { + Expected<DeclPointerLevels>> { friend class StmtVisitorBase; // Fallback method for all unsupported expression kind: - Expected<EntityPointerLevelSet> fallback(const Stmt *S) { + Expected<DeclPointerLevels> fallback(const Stmt *S) { // Report an error/warning (at least in debug mode) for any unsupported kind // of pointer/array typed expression, because we want to understand every // pointer/array expression. But for non-pointer/array typed expressions, we @@ -55,7 +57,7 @@ class EntityPointerLevelTranslator return makeErrAtNode(Ctx, E, "attempt to translate %s to EntityPointerLevels", E->getStmtClassName()); - return EntityPointerLevelSet{}; + return DeclPointerLevels{}; } Expected<EntityPointerLevel> @@ -79,15 +81,15 @@ class EntityPointerLevelTranslator // The common helper function for Translate(*base): // Translate(*base) -> Translate(base) with .pointerLevel + 1 - Expected<EntityPointerLevelSet> translateDereferencePointer(const Expr *Ptr) { + Expected<DeclPointerLevels> translateDereferencePointer(const Expr *Ptr) { assert(hasPtrOrArrType(Ptr)); - Expected<EntityPointerLevelSet> SubResult = Visit(Ptr); + Expected<DeclPointerLevels> SubResult = Visit(Ptr); if (!SubResult) return SubResult.takeError(); - auto Incremented = llvm::map_range(*SubResult, incrementPointerLevel); - return EntityPointerLevelSet{Incremented.begin(), Incremented.end()}; + llvm::for_each(*SubResult, [](DeclPointerLevel &D) { ++D.PointerLevel; }); + return SubResult; } TUSummaryExtractor &Extractor; @@ -97,7 +99,7 @@ class EntityPointerLevelTranslator EntityPointerLevelTranslator(TUSummaryExtractor &Extractor, ASTContext &Ctx) : Extractor(Extractor), Ctx(Ctx) {} - Expected<EntityPointerLevelSet> translate(const Expr *E) { return Visit(E); } + Expected<DeclPointerLevels> translate(const Expr *E) { return Visit(E); } Expected<EntityPointerLevel> translate(const NamedDecl *D, bool IsRet) { if (!IsRet) return createEntityPointerLevelFor(D); @@ -109,26 +111,27 @@ class EntityPointerLevelTranslator D->getDeclKindName()); } - static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) { - return EntityPointerLevel({E.getEntity(), E.getPointerLevel() + 1}); + // Converts a `DeclPointerLevel` to an `EntityPointerLevel` + Expected<EntityPointerLevel> toEntityPointerLevel(const DeclPointerLevel &D) { + Expected<EntityPointerLevel> Base = translate(D.Decl, D.IsReturn); + if (!Base) + return Base.takeError(); + return buildEntityPointerLevel(Base->getEntity(), D.PointerLevel); } - static EntityPointerLevel decrementPointerLevel(const EntityPointerLevel &E) { - assert(E.getPointerLevel() > 0); - return EntityPointerLevel({E.getEntity(), E.getPointerLevel() - 1}); + static EntityPointerLevel incrementPointerLevel(const EntityPointerLevel &E) { + return EntityPointerLevel({E.getEntity(), E.getPointerLevel() + 1}); } private: - Expected<EntityPointerLevelSet> VisitStmt(const Stmt *E) { - return fallback(E); - } + Expected<DeclPointerLevels> VisitStmt(const Stmt *E) { return fallback(E); } // Translate(base + x) -> Translate(base) // Translate(x + base) -> Translate(base) // Translate(base - x) -> Translate(base) // Translate(base {+=, -=, =} x) -> Translate(base) // Translate(x, base) -> Translate(base) - Expected<EntityPointerLevelSet> VisitBinaryOperator(const BinaryOperator *E) { + Expected<DeclPointerLevels> VisitBinaryOperator(const BinaryOperator *E) { switch (E->getOpcode()) { case clang::BO_Add: if (hasPtrOrArrType(E->getLHS())) @@ -152,7 +155,7 @@ class EntityPointerLevelTranslator // Translate(&base) -> {}, if Translate(base) is {} // -> Translate(base) with .pointerLevel -= 1 // Translate(+base) -> Translate(base) - Expected<EntityPointerLevelSet> VisitUnaryOperator(const UnaryOperator *E) { + Expected<DeclPointerLevels> VisitUnaryOperator(const UnaryOperator *E) { switch (E->getOpcode()) { case clang::UO_PostInc: case clang::UO_PostDec: @@ -160,12 +163,15 @@ class EntityPointerLevelTranslator case clang::UO_PreDec: return Visit(E->getSubExpr()); case clang::UO_AddrOf: { - Expected<EntityPointerLevelSet> SubResult = Visit(E->getSubExpr()); + Expected<DeclPointerLevels> SubResult = Visit(E->getSubExpr()); if (!SubResult) return SubResult.takeError(); - auto Decremented = llvm::map_range(*SubResult, decrementPointerLevel); - return EntityPointerLevelSet{Decremented.begin(), Decremented.end()}; + llvm::for_each(*SubResult, [](DeclPointerLevel &D) { + assert(D.PointerLevel > 0); + --D.PointerLevel; + }); + return SubResult; } case clang::UO_Deref: return translateDereferencePointer(E->getSubExpr()); @@ -178,36 +184,35 @@ class EntityPointerLevelTranslator // Translate((T*)base) -> Translate(base) if base has pointer type // -> {} otherwise - Expected<EntityPointerLevelSet> VisitCastExpr(const CastExpr *E) { + Expected<DeclPointerLevels> VisitCastExpr(const CastExpr *E) { if (hasPtrOrArrType(E->getSubExpr())) return Visit(E->getSubExpr()); - return EntityPointerLevelSet{}; + return DeclPointerLevels{}; } // Translate(f(...)) -> {} if it is an indirect call // -> {(f_return, 1)}, otherwise - Expected<EntityPointerLevelSet> VisitCallExpr(const CallExpr *E) { - if (auto *FD = E->getDirectCallee()) { - if (auto ReturnId = Extractor.addEntityForReturn(FD)) - return EntityPointerLevelSet{buildEntityPointerLevel(*ReturnId, 1)}; - } - return EntityPointerLevelSet{}; + Expected<DeclPointerLevels> VisitCallExpr(const CallExpr *E) { + if (auto *FD = E->getDirectCallee()) + if (Extractor.addEntityForReturn(FD)) + return DeclPointerLevels{{FD, /*PointerLevel=*/1, /*IsReturn=*/true}}; + return DeclPointerLevels{}; } // Translate(base[x]) -> Translate(*base) - Expected<EntityPointerLevelSet> + Expected<DeclPointerLevels> VisitArraySubscriptExpr(const ArraySubscriptExpr *E) { return translateDereferencePointer(E->getBase()); } // Translate(cond ? base1 : base2) := Translate(base1) U Translate(base2) - Expected<EntityPointerLevelSet> + Expected<DeclPointerLevels> VisitAbstractConditionalOperator(const AbstractConditionalOperator *E) { - Expected<EntityPointerLevelSet> ReT = Visit(E->getTrueExpr()); - Expected<EntityPointerLevelSet> ReF = Visit(E->getFalseExpr()); + Expected<DeclPointerLevels> ReT = Visit(E->getTrueExpr()); + Expected<DeclPointerLevels> ReF = Visit(E->getFalseExpr()); if (ReT && ReF) { - ReT->insert(ReF->begin(), ReF->end()); + ReT->insert(ReT->end(), ReF->begin(), ReF->end()); return ReT; } if (!ReF && !ReT) @@ -217,100 +222,94 @@ class EntityPointerLevelTranslator return ReT.takeError(); } - Expected<EntityPointerLevelSet> VisitParenExpr(const ParenExpr *E) { + Expected<DeclPointerLevels> VisitParenExpr(const ParenExpr *E) { return Visit(E->getSubExpr()); } // Translate("string-literal") -> {} // no entity involved - Expected<EntityPointerLevelSet> VisitStringLiteral(const StringLiteral *E) { - return EntityPointerLevelSet{}; + Expected<DeclPointerLevels> VisitStringLiteral(const StringLiteral *E) { + return DeclPointerLevels{}; } // Translate(predefined-expr) -> {} // treated the same as string literals - Expected<EntityPointerLevelSet> VisitPredefinedExpr(const PredefinedExpr *E) { - return EntityPointerLevelSet{}; + Expected<DeclPointerLevels> VisitPredefinedExpr(const PredefinedExpr *E) { + return DeclPointerLevels{}; } // Translate(integer-literal) -> {} // no entity involved - Expected<EntityPointerLevelSet> VisitIntegerLiteral(const IntegerLiteral *E) { - return EntityPointerLevelSet{}; + Expected<DeclPointerLevels> VisitIntegerLiteral(const IntegerLiteral *E) { + return DeclPointerLevels{}; } // Translate(DRE) -> {(Decl, 1)} - Expected<EntityPointerLevelSet> VisitDeclRefExpr(const DeclRefExpr *E) { - auto Res = createEntityPointerLevelFor(E->getDecl()); - if (!Res) - return Res.takeError(); - return EntityPointerLevelSet{*Res}; + Expected<DeclPointerLevels> VisitDeclRefExpr(const DeclRefExpr *E) { + return DeclPointerLevels{ + {E->getDecl(), /*PointerLevel=*/1, /*IsReturn=*/false}}; } // Translate({., ->}f) -> {(MemberDecl, 1)} - Expected<EntityPointerLevelSet> VisitMemberExpr(const MemberExpr *E) { - auto Res = createEntityPointerLevelFor(E->getMemberDecl()); - if (!Res) - return Res.takeError(); - return EntityPointerLevelSet{*Res}; + Expected<DeclPointerLevels> VisitMemberExpr(const MemberExpr *E) { + return DeclPointerLevels{ + {E->getMemberDecl(), /*PointerLevel=*/1, /*IsReturn=*/false}}; } // Unwrap CXXDefaultArgExpr - Expected<EntityPointerLevelSet> + Expected<DeclPointerLevels> VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E) { return Visit(E->getExpr()); } // Unwrap OpaqueValueExpr - Expected<EntityPointerLevelSet> - VisitOpaqueValueExpr(const OpaqueValueExpr *S) { + Expected<DeclPointerLevels> VisitOpaqueValueExpr(const OpaqueValueExpr *S) { return Visit(S->getSourceExpr()); } // Unwrap ExprWithCleanups - Expected<EntityPointerLevelSet> - VisitExprWithCleanups(const ExprWithCleanups *S) { + Expected<DeclPointerLevels> VisitExprWithCleanups(const ExprWithCleanups *S) { return Visit(S->getSubExpr()); } // Unwrap MaterializeTemporaryExpr - Expected<EntityPointerLevelSet> + Expected<DeclPointerLevels> VisitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *S) { return Visit(S->getSubExpr()); } // Unwrap CXXDefaultInitExpr - Expected<EntityPointerLevelSet> + Expected<DeclPointerLevels> VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E) { return Visit(E->getExpr()); } // Translate(`nullptr`) -> {} - Expected<EntityPointerLevelSet> + Expected<DeclPointerLevels> VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *S) { - return EntityPointerLevelSet{}; + return DeclPointerLevels{}; } // Translate(`this`) -> {} - Expected<EntityPointerLevelSet> VisitCXXThisExpr(const CXXThisExpr *S) { - return EntityPointerLevelSet{}; + Expected<DeclPointerLevels> VisitCXXThisExpr(const CXXThisExpr *S) { + return DeclPointerLevels{}; } // Translate(`new`/`new [*]`) -> {} - Expected<EntityPointerLevelSet> VisitCXXNewExpr(const CXXNewExpr *S) { - return EntityPointerLevelSet{}; + Expected<DeclPointerLevels> VisitCXXNewExpr(const CXXNewExpr *S) { + return DeclPointerLevels{}; } // ImplicitValueInitExpr, for raw pointer type, // evaluates to a compile-time constant zero (or null). So no EPL in the // result. - Expected<EntityPointerLevelSet> + Expected<DeclPointerLevels> VisitImplicitValueInitExpr(const ImplicitValueInitExpr *S) { - return EntityPointerLevelSet{}; + return DeclPointerLevels{}; } // The InitListExpr must be an empty or singleton list that // initializes a pointer scalar. Other cases are unexpected thus an error. - Expected<EntityPointerLevelSet> VisitInitListExpr(const InitListExpr *E) { + Expected<DeclPointerLevels> VisitInitListExpr(const InitListExpr *E) { if (E->getNumInits() < 1) - return EntityPointerLevelSet{}; + return DeclPointerLevels{}; if (E->getType()->isPointerType()) return Visit(E->getInit(0)); return llvm::createStringError( @@ -323,28 +322,42 @@ class EntityPointerLevelTranslator // When a CXXConstructExpr has an array type, clang is initializing an array // of class-type objects with default values. In this case, no entity is // associated with the initializer. - Expected<EntityPointerLevelSet> - VisitCXXConstructExpr(const CXXConstructExpr *E) { + Expected<DeclPointerLevels> VisitCXXConstructExpr(const CXXConstructExpr *E) { if (E->getType()->isArrayType()) { - return EntityPointerLevelSet{}; + return DeclPointerLevels{}; } return fallback(E); } // No entity is associated with a CXXScalarValueInitExpr: - Expected<EntityPointerLevelSet> + Expected<DeclPointerLevels> VisitCXXScalarValueInitExpr(const CXXScalarValueInitExpr *E) { - return EntityPointerLevelSet{}; + return DeclPointerLevels{}; } }; } // namespace clang::ssaf +Expected<DeclPointerLevels> +clang::ssaf::translateDeclPointerLevel(const Expr *E, ASTContext &Ctx, + TUSummaryExtractor &Extractor) { + EntityPointerLevelTranslator Translator(Extractor, Ctx); + + return Translator.translate(E); +} + Expected<EntityPointerLevelSet> clang::ssaf::translateEntityPointerLevel(const Expr *E, ASTContext &Ctx, TUSummaryExtractor &Extractor) { EntityPointerLevelTranslator Translator(Extractor, Ctx); + auto DPLs = Translator.translate(E); + if (!DPLs) + return DPLs.takeError(); + return toEntityPointerLevels(*DPLs, Ctx, Extractor); +} - return Translator.translate(E); +DeclPointerLevel clang::ssaf::createDeclPointerLevel(const NamedDecl *ND, + bool IsFunRet) { + return {ND, 1, IsFunRet}; } /// Create an EntityPointerLevel from a ValueDecl of a pointer type. @@ -355,6 +368,59 @@ Expected<EntityPointerLevel> clang::ssaf::createEntityPointerLevel( return Translator.translate(ND, IsFunRet); } +DeclPointerLevels +clang::ssaf::elaborateHigherDeclPointerLevels(const DeclPointerLevel &DPL) { + DeclPointerLevels Result{DPL}; + QualType T; + + if (DPL.IsReturn) { + if (const auto *FD = dyn_cast<FunctionDecl>(DPL.Decl)) + T = FD->getReturnType().getNonReferenceType(); + } else if (const auto *VD = dyn_cast<ValueDecl>(DPL.Decl)) { + T = VD->getType().getNonReferenceType(); + } + if (T.isNull()) + return Result; + + // Count the max pointer/array levels of `T`: + unsigned MaxLevel = 0; + for (T = T.getCanonicalType();; ++MaxLevel) { + if (const auto *PT = dyn_cast<PointerType>(T)) + T = PT->getPointeeType().getCanonicalType(); + else if (const auto *AT = dyn_cast<ArrayType>(T)) + T = AT->getElementType().getCanonicalType(); + else + break; + } + + for (unsigned Level = DPL.PointerLevel + 1; Level <= MaxLevel; ++Level) + Result.push_back({DPL.Decl, Level, DPL.IsReturn}); + return Result; +} + +Expected<EntityPointerLevelSet> +clang::ssaf::toEntityPointerLevels(const DeclPointerLevels &DPLs, + ASTContext &Ctx, + TUSummaryExtractor &Extractor) { + EntityPointerLevelTranslator Translator(Extractor, Ctx); + EntityPointerLevelSet Result; + + for (const auto &DPL : DPLs) { + Expected<EntityPointerLevel> EPL = Translator.toEntityPointerLevel(DPL); + if (!EPL) + return EPL.takeError(); + Result.insert(*EPL); + } + return Result; +} + +Expected<EntityPointerLevel> +clang::ssaf::toEntityPointerLevel(const DeclPointerLevel &DPL, ASTContext &Ctx, + TUSummaryExtractor &Extractor) { + EntityPointerLevelTranslator Translator(Extractor, Ctx); + return Translator.toEntityPointerLevel(DPL); +} + EntityPointerLevel clang::ssaf::incrementPointerLevel(const EntityPointerLevel &E) { return EntityPointerLevelTranslator::incrementPointerLevel(E); diff --git a/clang/unittests/ScalableStaticAnalysis/Analyses/EntityPointerLevel/EntityPointerLevelTest.cpp b/clang/unittests/ScalableStaticAnalysis/Analyses/EntityPointerLevel/EntityPointerLevelTest.cpp new file mode 100644 index 0000000000000..d9b5a347edb6c --- /dev/null +++ b/clang/unittests/ScalableStaticAnalysis/Analyses/EntityPointerLevel/EntityPointerLevelTest.cpp @@ -0,0 +1,102 @@ +//===- EntityPointerLevelTest.cpp -----------------------------------------===// +// +// 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 "clang/ScalableStaticAnalysis/Analyses/EntityPointerLevel/EntityPointerLevel.h" +#include "FindDecl.h" +#include "clang/AST/Decl.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Tooling/Tooling.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include <memory> +#include <vector> + +using namespace clang; +using namespace ssaf; +using testing::ElementsAre; + +namespace { + +static std::vector<unsigned> levelsOf(const DeclPointerLevels &DPLs) { + std::vector<unsigned> Levels; + for (const DeclPointerLevel &DPL : DPLs) + Levels.push_back(DPL.PointerLevel); + return Levels; +} + +// `elaborateHigherDeclPointerLevels` expands a DeclPointerLevel into an +// ascending, exhaustive vector of DeclPointerLevels for the same declaration, +// from the given level up to the maximum pointer level of the declared type. +TEST(EntityPointerLevelTest, ElaborateHigherDeclPointerLevels) { + std::unique_ptr<ASTUnit> AST = + tooling::buildASTFromCodeWithArgs(R"cpp( + int *p; // one pointer level + int **q; // two pointer levels + int ***r; // three pointer levels + int *arr[10]; // array of pointers: two levels (array + pointer) + )cpp", + {"-Wno-unused"}); + ASSERT_TRUE(AST); + ASTContext &Ctx = AST->getASTContext(); + + // Elaborate the decl named `Name` starting at `StartLevel`, checking that + // every result shares the input's declaration and is-return flag, and return + // the produced pointer levels. + auto elaborate = [&](StringRef Name, unsigned StartLevel) { + const NamedDecl *ND = findDeclByName(Name, Ctx); + EXPECT_NE(ND, nullptr) << "decl not found: " << Name.str(); + if (!ND) + return std::vector<unsigned>{}; + DeclPointerLevels DPLs = elaborateHigherDeclPointerLevels( + DeclPointerLevel{ND, StartLevel, /*IsReturn=*/false}); + for (const DeclPointerLevel &DPL : DPLs) { + EXPECT_TRUE(DPL.Decl == ND); + EXPECT_FALSE(DPL.IsReturn); + } + return levelsOf(DPLs); + }; + + EXPECT_THAT(elaborate("p", 1), ElementsAre(1U)); // int* + EXPECT_THAT(elaborate("q", 1), ElementsAre(1U, 2U)); // int** + EXPECT_THAT(elaborate("r", 1), ElementsAre(1U, 2U, 3U)); // int*** + EXPECT_THAT(elaborate("arr", 1), ElementsAre(1U, 2U)); // int*[10] + EXPECT_THAT(elaborate("r", 2), ElementsAre(2U, 3U)); + EXPECT_THAT(elaborate("r", 3), ElementsAre(3U)); +} + +// For a function entity (IsReturn=true), the maximum pointer level is bounded +// by the return type, whose reference must be stripped first. +TEST(EntityPointerLevelTest, ElaborateHigherDeclPointerLevelsForReturn) { + std::unique_ptr<ASTUnit> AST = + tooling::buildASTFromCodeWithArgs(R"cpp( + int **&refret(); // reference to int**: two pointer levels + int *valret(); // int*: one pointer level + )cpp", + {"-Wno-unused"}); + ASSERT_TRUE(AST); + ASTContext &Ctx = AST->getASTContext(); + + auto elaborateReturn = [&](StringRef Name, unsigned StartLevel) { + const FunctionDecl *FD = findFnByName(Name, Ctx); + EXPECT_NE(FD, nullptr) << "function not found: " << Name.str(); + if (!FD) + return std::vector<unsigned>{}; + DeclPointerLevels DPLs = elaborateHigherDeclPointerLevels( + DeclPointerLevel{FD, StartLevel, /*IsReturn=*/true}); + for (const DeclPointerLevel &DPL : DPLs) { + EXPECT_TRUE(DPL.Decl == FD); + EXPECT_TRUE(DPL.IsReturn); + } + return levelsOf(DPLs); + }; + + EXPECT_THAT(elaborateReturn("refret", 1), ElementsAre(1U, 2U)); // int**& + EXPECT_THAT(elaborateReturn("valret", 1), ElementsAre(1U)); // int* +} + +} // namespace diff --git a/clang/unittests/ScalableStaticAnalysis/CMakeLists.txt b/clang/unittests/ScalableStaticAnalysis/CMakeLists.txt index ed3b57168b069..21fcfd34741cb 100644 --- a/clang/unittests/ScalableStaticAnalysis/CMakeLists.txt +++ b/clang/unittests/ScalableStaticAnalysis/CMakeLists.txt @@ -1,4 +1,5 @@ add_distinct_clang_unittest(ClangScalableAnalysisTests + Analyses/EntityPointerLevel/EntityPointerLevelTest.cpp Analyses/PointerFlow/PointerFlowTest.cpp Analyses/PointerFlow/PointerFlowWPATest.cpp Analyses/CallGraph/CallGraphExtractorTest.cpp _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
