|
17 | 17 | #include "clang/Analysis/FlowSensitive/DataflowAnalysis.h" |
18 | 18 | #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h" |
19 | 19 | #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h" |
| 20 | +#include "clang/Analysis/FlowSensitive/Formula.h" |
20 | 21 | #include "clang/Analysis/FlowSensitive/NoopAnalysis.h" |
21 | 22 | #include "clang/Analysis/FlowSensitive/NoopLattice.h" |
22 | 23 | #include "clang/Analysis/FlowSensitive/RecordOps.h" |
@@ -4382,6 +4383,40 @@ TEST(TransferTest, VarDeclInitAssignConditionalOperator) { |
4382 | 4383 | }); |
4383 | 4384 | } |
4384 | 4385 |
|
| 4386 | +TEST(TransferTest, VarDeclInitReferenceAssignConditionalOperator) { |
| 4387 | + std::string Code = R"( |
| 4388 | + struct A { |
| 4389 | + int i; |
| 4390 | + }; |
| 4391 | +
|
| 4392 | + void target(A Foo, A Bar, bool Cond) { |
| 4393 | + A &Baz = Cond ? Foo : Bar; |
| 4394 | + // Make sure A::i is modeled. |
| 4395 | + Baz.i; |
| 4396 | + /*[[p]]*/ |
| 4397 | + } |
| 4398 | + )"; |
| 4399 | + runDataflow( |
| 4400 | + Code, |
| 4401 | + [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, |
| 4402 | + ASTContext &ASTCtx) { |
| 4403 | + const Environment &Env = getEnvironmentAtAnnotation(Results, "p"); |
| 4404 | + |
| 4405 | + auto *FooIVal = cast<IntegerValue>(getFieldValue( |
| 4406 | + &getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "Foo"), "i", |
| 4407 | + ASTCtx, Env)); |
| 4408 | + auto *BarIVal = cast<IntegerValue>(getFieldValue( |
| 4409 | + &getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "Bar"), "i", |
| 4410 | + ASTCtx, Env)); |
| 4411 | + auto *BazIVal = cast<IntegerValue>(getFieldValue( |
| 4412 | + &getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "Baz"), "i", |
| 4413 | + ASTCtx, Env)); |
| 4414 | + |
| 4415 | + EXPECT_NE(BazIVal, FooIVal); |
| 4416 | + EXPECT_NE(BazIVal, BarIVal); |
| 4417 | + }); |
| 4418 | +} |
| 4419 | + |
4385 | 4420 | TEST(TransferTest, VarDeclInDoWhile) { |
4386 | 4421 | std::string Code = R"( |
4387 | 4422 | void target(int *Foo) { |
@@ -6150,6 +6185,45 @@ TEST(TransferTest, ConditionalOperatorValue) { |
6150 | 6185 | }); |
6151 | 6186 | } |
6152 | 6187 |
|
| 6188 | +TEST(TransferTest, ConditionalOperatorValuesTested) { |
| 6189 | + // We should be able to show that the result of the conditional operator, |
| 6190 | + // JoinResultMustBeB1, must be equal to B1, because the condition is checking |
| 6191 | + // `B1 == B2` and selecting B1 on the false branch, or B2 on the true branch. |
| 6192 | + // Similarly, for JoinResultMustBeB2 == B2. |
| 6193 | + // Note that the conditional operator involves a join of two *different* |
| 6194 | + // glvalues, before casting the lvalue to an rvalue, which may affect the |
| 6195 | + // implementation of the transfer function, and thus affect whether or not we |
| 6196 | + // can prove that IsB1 == B1. |
| 6197 | + std::string Code = R"( |
| 6198 | + void target(bool B1, bool B2) { |
| 6199 | + bool JoinResultMustBeB1 = (B1 == B2) ? B2 : B1; |
| 6200 | + bool JoinResultMustBeB2 = (B1 == B2) ? B1 : B2; |
| 6201 | + // [[p]] |
| 6202 | + } |
| 6203 | + )"; |
| 6204 | + runDataflow( |
| 6205 | + Code, |
| 6206 | + [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, |
| 6207 | + ASTContext &ASTCtx) { |
| 6208 | + Environment Env = getEnvironmentAtAnnotation(Results, "p").fork(); |
| 6209 | + |
| 6210 | + auto &B1 = getValueForDecl<BoolValue>(ASTCtx, Env, "B1"); |
| 6211 | + auto &B2 = getValueForDecl<BoolValue>(ASTCtx, Env, "B2"); |
| 6212 | + auto &JoinResultMustBeB1 = |
| 6213 | + getValueForDecl<BoolValue>(ASTCtx, Env, "JoinResultMustBeB1"); |
| 6214 | + auto &JoinResultMustBeB2 = |
| 6215 | + getValueForDecl<BoolValue>(ASTCtx, Env, "JoinResultMustBeB2"); |
| 6216 | + |
| 6217 | + const Formula &MustBeB1_Eq_B1 = |
| 6218 | + Env.arena().makeEquals(JoinResultMustBeB1.formula(), B1.formula()); |
| 6219 | + EXPECT_TRUE(Env.proves(MustBeB1_Eq_B1)); |
| 6220 | + |
| 6221 | + const Formula &MustBeB2_Eq_B2 = |
| 6222 | + Env.arena().makeEquals(JoinResultMustBeB2.formula(), B2.formula()); |
| 6223 | + EXPECT_TRUE(Env.proves(MustBeB2_Eq_B2)); |
| 6224 | + }); |
| 6225 | +} |
| 6226 | + |
6153 | 6227 | TEST(TransferTest, ConditionalOperatorLocation) { |
6154 | 6228 | std::string Code = R"( |
6155 | 6229 | void target(bool Cond, int I1, int I2) { |
@@ -6177,6 +6251,66 @@ TEST(TransferTest, ConditionalOperatorLocation) { |
6177 | 6251 | }); |
6178 | 6252 | } |
6179 | 6253 |
|
| 6254 | +TEST(TransferTest, ConditionalOperatorLocationUpdatedAfter) { |
| 6255 | + // We don't currently model a Conditional Operator with an LValue result |
| 6256 | + // as having aliases to the LHS and RHS (if it isn't just the same LValue |
| 6257 | + // on both sides). We also don't model that the update "may" happen |
| 6258 | + // (a weak update). So, we don't consider the LHS and RHS as being weakly |
| 6259 | + // updated at [[after_diff]]. |
| 6260 | + std::string Code = R"( |
| 6261 | + void target(bool Cond, bool B1, bool B2) { |
| 6262 | + (void)0; |
| 6263 | + // [[before_same]] |
| 6264 | + (Cond ? B1 : B1) = !B1; |
| 6265 | + // [[after_same]] |
| 6266 | + (Cond ? B1 : B2) = !B1; |
| 6267 | + // [[after_diff]] |
| 6268 | + } |
| 6269 | + )"; |
| 6270 | + runDataflow( |
| 6271 | + Code, |
| 6272 | + [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, |
| 6273 | + ASTContext &ASTCtx) { |
| 6274 | + Environment BeforeSameEnv = |
| 6275 | + getEnvironmentAtAnnotation(Results, "before_same").fork(); |
| 6276 | + Environment AfterSameEnv = |
| 6277 | + getEnvironmentAtAnnotation(Results, "after_same").fork(); |
| 6278 | + Environment AfterDiffEnv = |
| 6279 | + getEnvironmentAtAnnotation(Results, "after_diff").fork(); |
| 6280 | + |
| 6281 | + auto &BeforeSameB1 = |
| 6282 | + getValueForDecl<BoolValue>(ASTCtx, BeforeSameEnv, "B1"); |
| 6283 | + auto &AfterSameB1 = |
| 6284 | + getValueForDecl<BoolValue>(ASTCtx, AfterSameEnv, "B1"); |
| 6285 | + auto &AfterDiffB1 = |
| 6286 | + getValueForDecl<BoolValue>(ASTCtx, AfterDiffEnv, "B1"); |
| 6287 | + |
| 6288 | + EXPECT_NE(&BeforeSameB1, &AfterSameB1); |
| 6289 | + EXPECT_NE(&BeforeSameB1, &AfterDiffB1); |
| 6290 | + // FIXME: The formula for AfterSameB1 should be different from |
| 6291 | + // AfterDiffB1 to reflect that B1 may be updated. |
| 6292 | + EXPECT_EQ(&AfterSameB1, &AfterDiffB1); |
| 6293 | + |
| 6294 | + // The value of B1 is definitely different from before_same vs |
| 6295 | + // after_same. |
| 6296 | + const Formula &B1ChangedForSame = |
| 6297 | + AfterSameEnv.arena().makeNot(AfterSameEnv.arena().makeEquals( |
| 6298 | + AfterSameB1.formula(), BeforeSameB1.formula())); |
| 6299 | + EXPECT_TRUE(AfterSameEnv.allows(B1ChangedForSame)); |
| 6300 | + EXPECT_TRUE(AfterSameEnv.proves(B1ChangedForSame)); |
| 6301 | + |
| 6302 | + const Formula &B1ChangedForDiff = |
| 6303 | + AfterDiffEnv.arena().makeNot(AfterDiffEnv.arena().makeEquals( |
| 6304 | + AfterDiffB1.formula(), AfterSameB1.formula())); |
| 6305 | + // FIXME: It should be possible that B1 *may* be updated, so it may be |
| 6306 | + // that AfterSameB1 != AfterDiffB1 or AfterSameB1 == AfterDiffB1. |
| 6307 | + EXPECT_FALSE(AfterSameEnv.allows(B1ChangedForDiff)); |
| 6308 | + // proves() should be false, since B1 may or may not have changed |
| 6309 | + // depending on `Cond`. |
| 6310 | + EXPECT_FALSE(AfterSameEnv.proves(B1ChangedForDiff)); |
| 6311 | + }); |
| 6312 | +} |
| 6313 | + |
6180 | 6314 | TEST(TransferTest, ConditionalOperatorOnConstantExpr) { |
6181 | 6315 | // This is a regression test: We used to crash when a `ConstantExpr` was used |
6182 | 6316 | // in the branches of a conditional operator. |
|
0 commit comments