| // RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s |
| |
| void clang_analyzer_eval(bool); |
| |
| struct A { |
| int x; |
| void foo() const; |
| void bar(); |
| }; |
| |
| struct B { |
| mutable int mut; |
| void foo() const; |
| }; |
| |
| struct C { |
| int *p; |
| void foo() const; |
| }; |
| |
| struct MutBase { |
| mutable int b_mut; |
| }; |
| |
| struct MutDerived : MutBase { |
| void foo() const; |
| }; |
| |
| struct PBase { |
| int *p; |
| }; |
| |
| struct PDerived : PBase { |
| void foo() const; |
| }; |
| |
| struct Inner { |
| int x; |
| int *p; |
| void bar() const; |
| }; |
| |
| struct Outer { |
| int x; |
| Inner in; |
| void foo() const; |
| }; |
| |
| void checkThatConstMethodWithoutDefinitionDoesNotInvalidateObject() { |
| A t; |
| t.x = 3; |
| t.foo(); |
| clang_analyzer_eval(t.x == 3); // expected-warning{{TRUE}} |
| // Test non-const does invalidate |
| t.bar(); |
| clang_analyzer_eval(t.x); // expected-warning{{UNKNOWN}} |
| } |
| |
| void checkThatConstMethodDoesInvalidateMutableFields() { |
| B t; |
| t.mut = 4; |
| t.foo(); |
| clang_analyzer_eval(t.mut); // expected-warning{{UNKNOWN}} |
| } |
| |
| void checkThatConstMethodDoesInvalidatePointedAtMemory() { |
| int x = 1; |
| C t; |
| t.p = &x; |
| t.foo(); |
| clang_analyzer_eval(x); // expected-warning{{UNKNOWN}} |
| clang_analyzer_eval(t.p == &x); // expected-warning{{TRUE}} |
| } |
| |
| void checkThatConstMethodDoesInvalidateInheritedMutableFields() { |
| MutDerived t; |
| t.b_mut = 4; |
| t.foo(); |
| clang_analyzer_eval(t.b_mut); // expected-warning{{UNKNOWN}} |
| } |
| |
| void checkThatConstMethodDoesInvalidateInheritedPointedAtMemory() { |
| int x = 1; |
| PDerived t; |
| t.p = &x; |
| t.foo(); |
| clang_analyzer_eval(x); // expected-warning{{UNKNOWN}} |
| clang_analyzer_eval(t.p == &x); // expected-warning{{TRUE}} |
| } |
| |
| void checkThatConstMethodDoesInvalidateContainedPointedAtMemory() { |
| int x = 1; |
| Outer t; |
| t.x = 2; |
| t.in.p = &x; |
| t.foo(); |
| clang_analyzer_eval(x); // expected-warning{{UNKNOWN}} |
| clang_analyzer_eval(t.x == 2); // expected-warning{{TRUE}} |
| clang_analyzer_eval(t.in.p == &x); // expected-warning{{TRUE}} |
| } |
| |
| void checkThatContainedConstMethodDoesNotInvalidateObjects() { |
| Outer t; |
| t.x = 1; |
| t.in.x = 2; |
| t.in.bar(); |
| clang_analyzer_eval(t.x == 1); // expected-warning{{TRUE}} |
| clang_analyzer_eval(t.in.x == 2); // expected-warning{{TRUE}} |
| } |
| |
| // --- Versions of the above tests where the const method is inherited --- // |
| |
| struct B1 { |
| void foo() const; |
| }; |
| |
| struct D1 : public B1 { |
| int x; |
| }; |
| |
| struct D2 : public B1 { |
| mutable int mut; |
| }; |
| |
| struct D3 : public B1 { |
| int *p; |
| }; |
| |
| struct DInner : public B1 { |
| int x; |
| int *p; |
| }; |
| |
| struct DOuter : public B1 { |
| int x; |
| DInner in; |
| }; |
| |
| void checkThatInheritedConstMethodDoesNotInvalidateObject() { |
| D1 t; |
| t.x = 1; |
| t.foo(); |
| clang_analyzer_eval(t.x == 1); // expected-warning{{TRUE}} |
| } |
| |
| void checkThatInheritedConstMethodDoesInvalidateMutableFields() { |
| D2 t; |
| t.mut = 1; |
| t.foo(); |
| clang_analyzer_eval(t.mut); // expected-warning{{UNKNOWN}} |
| } |
| |
| void checkThatInheritedConstMethodDoesInvalidatePointedAtMemory() { |
| int x = 1; |
| D3 t; |
| t.p = &x; |
| t.foo(); |
| clang_analyzer_eval(x); // expected-warning{{UNKNOWN}} |
| clang_analyzer_eval(t.p == &x); // expected-warning{{TRUE}} |
| } |
| |
| void checkThatInheritedConstMethodDoesInvalidateContainedPointedAtMemory() { |
| int x = 1; |
| DOuter t; |
| t.x = 2; |
| t.in.x = 3; |
| t.in.p = &x; |
| t.foo(); |
| clang_analyzer_eval(x); // expected-warning{{UNKNOWN}} |
| clang_analyzer_eval(t.x == 2); // expected-warning{{TRUE}} |
| clang_analyzer_eval(t.in.x == 3); // expected-warning{{TRUE}} |
| clang_analyzer_eval(t.in.p == &x); // expected-warning{{TRUE}} |
| } |
| |
| void checkThatInheritedContainedConstMethodDoesNotInvalidateObjects() { |
| DOuter t; |
| t.x = 1; |
| t.in.x = 2; |
| t.in.foo(); |
| clang_analyzer_eval(t.x == 1); // expected-warning{{TRUE}} |
| clang_analyzer_eval(t.in.x == 2); // expected-warning{{TRUE}} |
| } |
| |
| // --- PR21606 --- // |
| |
| struct s1 { |
| void g(const int *i) const; |
| }; |
| |
| struct s2 { |
| void f(int *i) { |
| m_i = i; |
| m_s.g(m_i); |
| if (m_i) |
| *i = 42; // no-warning |
| } |
| |
| int *m_i; |
| s1 m_s; |
| }; |
| |
| void PR21606() |
| { |
| s2().f(0); |
| } |
| |
| // --- PR25392 --- // |
| |
| struct HasConstMemberFunction { |
| public: |
| void constMemberFunction() const; |
| }; |
| |
| HasConstMemberFunction hasNoReturn() { } // expected-warning {{control reaches end of non-void function}} |
| |
| void testUnknownWithConstMemberFunction() { |
| hasNoReturn().constMemberFunction(); |
| } |
| |
| void testNonRegionLocWithConstMemberFunction() { |
| (*((HasConstMemberFunction *)(&&label))).constMemberFunction(); |
| |
| label: return; |
| } |
| |
| // FIXME |
| // When there is a circular reference to an object and a const method is called |
| // the object is not invalidated because TK_PreserveContents has already been |
| // set. |
| struct Outer2; |
| |
| struct InnerWithRef { |
| Outer2 *ref; |
| }; |
| |
| struct Outer2 { |
| int x; |
| InnerWithRef in; |
| void foo() const; |
| }; |
| |
| void checkThatConstMethodCallDoesInvalidateObjectForCircularReferences() { |
| Outer2 t; |
| t.x = 1; |
| t.in.ref = &t; |
| t.foo(); |
| // FIXME: Should be UNKNOWN. |
| clang_analyzer_eval(t.x); // expected-warning{{TRUE}} |
| } |