@@ -3400,6 +3400,60 @@ TEST(TransferTest, ResultObjectLocationDontVisitNestedRecordDecl) {
34003400 ASTContext &ASTCtx) {});
34013401}
34023402
3403+ TEST (TransferTest, ResultObjectLocationDontVisitUnevaluatedContexts) {
3404+ // This is a crash repro.
3405+ // We used to crash because when propagating result objects, we would visit
3406+ // unevaluated contexts, but we don't model fields used only in these.
3407+
3408+ auto testFunction = [](llvm::StringRef Code, llvm::StringRef TargetFun) {
3409+ runDataflow (
3410+ Code,
3411+ [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3412+ ASTContext &ASTCtx) {},
3413+ LangStandard::lang_gnucxx17,
3414+ /* ApplyBuiltinTransfer= */ true , TargetFun);
3415+ };
3416+
3417+ std::string Code = R"cc(
3418+ // Definitions needed for `typeid`.
3419+ namespace std {
3420+ class type_info {};
3421+ class bad_typeid {};
3422+ } // namespace std
3423+
3424+ struct S1 {};
3425+ struct S2 { S1 s1; };
3426+
3427+ // We test each type of unevaluated context from a different target
3428+ // function. Some types of unevaluated contexts may actually cause the
3429+ // field `s1` to be modeled, and we don't want this to "pollute" the tests
3430+ // for the other unevaluated contexts.
3431+ void decltypeTarget() {
3432+ decltype(S2{}) Dummy;
3433+ }
3434+ void typeofTarget() {
3435+ typeof(S2{}) Dummy;
3436+ }
3437+ void typeidTarget() {
3438+ #if __has_feature(cxx_rtti)
3439+ typeid(S2{});
3440+ #endif
3441+ }
3442+ void sizeofTarget() {
3443+ sizeof(S2{});
3444+ }
3445+ void noexceptTarget() {
3446+ noexcept(S2{});
3447+ }
3448+ )cc" ;
3449+
3450+ testFunction (Code, " decltypeTarget" );
3451+ testFunction (Code, " typeofTarget" );
3452+ testFunction (Code, " typeidTarget" );
3453+ testFunction (Code, " sizeofTarget" );
3454+ testFunction (Code, " noexceptTarget" );
3455+ }
3456+
34033457TEST (TransferTest, StaticCast) {
34043458 std::string Code = R"(
34053459 void target(int Foo) {
0 commit comments