| // RUN: %clang_analyze_cc1 -Wno-error=return-type -analyzer-checker=core,debug.ExprInspection -verify -analyzer-config eagerly-assume=false %s |
| |
| void clang_analyzer_eval(bool); |
| |
| struct A { |
| int x; |
| void foo() const; |
| void bar(); |
| |
| void testImplicitThisSyntax() { |
| x = 3; |
| foo(); |
| clang_analyzer_eval(x == 3); // expected-warning{{TRUE}} |
| bar(); |
| clang_analyzer_eval(x == 3); // expected-warning{{UNKNOWN}} |
| } |
| }; |
| |
| 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}} |
| } |
| |
| void checkPointerTypedThisExpression(A *a) { |
| a->x = 3; |
| a->foo(); |
| clang_analyzer_eval(a->x == 3); // expected-warning{{TRUE}} |
| a->bar(); |
| clang_analyzer_eval(a->x == 3); // expected-warning{{UNKNOWN}} |
| } |
| |
| void checkReferenceTypedThisExpression(A &a) { |
| a.x = 3; |
| a.foo(); |
| clang_analyzer_eval(a.x == 3); // expected-warning{{TRUE}} |
| a.bar(); |
| clang_analyzer_eval(a.x == 3); // expected-warning{{UNKNOWN}} |
| } |
| |
| // --- 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 {{non-void function does not return a value}} |
| |
| 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}} |
| } |
| |
| namespace gh77378 { |
| template <typename Signature> class callable; |
| |
| template <typename R> class callable<R()> { |
| struct CallableType { |
| bool operator()(); |
| }; |
| using MethodType = R (CallableType::*)(); |
| CallableType *object_{nullptr}; |
| MethodType method_; |
| |
| public: |
| callable() = default; |
| |
| template <typename T> |
| constexpr callable(const T &obj) |
| : object_{reinterpret_cast<CallableType *>(&const_cast<T &>(obj))}, |
| method_{reinterpret_cast<MethodType>( |
| static_cast<bool (T::*)() const>(&T::operator()))} {} |
| |
| constexpr bool const_method() const { |
| return (object_->*(method_))(); |
| } |
| |
| callable call() const & { |
| static const auto L = [this]() { |
| while (true) { |
| // This should not crash when conservative eval calling the member function |
| // when it unwinds the call stack due to exhausting the budget or reaching |
| // the inlining limit. |
| if (this->const_method()) { |
| break; |
| } |
| } |
| return true; |
| }; |
| return L; |
| } |
| }; |
| |
| void entry() { |
| callable<bool()>{}.call().const_method(); |
| // expected-warning@-1 {{Address of stack memory associated with temporary object of type 'callable<bool ()>' is still referred to by the static variable 'L' upon returning to the caller. This will be a dangling reference}} |
| } |
| } // namespace gh77378 |