Skip to content

Commit

Permalink
[analyzer] Fix invalidation on C++ const methods with arrow syntax.
Browse files Browse the repository at this point in the history
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

llvm-svn: 335555
  • Loading branch information
haoNoQ committed Jun 25, 2018
1 parent a157b8b commit f74ef4b
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 2 deletions.
9 changes: 7 additions & 2 deletions clang/lib/StaticAnalyzer/Core/CallEvent.cpp
Expand Up @@ -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();
Expand Down
24 changes: 24 additions & 0 deletions clang/test/Analysis/const-method-call.cpp
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit f74ef4b

Please sign in to comment.