148 lines · cpp
1// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -analyzer-config c++-inlining=constructors -verify %s \2// RUN: 2>&1 | FileCheck %s3 4 5void clang_analyzer_printState();6template <typename T> void clang_analyzer_dump_lref(T& param);7template <typename T> void clang_analyzer_dump_val(T param);8template <typename T> T conjure();9template <typename... Ts> void nop(const Ts &... args) {}10 11struct aggr {12 int x;13 int y;14};15 16struct empty {17};18 19void test_copy_return() {20 aggr s1 = {1, 2};21 aggr const& cr1 = aggr(s1);22 clang_analyzer_dump_lref(cr1); // expected-warning-re {{&lifetime_extended_object{aggr, cr1, S{{[0-9]+}}} }}23 24 empty s2;25 empty const& cr2 = empty{s2};26 clang_analyzer_dump_lref(cr2); // expected-warning-re {{&lifetime_extended_object{empty, cr2, S{{[0-9]+}}} }}27}28 29void test_assign_return() {30 aggr s1 = {1, 2};31 aggr d1;32 clang_analyzer_dump_lref(d1 = s1); // expected-warning {{&d1 }}33 34 empty s2;35 empty d2;36 clang_analyzer_dump_lref(d2 = s2); // expected-warning {{&d2 }} was Unknown37}38 39 40namespace trivial_struct_copy {41 42void _01_empty_structs() {43 clang_analyzer_dump_val(conjure<empty>()); // expected-warning {{lazyCompoundVal}}44 empty Empty = conjure<empty>();45 empty Empty2 = Empty;46 empty Empty3 = Empty2;47 // All of these should refer to the exact same LCV, because all of48 // these trivial copies refer to the original conjured value.49 // There were Unknown before:50 clang_analyzer_dump_val(Empty); // expected-warning {{lazyCompoundVal}}51 clang_analyzer_dump_val(Empty2); // expected-warning {{lazyCompoundVal}}52 clang_analyzer_dump_val(Empty3); // expected-warning {{lazyCompoundVal}}53 54 // We only have binding for the original Empty object, because copying empty55 // objects is a no-op in the performTrivialCopy. This is fine, because empty56 // objects don't have any data members that could be accessed anyway.57 clang_analyzer_printState();58 // CHECK: "store": { "pointer": "0x{{[0-9a-f]+}}", "items": [59 // CHECK-NEXT: { "cluster": "GlobalInternalSpaceRegion", "pointer": "0x{{[0-9a-f]+}}", "items": [60 // CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "conj_$61 // CHECK-NEXT: ]},62 // CHECK-NEXT: { "cluster": "GlobalSystemSpaceRegion", "pointer": "0x{{[0-9a-f]+}}", "items": [63 // CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "conj_$64 // CHECK-NEXT: ]},65 // CHECK-NEXT: { "cluster": "Empty", "pointer": "0x{{[0-9a-f]+}}", "items": [66 // CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "[[EMPTY_CONJ:conj_\$[0-9]+{int, LC[0-9]+, S[0-9]+, #[0-9]+}]]" }67 // CHECK-NEXT: ]}68 // CHECK-NEXT: ]},69 70 nop(Empty, Empty2, Empty3);71}72 73void _02_structs_with_members() {74 clang_analyzer_dump_val(conjure<aggr>()); // expected-warning {{lazyCompoundVal}}75 aggr Aggr = conjure<aggr>();76 aggr Aggr2 = Aggr;77 aggr Aggr3 = Aggr2;78 // All of these should refer to the exact same LCV, because all of79 // these trivial copies refer to the original conjured value.80 clang_analyzer_dump_val(Aggr); // expected-warning {{lazyCompoundVal}}81 clang_analyzer_dump_val(Aggr2); // expected-warning {{lazyCompoundVal}}82 clang_analyzer_dump_val(Aggr3); // expected-warning {{lazyCompoundVal}}83 84 // We have fields in the struct we copy, thus we also have the entries for the copies85 // (and for all of their fields).86 clang_analyzer_printState();87 // CHECK: "store": { "pointer": "0x{{[0-9a-f]+}}", "items": [88 // CHECK-NEXT: { "cluster": "GlobalInternalSpaceRegion", "pointer": "0x{{[0-9a-f]+}}", "items": [89 // CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "conj_$90 // CHECK-NEXT: ]},91 // CHECK-NEXT: { "cluster": "GlobalSystemSpaceRegion", "pointer": "0x{{[0-9a-f]+}}", "items": [92 // CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "conj_$93 // CHECK-NEXT: ]},94 // CHECK-NEXT: { "cluster": "Aggr", "pointer": "0x{{[0-9a-f]+}}", "items": [95 // CHECK-NEXT: { "kind": "Default", "offset": 0, "value": "[[AGGR_CONJ:conj_\$[0-9]+{int, LC[0-9]+, S[0-9]+, #[0-9]+}]]" }96 // CHECK-NEXT: ]},97 // CHECK-NEXT: { "cluster": "Aggr2", "pointer": "0x{{[0-9a-f]+}}", "items": [98 // CHECK-NEXT: { "kind": "Direct", "offset": 0, "value": "derived_${{[0-9]+}}{[[AGGR_CONJ]],Aggr.x}" },99 // CHECK-NEXT: { "kind": "Direct", "offset": 32, "value": "derived_${{[0-9]+}}{[[AGGR_CONJ]],Aggr.y}" }100 // CHECK-NEXT: ]},101 // CHECK-NEXT: { "cluster": "Aggr3", "pointer": "0x{{[0-9a-f]+}}", "items": [102 // CHECK-NEXT: { "kind": "Direct", "offset": 0, "value": "derived_${{[0-9]+}}{[[AGGR_CONJ]],Aggr.x}" },103 // CHECK-NEXT: { "kind": "Direct", "offset": 32, "value": "derived_${{[0-9]+}}{[[AGGR_CONJ]],Aggr.y}" }104 // CHECK-NEXT: ]}105 // CHECK-NEXT: ]},106 107 nop(Aggr, Aggr2, Aggr3);108}109 110// Tests that use `clang_analyzer_printState()` must share the analysis entry111// point, and have a strict ordering between. This is to meet the different112// `clang_analyzer_printState()` calls in a fixed relative ordering, thus113// FileCheck could check the stdouts.114void entrypoint() {115 _01_empty_structs();116 _02_structs_with_members();117}118 119} // namespace trivial_struct_copy120 121namespace gh153782 {122 123// Ensure we do not regress on the following use cases.124// The assumption made on a field in `setPtr` should apply to the returned copy in `func`.125struct Status { int error; };126Status getError();127 128Status setPtr(int **outptr, int* ptr) {129 Status e = getError();130 if (e.error != 0) return e; // When assuming the error field is non-zero,131 *outptr = ptr; // this is not executed132 return e;133}134 135int func() {136 int *ptr = nullptr;137 int x = 42;138 if (setPtr(&ptr, &x).error == 0) {139 // The assumption made in get() SHOULD match the assumption about140 // the returned value, hence the engine SHOULD NOT assume ptr is null.141 clang_analyzer_dump_val(ptr); // expected-warning {{&x}}142 return *ptr;143 }144 return 0;145}146 147} // namespace gh153782148