temporaries.cpp revision 176edba5311f6eff0cad2631449885ddf4fbc9ea
1// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify -w -std=c++03 %s
2// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify -w -std=c++11 %s
3// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -DTEMPORARY_DTORS -verify -w -analyzer-config cfg-temporary-dtors=true %s -std=c++11
4
5extern bool clang_analyzer_eval(bool);
6extern bool clang_analyzer_warnIfReached();
7
8struct Trivial {
9  Trivial(int x) : value(x) {}
10  int value;
11};
12
13struct NonTrivial : public Trivial {
14  NonTrivial(int x) : Trivial(x) {}
15  ~NonTrivial();
16};
17
18
19Trivial getTrivial() {
20  return Trivial(42); // no-warning
21}
22
23const Trivial &getTrivialRef() {
24  return Trivial(42); // expected-warning {{Address of stack memory associated with temporary object of type 'Trivial' returned to caller}}
25}
26
27
28NonTrivial getNonTrivial() {
29  return NonTrivial(42); // no-warning
30}
31
32const NonTrivial &getNonTrivialRef() {
33  return NonTrivial(42); // expected-warning {{Address of stack memory associated with temporary object of type 'NonTrivial' returned to caller}}
34}
35
36namespace rdar13265460 {
37  struct TrivialSubclass : public Trivial {
38    TrivialSubclass(int x) : Trivial(x), anotherValue(-x) {}
39    int anotherValue;
40  };
41
42  TrivialSubclass getTrivialSub() {
43    TrivialSubclass obj(1);
44    obj.value = 42;
45    obj.anotherValue = -42;
46    return obj;
47  }
48
49  void testImmediate() {
50    TrivialSubclass obj = getTrivialSub();
51
52    clang_analyzer_eval(obj.value == 42); // expected-warning{{TRUE}}
53    clang_analyzer_eval(obj.anotherValue == -42); // expected-warning{{TRUE}}
54
55    clang_analyzer_eval(getTrivialSub().value == 42); // expected-warning{{TRUE}}
56    clang_analyzer_eval(getTrivialSub().anotherValue == -42); // expected-warning{{TRUE}}
57  }
58
59  void testMaterializeTemporaryExpr() {
60    const TrivialSubclass &ref = getTrivialSub();
61    clang_analyzer_eval(ref.value == 42); // expected-warning{{TRUE}}
62
63    const Trivial &baseRef = getTrivialSub();
64    clang_analyzer_eval(baseRef.value == 42); // expected-warning{{TRUE}}
65  }
66}
67
68namespace rdar13281951 {
69  struct Derived : public Trivial {
70    Derived(int value) : Trivial(value), value2(-value) {}
71    int value2;
72  };
73
74  void test() {
75    Derived obj(1);
76    obj.value = 42;
77    const Trivial * const &pointerRef = &obj;
78    clang_analyzer_eval(pointerRef->value == 42); // expected-warning{{TRUE}}
79  }
80}
81
82namespace compound_literals {
83  struct POD {
84    int x, y;
85  };
86  struct HasCtor {
87    HasCtor(int x, int y) : x(x), y(y) {}
88    int x, y;
89  };
90  struct HasDtor {
91    int x, y;
92    ~HasDtor();
93  };
94  struct HasCtorDtor {
95    HasCtorDtor(int x, int y) : x(x), y(y) {}
96    ~HasCtorDtor();
97    int x, y;
98  };
99
100  void test() {
101    clang_analyzer_eval(((POD){1, 42}).y == 42); // expected-warning{{TRUE}}
102    clang_analyzer_eval(((HasDtor){1, 42}).y == 42); // expected-warning{{TRUE}}
103
104#if __cplusplus >= 201103L
105    clang_analyzer_eval(((HasCtor){1, 42}).y == 42); // expected-warning{{TRUE}}
106
107    // FIXME: should be TRUE, but we don't inline the constructors of
108    // temporaries because we can't model their destructors yet.
109    clang_analyzer_eval(((HasCtorDtor){1, 42}).y == 42); // expected-warning{{UNKNOWN}}
110#endif
111  }
112}
113
114namespace destructors {
115  struct Dtor {
116    ~Dtor();
117  };
118  extern bool coin();
119  extern bool check(const Dtor &);
120
121  void testPR16664andPR18159Crash() {
122    // Regression test: we used to assert here when tmp dtors are enabled.
123    // PR16664 and PR18159
124    if (coin() && (coin() || coin() || check(Dtor()))) {
125      Dtor();
126    }
127  }
128
129#ifdef TEMPORARY_DTORS
130  struct NoReturnDtor {
131    ~NoReturnDtor() __attribute__((noreturn));
132  };
133
134  void noReturnTemp(int *x) {
135    if (! x) NoReturnDtor();
136    *x = 47; // no warning
137  }
138
139  void noReturnInline(int **x) {
140    NoReturnDtor();
141  }
142
143  void callNoReturn() {
144    int *x;
145    noReturnInline(&x);
146    *x = 47; // no warning
147  }
148
149  extern bool check(const NoReturnDtor &);
150
151  void testConsistencyIf(int i) {
152    if (i != 5)
153      return;
154    if (i == 5 && (i == 4 || check(NoReturnDtor()) || i == 5)) {
155      clang_analyzer_eval(true); // no warning, unreachable code
156    }
157  }
158
159  void testConsistencyTernary(int i) {
160    (i == 5 && (i == 4 || check(NoReturnDtor()) || i == 5)) ? 1 : 0;
161
162    clang_analyzer_eval(true);  // expected-warning{{TRUE}}
163
164    if (i != 5)
165      return;
166
167    (i == 5 && (i == 4 || check(NoReturnDtor()) || i == 5)) ? 1 : 0;
168
169    clang_analyzer_eval(true); // no warning, unreachable code
170  }
171
172  // Regression test: we used to assert here.
173  // PR16664 and PR18159
174  void testConsistencyNested(int i) {
175    extern bool compute(bool);
176
177    if (i == 5 && (i == 4 || i == 5 || check(NoReturnDtor())))
178      clang_analyzer_eval(true);  // expected-warning{{TRUE}}
179
180    if (i == 5 && (i == 4 || i == 5 || check(NoReturnDtor())))
181      clang_analyzer_eval(true);  // expected-warning{{TRUE}}
182
183    if (i != 5)
184      return;
185
186    if (compute(i == 5 &&
187                (i == 4 || compute(true) ||
188                 compute(i == 5 && (i == 4 || check(NoReturnDtor()))))) ||
189        i != 4) {
190      clang_analyzer_eval(true);  // expected-warning{{TRUE}}
191    }
192
193    if (compute(i == 5 &&
194                (i == 4 || i == 4 ||
195                 compute(i == 5 && (i == 4 || check(NoReturnDtor()))))) ||
196        i != 4) {
197      clang_analyzer_eval(true);  // no warning, unreachable code
198    }
199  }
200
201  // PR16664 and PR18159
202  void testConsistencyNestedSimple(bool value) {
203    if (value) {
204      if (!value || check(NoReturnDtor())) {
205        clang_analyzer_eval(true); // no warning, unreachable code
206      }
207    }
208  }
209
210  // PR16664 and PR18159
211  void testConsistencyNestedComplex(bool value) {
212    if (value) {
213      if (!value || !value || check(NoReturnDtor())) {
214        clang_analyzer_eval(true);  // no warning, unreachable code
215      }
216    }
217  }
218
219  // PR16664 and PR18159
220  void testConsistencyNestedWarning(bool value) {
221    if (value) {
222      if (!value || value || check(NoReturnDtor())) {
223        clang_analyzer_eval(true); // expected-warning{{TRUE}}
224      }
225    }
226  }
227  // PR16664 and PR18159
228  void testConsistencyNestedComplexMidBranch(bool value) {
229    if (value) {
230      if (!value || !value || check(NoReturnDtor()) || value) {
231        clang_analyzer_eval(true);  // no warning, unreachable code
232      }
233    }
234  }
235
236  // PR16664 and PR18159
237  void testConsistencyNestedComplexNestedBranch(bool value) {
238    if (value) {
239      if (!value || (!value || check(NoReturnDtor()) || value)) {
240        clang_analyzer_eval(true);  // no warning, unreachable code
241      }
242    }
243  }
244
245  // PR16664 and PR18159
246  void testConsistencyNestedVariableModification(bool value) {
247    bool other = true;
248    if (value) {
249      if (!other || !value || (other = false) || check(NoReturnDtor()) ||
250          !other) {
251        clang_analyzer_eval(true);  // no warning, unreachable code
252      }
253    }
254  }
255
256  void testTernaryNoReturnTrueBranch(bool value) {
257    if (value) {
258      bool b = value && (value ? check(NoReturnDtor()) : true);
259      clang_analyzer_eval(true);  // no warning, unreachable code
260    }
261  }
262  void testTernaryNoReturnFalseBranch(bool value) {
263    if (value) {
264      bool b = !value && !value ? true : check(NoReturnDtor());
265      clang_analyzer_eval(true);  // no warning, unreachable code
266    }
267  }
268  void testTernaryIgnoreNoreturnBranch(bool value) {
269    if (value) {
270      bool b = !value && !value ? check(NoReturnDtor()) : true;
271      clang_analyzer_eval(true);  // expected-warning{{TRUE}}
272    }
273  }
274  void testTernaryTrueBranchReached(bool value) {
275    value ? clang_analyzer_warnIfReached() : // expected-warning{{REACHABLE}}
276            check(NoReturnDtor());
277  }
278  void testTernaryFalseBranchReached(bool value) {
279    value ? check(NoReturnDtor()) :
280            clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
281  }
282
283  void testLoop() {
284    for (int i = 0; i < 10; ++i) {
285      if (i < 3 && (i >= 2 || check(NoReturnDtor()))) {
286        clang_analyzer_eval(true);  // no warning, unreachable code
287      }
288    }
289  }
290
291  bool testRecursiveFrames(bool isInner) {
292    if (isInner ||
293        (clang_analyzer_warnIfReached(), false) || // expected-warning{{REACHABLE}}
294        check(NoReturnDtor()) ||
295        testRecursiveFrames(true)) {
296      clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
297    }
298  }
299  void testRecursiveFramesStart() { testRecursiveFrames(false); }
300
301  void testLambdas() {
302    // This is the test we would like to write:
303    // []() { check(NoReturnDtor()); } != nullptr || check(Dtor());
304    // But currently the analyzer stops when it encounters a lambda:
305    [] {};
306    // The CFG for this now looks correct, but we still do not reach the line
307    // below.
308    clang_analyzer_warnIfReached(); // FIXME: Should warn.
309  }
310
311  void testGnuExpressionStatements(int v) {
312    ({ ++v; v == 10 || check(NoReturnDtor()); v == 42; }) || v == 23;
313    clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
314
315    ({ ++v; check(NoReturnDtor()); v == 42; }) || v == 23;
316    clang_analyzer_warnIfReached();  // no warning, unreachable code
317  }
318
319  void testGnuExpressionStatementsDestructionPoint(int v) {
320    // In normal context, the temporary destructor runs at the end of the full
321    // statement, thus the last statement is reached.
322    (++v, check(NoReturnDtor()), v == 42),
323        clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
324
325    // GNU expression statements execute temporary destructors within the
326    // blocks, thus the last statement is not reached.
327    ({ ++v; check(NoReturnDtor()); v == 42; }),
328        clang_analyzer_warnIfReached();  // no warning, unreachable code
329  }
330
331  void testMultipleTemporaries(bool value) {
332    if (value) {
333      // FIXME: Find a way to verify construction order.
334      // ~Dtor should run before ~NoReturnDtor() because construction order is
335      // guaranteed by comma operator.
336      if (!value || check((NoReturnDtor(), Dtor())) || value) {
337        clang_analyzer_eval(true);  // no warning, unreachable code
338      }
339    }
340  }
341
342  void testBinaryOperatorShortcut(bool value) {
343    if (value) {
344      if (false && false && check(NoReturnDtor()) && true) {
345        clang_analyzer_eval(true);
346      }
347    }
348  }
349
350  void testIfAtEndOfLoop() {
351    int y = 0;
352    while (true) {
353      if (y > 0) {
354        clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
355      }
356      ++y;
357      // Test that the CFG gets hooked up correctly when temporary destructors
358      // are handled after a statically known branch condition.
359      if (true) (void)0; else (void)check(NoReturnDtor());
360    }
361  }
362
363  void testTernaryAtEndOfLoop() {
364    int y = 0;
365    while (true) {
366      if (y > 0) {
367        clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
368      }
369      ++y;
370      // Test that the CFG gets hooked up correctly when temporary destructors
371      // are handled after a statically known branch condition.
372      true ? (void)0 : (void)check(NoReturnDtor());
373    }
374  }
375
376  void testNoReturnInComplexCondition() {
377    check(Dtor()) &&
378        (check(NoReturnDtor()) || check(NoReturnDtor())) && check(Dtor());
379    clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
380  }
381
382  void testSequencingOfConditionalTempDtors(bool b) {
383    b || (check(Dtor()), check(NoReturnDtor()));
384    clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
385  }
386
387  void testSequencingOfConditionalTempDtors2(bool b) {
388    (b || check(Dtor())), check(NoReturnDtor());
389    clang_analyzer_warnIfReached();  // no warning, unreachable code
390  }
391
392  void testSequencingOfConditionalTempDtorsWithinBinaryOperators(bool b) {
393    b || (check(Dtor()) + check(NoReturnDtor()));
394    clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
395  }
396
397  void f(Dtor d = Dtor());
398  void testDefaultParameters() {
399    f();
400  }
401
402  struct DefaultParam {
403    DefaultParam(int, const Dtor& d = Dtor());
404    ~DefaultParam();
405  };
406  void testDefaultParamConstructorsInLoops() {
407    while (true) {
408      // FIXME: This exact pattern triggers the temporary cleanup logic
409      // to fail when adding a 'clean' state.
410      DefaultParam(42);
411      DefaultParam(42);
412    }
413  }
414  void testDefaultParamConstructorsInTernariesInLoops(bool value) {
415    while (true) {
416      // FIXME: This exact pattern triggers the temporary cleanup logic
417      // to visit the bind-temporary logic with a state that already has that
418      // temporary marked as executed.
419      value ? DefaultParam(42) : DefaultParam(42);
420    }
421  }
422#endif // TEMPORARY_DTORS
423}
424
425void testStaticMaterializeTemporaryExpr() {
426  static const Trivial &ref = getTrivial();
427  clang_analyzer_eval(ref.value == 42); // expected-warning{{TRUE}}
428
429  static const Trivial &directRef = Trivial(42);
430  clang_analyzer_eval(directRef.value == 42); // expected-warning{{TRUE}}
431
432#if __has_feature(cxx_thread_local)
433  thread_local static const Trivial &threadRef = getTrivial();
434  clang_analyzer_eval(threadRef.value == 42); // expected-warning{{TRUE}}
435
436  thread_local static const Trivial &threadDirectRef = Trivial(42);
437  clang_analyzer_eval(threadDirectRef.value == 42); // expected-warning{{TRUE}}
438#endif
439}
440
441namespace PR16629 {
442  struct A {
443    explicit A(int* p_) : p(p_) {}
444    int* p;
445  };
446
447  extern void escape(const A*[]);
448  extern void check(int);
449
450  void callEscape(const A& a) {
451    const A* args[] = { &a };
452    escape(args);
453  }
454
455  void testNoWarning() {
456    int x;
457    callEscape(A(&x));
458    check(x); // Analyzer used to give a "x is uninitialized warning" here
459  }
460
461  void set(const A*a[]) {
462    *a[0]->p = 47;
463  }
464
465  void callSet(const A& a) {
466    const A* args[] = { &a };
467    set(args);
468  }
469
470  void testConsistency() {
471    int x;
472    callSet(A(&x));
473    clang_analyzer_eval(x == 47); // expected-warning{{TRUE}}
474  }
475}
476