]> granicus.if.org Git - clang/commitdiff
[analyzer] Fix invalidation on C++ const methods with arrow syntax.
authorArtem Dergachev <artem.dergachev@gmail.com>
Mon, 25 Jun 2018 23:43:45 +0000 (23:43 +0000)
committerArtem Dergachev <artem.dergachev@gmail.com>
Mon, 25 Jun 2018 23:43:45 +0000 (23:43 +0000)
Conservative evaluation of a C++ method call would invalidate the object,
as long as the method is not const or the object has mutable fields.

When checking for mutable fields, we need to scan the type of the object on
which the method is called, which may be more specific than the type of the
object on which the method is defined, hence we look up the type from the
this-argument expression.

If arrow syntax or implicit-this syntax is used, this-argument expression
has pointer type, not record type, and lookup accidentally failed for that
reason. Obtain object type correctly.

Differential Revision: https://reviews.llvm.org/D48460

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@335555 91177308-0d34-0410-b5e6-96231b3b80d8

lib/StaticAnalyzer/Core/CallEvent.cpp
test/Analysis/const-method-call.cpp

index a7d0f3894b7d49417e7b9136070d4a0c630a6510..2b2de16dbdfb94f5bffe0f6f259866435d5a527f 100644 (file)
@@ -534,9 +534,14 @@ void CXXInstanceCall::getExtraInvalidatedValues(
     // 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 17df2a016b89267d49de7337912f1c0d6b975f7e..a5a38929c22fe86b7868ea44c60f3cad509d6705 100644 (file)
@@ -6,6 +6,14 @@ 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 {
@@ -108,6 +116,22 @@ void checkThatContainedConstMethodDoesNotInvalidateObjects() {
   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 {