diff --git a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h --- a/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h +++ b/clang/include/clang/Analysis/FlowSensitive/DataflowEnvironment.h @@ -135,6 +135,8 @@ /// /// Requirements: /// + /// `Call` must be either a `CallExpr` or a `CXXConstructExpr`. + /// /// The callee of `Call` must be a `FunctionDecl` with a body. /// /// The body of the callee must not reference globals. @@ -142,7 +144,7 @@ /// The arguments of `Call` must map 1:1 to the callee's parameters. /// /// Each argument of `Call` must already have a `StorageLocation`. - Environment pushCall(const CallExpr *Call) const; + Environment pushCall(const Expr *Call) const; /// Moves gathered information back into `this` from a `CalleeEnv` created via /// `pushCall`. diff --git a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp --- a/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp +++ b/clang/lib/Analysis/FlowSensitive/DataflowEnvironment.cpp @@ -203,13 +203,11 @@ } } -Environment Environment::pushCall(const CallExpr *Call) const { +Environment Environment::pushCall(const Expr *Call) const { Environment Env(*this); // FIXME: Support references here. Env.ReturnLoc = Env.getStorageLocation(*Call, SkipPast::Reference); - const auto *FuncDecl = Call->getDirectCallee(); - assert(FuncDecl != nullptr); // FIXME: In order to allow the callee to reference globals, we probably need // to call `initGlobalVars` here in some way. @@ -219,16 +217,37 @@ } } + const FunctionDecl *FuncDecl; + unsigned NumArgs; + if (const auto *ConstructExpr = dyn_cast(Call)) { + Env.ThisPointeeLoc = Env.ReturnLoc; + + FuncDecl = ConstructExpr->getConstructor(); + NumArgs = ConstructExpr->getNumArgs(); + } else if (const auto *NonConstructExpr = dyn_cast(Call)) { + FuncDecl = NonConstructExpr->getDirectCallee(); + NumArgs = NonConstructExpr->getNumArgs(); + } else { + // This case is disallowed by the precondition from the method docstring. + assert(false); + } auto ParamIt = FuncDecl->param_begin(); - auto ArgIt = Call->arg_begin(); - auto ArgEnd = Call->arg_end(); // FIXME: Parameters don't always map to arguments 1:1; examples include // overloaded operators implemented as member functions, and parameter packs. - for (; ArgIt != ArgEnd; ++ParamIt, ++ArgIt) { + for (unsigned ArgIndex = 0; ArgIndex < NumArgs; ++ParamIt, ++ArgIndex) { assert(ParamIt != FuncDecl->param_end()); - const Expr *Arg = *ArgIt; + const Expr *Arg; + if (const auto *ConstructExpr = dyn_cast(Call)) { + Arg = ConstructExpr->getArg(ArgIndex); + } else if (const auto *NonConstructExpr = dyn_cast(Call)) { + Arg = NonConstructExpr->getArg(ArgIndex); + } else { + // This case is disallowed by the precondition from the method docstring. + assert(false); + } + auto *ArgLoc = Env.getStorageLocation(*Arg, SkipPast::Reference); assert(ArgLoc != nullptr); diff --git a/clang/lib/Analysis/FlowSensitive/Transfer.cpp b/clang/lib/Analysis/FlowSensitive/Transfer.cpp --- a/clang/lib/Analysis/FlowSensitive/Transfer.cpp +++ b/clang/lib/Analysis/FlowSensitive/Transfer.cpp @@ -444,6 +444,8 @@ Env.setStorageLocation(*S, Loc); if (Value *Val = Env.createValue(S->getType())) Env.setValue(Loc, *Val); + + transferInlineCall(S, ConstructorDecl); } void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *S) { @@ -530,41 +532,13 @@ if (!Options.ContextSensitive) return; - const ControlFlowContext *CFCtx = Env.getControlFlowContext(F); - if (!CFCtx) - return; - - // FIXME: We don't support context-sensitive analysis of recursion, so - // we should return early here if `F` is the same as the `FunctionDecl` - // holding `S` itself. - - auto ExitBlock = CFCtx->getCFG().getExit().getBlockID(); - // Note that it is important for the storage location of `S` to be set // before `pushCall`, because the latter uses it to set the storage // location for `return`. auto &ReturnLoc = Env.createStorageLocation(*S); Env.setStorageLocation(*S, ReturnLoc); - auto CalleeEnv = Env.pushCall(S); - - // FIXME: Use the same analysis as the caller for the callee. Note, - // though, that doing so would require support for changing the analysis's - // ASTContext. - assert( - CFCtx->getDecl() != nullptr && - "ControlFlowContexts in the environment should always carry a decl"); - auto Analysis = NoopAnalysis(CFCtx->getDecl()->getASTContext(), - DataflowAnalysisOptions()); - - auto BlockToOutputState = - dataflow::runDataflowAnalysis(*CFCtx, Analysis, CalleeEnv); - assert(BlockToOutputState); - assert(ExitBlock < BlockToOutputState->size()); - - auto ExitState = (*BlockToOutputState)[ExitBlock]; - assert(ExitState); - - Env.popCall(ExitState->Env); + + transferInlineCall(S, F); } } @@ -693,6 +667,42 @@ return Env.makeAtomicBoolValue(); } + void transferInlineCall(const Expr *S, const FunctionDecl *F) { + if (!Options.ContextSensitive) + return; + + const ControlFlowContext *CFCtx = Env.getControlFlowContext(F); + + if (!CFCtx) + return; + + // FIXME: We don't support context-sensitive analysis of recursion, so + // we should return early here if `F` is the same as the `FunctionDecl` + // holding `S` itself. + + auto ExitBlock = CFCtx->getCFG().getExit().getBlockID(); + + auto CalleeEnv = Env.pushCall(S); + + // FIXME: Use the same analysis as the caller for the callee. Note, + // though, that doing so would require support for changing the analysis's + // ASTContext. + assert(CFCtx->getDecl() != nullptr && + "ControlFlowContexts in the environment should always carry a decl"); + auto Analysis = NoopAnalysis(CFCtx->getDecl()->getASTContext(), + DataflowAnalysisOptions()); + + auto BlockToOutputState = + dataflow::runDataflowAnalysis(*CFCtx, Analysis, CalleeEnv); + assert(BlockToOutputState); + assert(ExitBlock < BlockToOutputState->size()); + + auto ExitState = (*BlockToOutputState)[ExitBlock]; + assert(ExitState); + + Env.popCall(ExitState->Env); + } + const StmtToEnvMap &StmtToEnv; Environment &Env; TransferOptions Options; diff --git a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp --- a/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp +++ b/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp @@ -4368,4 +4368,72 @@ /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}); } +TEST(TransferTest, ContextSensitiveConstructorBody) { + std::string Code = R"( + class MyClass { + public: + MyClass() { MyField = true; } + + bool MyField; + }; + + void target() { + MyClass MyObj; + bool Foo = MyObj.MyField; + // [[p]] + } + )"; + runDataflow(Code, + [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; + + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); + ASSERT_THAT(FooDecl, NotNull()); + + auto &FooVal = + *cast(Env.getValue(*FooDecl, SkipPast::None)); + EXPECT_TRUE(Env.flowConditionImplies(FooVal)); + }, + {/*.ApplyBuiltinTransfer=*/true, + /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}); +} + +TEST(TransferTest, ContextSensitiveConstructorInitializer) { + std::string Code = R"( + class MyClass { + public: + MyClass() : MyField(true) {} + + bool MyField; + }; + + void target() { + MyClass MyObj; + bool Foo = MyObj.MyField; + // [[p]] + } + )"; + runDataflow(Code, + [](llvm::ArrayRef< + std::pair>> + Results, + ASTContext &ASTCtx) { + ASSERT_THAT(Results, ElementsAre(Pair("p", _))); + const Environment &Env = Results[0].second.Env; + + const ValueDecl *FooDecl = findValueDecl(ASTCtx, "Foo"); + ASSERT_THAT(FooDecl, NotNull()); + + auto &FooVal = + *cast(Env.getValue(*FooDecl, SkipPast::None)); + EXPECT_TRUE(Env.flowConditionImplies(FooVal)); + }, + {/*.ApplyBuiltinTransfer=*/true, + /*.BuiltinTransferOptions=*/{/*.ContextSensitive=*/true}}); +} + } // namespace