Index: lib/StaticAnalyzer/Core/CallEvent.cpp =================================================================== --- lib/StaticAnalyzer/Core/CallEvent.cpp +++ lib/StaticAnalyzer/Core/CallEvent.cpp @@ -534,9 +534,14 @@ // Get the record decl for the class of 'This'. D->getParent() may return a // base class decl, rather than the class of the instance which needs to be // checked for mutable fields. + // TODO: We might as well look at the dynamic type of the object. const Expr *Ex = getCXXThisExpr()->ignoreParenBaseCasts(); - const CXXRecordDecl *ParentRecord = Ex->getType()->getAsCXXRecordDecl(); - if (!ParentRecord || ParentRecord->hasMutableFields()) + QualType T = Ex->getType(); + if (T->isPointerType()) // Arrow or implicit-this syntax? + T = T->getPointeeType(); + const CXXRecordDecl *ParentRecord = T->getAsCXXRecordDecl(); + assert(ParentRecord); + if (ParentRecord->hasMutableFields()) return; // Preserve CXXThis. const MemRegion *ThisRegion = ThisVal.getAsRegion(); Index: test/Analysis/const-method-call.cpp =================================================================== --- test/Analysis/const-method-call.cpp +++ test/Analysis/const-method-call.cpp @@ -6,6 +6,14 @@ 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 { @@ -108,6 +116,22 @@ 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 {