Index: include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -164,7 +164,8 @@ /// \brief Used to specify non-argument regions that will be invalidated as a /// result of this call. - virtual void getExtraInvalidatedValues(ValueList &Values) const {} + virtual void getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const {} public: virtual ~CallEvent() {} @@ -472,7 +473,8 @@ BlockCall(const BlockCall &Other) : CallEvent(Other) {} void cloneTo(void *Dest) const override { new (Dest) BlockCall(*this); } - void getExtraInvalidatedValues(ValueList &Values) const override; + void getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const override; public: virtual const CallExpr *getOriginExpr() const { @@ -521,7 +523,8 @@ /// it is written. class CXXInstanceCall : public AnyFunctionCall { protected: - void getExtraInvalidatedValues(ValueList &Values) const override; + void getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const override; CXXInstanceCall(const CallExpr *CE, ProgramStateRef St, const LocationContext *LCtx) @@ -704,7 +707,8 @@ CXXConstructorCall(const CXXConstructorCall &Other) : AnyFunctionCall(Other){} void cloneTo(void *Dest) const override { new (Dest) CXXConstructorCall(*this); } - void getExtraInvalidatedValues(ValueList &Values) const override; + void getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const override; public: virtual const CXXConstructExpr *getOriginExpr() const { @@ -803,7 +807,8 @@ ObjCMethodCall(const ObjCMethodCall &Other) : CallEvent(Other) {} void cloneTo(void *Dest) const override { new (Dest) ObjCMethodCall(*this); } - void getExtraInvalidatedValues(ValueList &Values) const override; + void getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const override; /// Check if the selector may have multiple definitions (may have overrides). virtual bool canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl, Index: lib/StaticAnalyzer/Core/CallEvent.cpp =================================================================== --- lib/StaticAnalyzer/Core/CallEvent.cpp +++ lib/StaticAnalyzer/Core/CallEvent.cpp @@ -148,7 +148,7 @@ SmallVector ValuesToInvalidate; RegionAndSymbolInvalidationTraits ETraits; - getExtraInvalidatedValues(ValuesToInvalidate); + getExtraInvalidatedValues(ValuesToInvalidate, &ETraits); // Indexes of arguments whose values will be preserved by the call. llvm::SmallSet PreserveArgs; @@ -403,8 +403,20 @@ return getSVal(CE->getCallee()).getAsFunctionDecl(); } -void CXXInstanceCall::getExtraInvalidatedValues(ValueList &Values) const { - Values.push_back(getCXXThisVal()); +void CXXInstanceCall::getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const { + SVal ThisVal = getCXXThisVal(); + Values.push_back(ThisVal); + + // Don't invalitate if the method is const and there are no mutable fields + if (const CXXMethodDecl *D = cast_or_null(getDecl())) { + if (D->isConst() && !D->getParent()->hasMutableFields()) { + const MemRegion *ThisRegion = ThisVal.getAsRegion(); + assert(ThisRegion && "ThisValue was not a memory region"); + ETraits->setTrait(ThisRegion->StripCasts(), + RegionAndSymbolInvalidationTraits::TK_PreserveContents); + } + } } SVal CXXInstanceCall::getCXXThisVal() const { @@ -550,7 +562,8 @@ return D->parameters(); } -void BlockCall::getExtraInvalidatedValues(ValueList &Values) const { +void BlockCall::getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const { // FIXME: This also needs to invalidate captured globals. if (const MemRegion *R = getBlockRegion()) Values.push_back(loc::MemRegionVal(R)); @@ -571,7 +584,8 @@ return UnknownVal(); } -void CXXConstructorCall::getExtraInvalidatedValues(ValueList &Values) const { +void CXXConstructorCall::getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const { if (Data) Values.push_back(loc::MemRegionVal(static_cast(Data))); } @@ -613,7 +627,8 @@ } void -ObjCMethodCall::getExtraInvalidatedValues(ValueList &Values) const { +ObjCMethodCall::getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const { Values.push_back(getReceiverSVal()); } 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/const-method-call.cpp =================================================================== --- test/Analysis/const-method-call.cpp +++ test/Analysis/const-method-call.cpp @@ -0,0 +1,89 @@ +// RUN: %clang_cc1 -analyze -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 Base { + mutable int b_mut; + int *p; +}; + +struct Derived : Base { + void foo() const; +}; + +struct Inner { + int *p; +}; + +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}} +} + +void checkThatConstMethodDoesInvalidateInheritedMutableFields() { + Derived t; + t.b_mut = 4; + t.foo(); + clang_analyzer_eval(t.b_mut); // expected-warning{{UNKNOWN}} +} + +void checkThatConstMethodDoesInvalidateInheritedPointedAtMemory() { + int x = 1; + Derived t; + t.p = &x; + t.foo(); + clang_analyzer_eval(x); // expected-warning{{UNKNOWN}} +} + +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}} +}