Index: lib/StaticAnalyzer/Core/CallEvent.cpp =================================================================== --- lib/StaticAnalyzer/Core/CallEvent.cpp +++ lib/StaticAnalyzer/Core/CallEvent.cpp @@ -403,7 +403,40 @@ return getSVal(CE->getCallee()).getAsFunctionDecl(); } +/// Get all mutable fields of Record and its base classes. +static void getMutableFields(const CXXRecordDecl *Record, + SmallVector &MutFields) { + if (Record == nullptr) + return; + for (auto F : Record->fields()) { + if (F->isMutable()) + MutFields.push_back(F); + } + for (auto C : Record->bases()) { + getMutableFields(C.getType()->getAsCXXRecordDecl(), MutFields); + } +} + 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->isConst()) { + // Get any mutable members and invalidate them. + SmallVector MutableFields; + getMutableFields(D->getParent(), MutableFields); + if (!MutableFields.empty()) { + const MemRegion *ThisRegion = getCXXThisVal().getAsRegion(); + assert(ThisRegion && "CXXThisVal was not a region"); + MemRegionManager *MemMgr = ThisRegion->getMemRegionManager(); + for (auto it = MutableFields.begin(), end = MutableFields.end(); + it != end; ++it) { + const FieldRegion *FR = MemMgr->getFieldRegion(*it, 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); +} Index: test/Analysis/method-call.cpp =================================================================== --- test/Analysis/method-call.cpp +++ test/Analysis/method-call.cpp @@ -13,6 +13,25 @@ int x; }; +struct C { + int x; + void foo() const; + void bar(); +}; + +struct D { + mutable int x; + void foo() const; +}; + +struct Base { + mutable int x; +}; + +struct Derived : Base { + 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 +64,27 @@ 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}} +} + +void checkThatConstMethodDoesInvalidateInheritedMutableFields() { + Derived t; + t.x = 3; + t.foo(); + clang_analyzer_eval(t.x); // expected-warning{{UNKNOWN}} +}