Index: lib/StaticAnalyzer/Core/CallEvent.cpp =================================================================== --- lib/StaticAnalyzer/Core/CallEvent.cpp +++ lib/StaticAnalyzer/Core/CallEvent.cpp @@ -404,6 +404,24 @@ } void CXXInstanceCall::getExtraInvalidatedValues(ValueList &Values) const { + // Check if this is a call to a const method. + if (const CXXMethodDecl *D = cast_or_null(getDecl())) { + if(D->getCanonicalDecl()->isConst()) { + // Check if the containing class/struct has mutable members + const MemRegion *ThisRegion = getCXXThisVal().getAsRegion(); + if (ThisRegion) { + MemRegionManager *MemMgr = ThisRegion->getMemRegionManager(); + const CXXRecordDecl *Parent = D->getParent(); + for (const auto *I : Parent->fields()) { + if (I->isMutable()) { + const FieldRegion *FR = MemMgr->getFieldRegion(I, ThisRegion); + Values.push_back(loc::MemRegionVal(FR)); + } + } + } + return; + } + } Values.push_back(getCXXThisVal()); } Index: test/Analysis/PR21606.cpp =================================================================== --- test/Analysis/PR21606.cpp +++ test/Analysis/PR21606.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core +// 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; +}; + +int main() +{ + s2().f(0); +} \ No newline at end of file Index: test/Analysis/method-call.cpp =================================================================== --- test/Analysis/method-call.cpp +++ test/Analysis/method-call.cpp @@ -13,6 +13,17 @@ int x; }; +struct C { + int x; + void foo() const; + void bar(); +}; + +struct D { + mutable int x; + void foo() const; +}; + void testNullObject(A *a) { clang_analyzer_eval(a); // expected-warning{{UNKNOWN}} (void)a->getx(); // assume we know what we're doing @@ -45,3 +56,21 @@ B t2(t); clang_analyzer_eval(t.x == 0); // expected-warning{{TRUE}} } + +void checkThatConstMethodWithoutDefinitionDoesNotInvalidateObject() { + C 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() { + D t; + t.x = 3; + t.foo(); + clang_analyzer_eval(t.x); // expected-warning{{UNKNOWN}} +} +