diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h index 97ea6a573cffd..4e65d974133a6 100644 --- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h @@ -492,9 +492,9 @@ class Environment { void pushCallInternal(const FunctionDecl *FuncDecl, ArrayRef Args); - /// Assigns storage locations and values to all global variables and fields - /// referenced in `FuncDecl`. `FuncDecl` must have a body. - void initFieldsAndGlobals(const FunctionDecl *FuncDecl); + /// Assigns storage locations and values to all global variables, fields + /// and functions referenced in `FuncDecl`. `FuncDecl` must have a body. + void initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl); // `DACtx` is not null and not owned by this object. DataflowAnalysisContext *DACtx; diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp index 680036b6a5b39..9f5b3adc8b1b1 100644 --- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp @@ -162,10 +162,19 @@ static void insertIfGlobal(const Decl &D, Vars.insert(V); } -static void getFieldsAndGlobalVars(const Decl &D, - llvm::DenseSet &Fields, - llvm::DenseSet &Vars) { +static void insertIfFunction(const Decl &D, + llvm::DenseSet &Funcs) { + if (auto *FD = dyn_cast(&D)) + Funcs.insert(FD); +} + +static void +getFieldsGlobalsAndFuncs(const Decl &D, + llvm::DenseSet &Fields, + llvm::DenseSet &Vars, + llvm::DenseSet &Funcs) { insertIfGlobal(D, Vars); + insertIfFunction(D, Funcs); if (const auto *Decomp = dyn_cast(&D)) for (const auto *B : Decomp->bindings()) if (auto *ME = dyn_cast_or_null(B->getBinding())) @@ -174,27 +183,32 @@ static void getFieldsAndGlobalVars(const Decl &D, Fields.insert(FD); } -/// Traverses `S` and inserts into `Vars` any global storage values that are -/// declared in or referenced from sub-statements. -static void getFieldsAndGlobalVars(const Stmt &S, - llvm::DenseSet &Fields, - llvm::DenseSet &Vars) { +/// Traverses `S` and inserts into `Fields`, `Vars` and `Funcs` any fields, +/// global variables and functions that are declared in or referenced from +/// sub-statements. +static void +getFieldsGlobalsAndFuncs(const Stmt &S, + llvm::DenseSet &Fields, + llvm::DenseSet &Vars, + llvm::DenseSet &Funcs) { for (auto *Child : S.children()) if (Child != nullptr) - getFieldsAndGlobalVars(*Child, Fields, Vars); + getFieldsGlobalsAndFuncs(*Child, Fields, Vars, Funcs); if (auto *DS = dyn_cast(&S)) { if (DS->isSingleDecl()) - getFieldsAndGlobalVars(*DS->getSingleDecl(), Fields, Vars); + getFieldsGlobalsAndFuncs(*DS->getSingleDecl(), Fields, Vars, Funcs); else for (auto *D : DS->getDeclGroup()) - getFieldsAndGlobalVars(*D, Fields, Vars); + getFieldsGlobalsAndFuncs(*D, Fields, Vars, Funcs); } else if (auto *E = dyn_cast(&S)) { insertIfGlobal(*E->getDecl(), Vars); + insertIfFunction(*E->getDecl(), Funcs); } else if (auto *E = dyn_cast(&S)) { // FIXME: should we be using `E->getFoundDecl()`? const ValueDecl *VD = E->getMemberDecl(); insertIfGlobal(*VD, Vars); + insertIfFunction(*VD, Funcs); if (const auto *FD = dyn_cast(VD)) Fields.insert(FD); } @@ -202,11 +216,12 @@ static void getFieldsAndGlobalVars(const Stmt &S, // FIXME: Add support for resetting globals after function calls to enable // the implementation of sound analyses. -void Environment::initFieldsAndGlobals(const FunctionDecl *FuncDecl) { +void Environment::initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl) { assert(FuncDecl->getBody() != nullptr); llvm::DenseSet Fields; llvm::DenseSet Vars; + llvm::DenseSet Funcs; // Look for global variable and field references in the // constructor-initializers. @@ -216,14 +231,14 @@ void Environment::initFieldsAndGlobals(const FunctionDecl *FuncDecl) { Fields.insert(M); const Expr *E = Init->getInit(); assert(E != nullptr); - getFieldsAndGlobalVars(*E, Fields, Vars); + getFieldsGlobalsAndFuncs(*E, Fields, Vars, Funcs); } // Add all fields mentioned in default member initializers. for (const FieldDecl *F : CtorDecl->getParent()->fields()) if (const auto *I = F->getInClassInitializer()) - getFieldsAndGlobalVars(*I, Fields, Vars); + getFieldsGlobalsAndFuncs(*I, Fields, Vars, Funcs); } - getFieldsAndGlobalVars(*FuncDecl->getBody(), Fields, Vars); + getFieldsGlobalsAndFuncs(*FuncDecl->getBody(), Fields, Vars, Funcs); // These have to be added before the lines that follow to ensure that // `create*` work correctly for structs. @@ -237,6 +252,13 @@ void Environment::initFieldsAndGlobals(const FunctionDecl *FuncDecl) { if (auto *Val = createValue(D->getType())) setValue(Loc, *Val); } + + for (const FunctionDecl *FD : Funcs) { + if (getStorageLocation(*FD, SkipPast::None) != nullptr) + continue; + auto &Loc = createStorageLocation(FD->getType()); + setStorageLocation(*FD, Loc); + } } Environment::Environment(DataflowAnalysisContext &DACtx) @@ -264,7 +286,7 @@ Environment::Environment(DataflowAnalysisContext &DACtx, if (const auto *FuncDecl = dyn_cast(&DeclCtx)) { assert(FuncDecl->getBody() != nullptr); - initFieldsAndGlobals(FuncDecl); + initFieldsGlobalsAndFuncs(FuncDecl); for (const auto *ParamDecl : FuncDecl->parameters()) { assert(ParamDecl != nullptr); @@ -338,7 +360,7 @@ void Environment::pushCallInternal(const FunctionDecl *FuncDecl, ArrayRef Args) { CallStack.push_back(FuncDecl); - initFieldsAndGlobals(FuncDecl); + initFieldsGlobalsAndFuncs(FuncDecl); const auto *ParamIt = FuncDecl->param_begin(); diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp index 1d273e77ef0b5..2d85e7b90f73e 100644 --- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp +++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp @@ -403,6 +403,18 @@ class TransferVisitor : public ConstStmtVisitor { Env.setValue(Loc, NullPointerVal); break; } + case CK_FunctionToPointerDecay: { + StorageLocation *PointeeLoc = + Env.getStorageLocation(*SubExpr, SkipPast::Reference); + if (PointeeLoc == nullptr) + break; + + auto &PointerLoc = Env.createStorageLocation(*S); + auto &PointerVal = Env.create(*PointeeLoc); + Env.setStorageLocation(*S, PointerLoc); + Env.setValue(PointerLoc, PointerVal); + break; + } default: break; } diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp index 8f02161834dc8..1589067a81c7b 100644 --- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp @@ -5232,4 +5232,43 @@ TEST(TransferTest, NewExpressions_Structs) { }); } +TEST(TransferTest, FunctionToPointerDecayHasValue) { + std::string Code = R"( + struct A { static void static_member_func(); }; + void target() { + // To check that we're treating function-to-pointer decay correctly, + // create two pointers, then verify they refer to the same storage + // location. + // We need to do the test this way because even if an initializer (in this + // case, the function-to-pointer decay) does not create a value, we still + // create a value for the variable. + void (*non_member_p1)() = target; + void (*non_member_p2)() = target; + + // Do the same thing but for a static member function. + void (*member_p1)() = A::static_member_func; + void (*member_p2)() = A::static_member_func; + // [[p]] + } + )"; + runDataflow( + Code, + [](const llvm::StringMap> &Results, + ASTContext &ASTCtx) { + const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); + + auto &NonMemberP1 = + getValueForDecl(ASTCtx, Env, "non_member_p1"); + auto &NonMemberP2 = + getValueForDecl(ASTCtx, Env, "non_member_p2"); + EXPECT_EQ(&NonMemberP1.getPointeeLoc(), &NonMemberP2.getPointeeLoc()); + + auto &MemberP1 = + getValueForDecl(ASTCtx, Env, "member_p1"); + auto &MemberP2 = + getValueForDecl(ASTCtx, Env, "member_p2"); + EXPECT_EQ(&MemberP1.getPointeeLoc(), &MemberP2.getPointeeLoc()); + }); +} + } // namespace