Index: cfe/trunk/test/Analysis/exploded-graph-rewriter/constraints.dot =================================================================== --- cfe/trunk/test/Analysis/exploded-graph-rewriter/constraints.dot +++ cfe/trunk/test/Analysis/exploded-graph-rewriter/constraints.dot @@ -20,6 +20,7 @@ "store": null, "environment": null, "dynamic_types": null, + "constructing_objects": null, "constraints": [ { "symbol": "reg_$0", "range": "{ [0, 0] }" } ] Index: cfe/trunk/test/Analysis/exploded-graph-rewriter/constraints_diff.dot =================================================================== --- cfe/trunk/test/Analysis/exploded-graph-rewriter/constraints_diff.dot +++ cfe/trunk/test/Analysis/exploded-graph-rewriter/constraints_diff.dot @@ -13,6 +13,7 @@ "store": null, "environment": null, "dynamic_types": null, + "constructing_objects": null, "constraints": [ { "symbol": "reg_$0", "range": "{ [0, 10] }" } ] @@ -43,6 +44,7 @@ "store": null, "environment": null, "dynamic_types": null, + "constructing_objects": null, "constraints": [ { "symbol": "reg_$0", "range": "{ [0, 5] }" } ] @@ -62,7 +64,8 @@ "store": null, "environment": null, "constraints": null, - "dynamic_types": null + "dynamic_types": null, + "constructing_objects": null } } \l}"]; Index: cfe/trunk/test/Analysis/exploded-graph-rewriter/environment.dot =================================================================== --- cfe/trunk/test/Analysis/exploded-graph-rewriter/environment.dot +++ cfe/trunk/test/Analysis/exploded-graph-rewriter/environment.dot @@ -35,6 +35,7 @@ "store": null, "constraints": null, "dynamic_types": null, + "constructing_objects": null, "environment": { "pointer": "0x2", "items": [ Index: cfe/trunk/test/Analysis/exploded-graph-rewriter/environment_diff.dot =================================================================== --- cfe/trunk/test/Analysis/exploded-graph-rewriter/environment_diff.dot +++ cfe/trunk/test/Analysis/exploded-graph-rewriter/environment_diff.dot @@ -14,6 +14,7 @@ "store": null, "constraints": null, "dynamic_types": null, + "constructing_objects": null, "environment": { "pointer": "0x2", "items": [ @@ -61,6 +62,7 @@ "store": null, "constraints": null, "dynamic_types": null, + "constructing_objects": null, "environment": { "pointer": "0x2", "items": [ @@ -102,6 +104,7 @@ "store": null, "constraints": null, "dynamic_types": null, + "constructing_objects": null, "environment": { "pointer": "0x2", "items": [ Index: cfe/trunk/test/Analysis/exploded-graph-rewriter/initializers_under_construction.cpp =================================================================== --- cfe/trunk/test/Analysis/exploded-graph-rewriter/initializers_under_construction.cpp +++ cfe/trunk/test/Analysis/exploded-graph-rewriter/initializers_under_construction.cpp @@ -0,0 +1,25 @@ +// FIXME: Figure out how to use %clang_analyze_cc1 with our lit.local.cfg. +// RUN: %clang_cc1 -analyze -triple x86_64-unknown-linux-gnu \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-dump-egraph=%t.dot %s +// RUN: %exploded_graph_rewriter %t.dot | FileCheck %s +// REQUIRES: asserts + +// FIXME: Substitution doesn't seem to work on Windows. +// UNSUPPORTED: system-windows + +struct A { + A() {} +}; + +struct B { + A a; + B() : a() {} +}; + +void test() { + // CHECK: (construct into member variable) + // CHECK-SAME: a + // CHECK-SAME: &b->a + B b; +} Index: cfe/trunk/test/Analysis/exploded-graph-rewriter/objects_under_construction.cpp =================================================================== --- cfe/trunk/test/Analysis/exploded-graph-rewriter/objects_under_construction.cpp +++ cfe/trunk/test/Analysis/exploded-graph-rewriter/objects_under_construction.cpp @@ -0,0 +1,48 @@ +// FIXME: Figure out how to use %clang_analyze_cc1 with our lit.local.cfg. +// RUN: %clang_cc1 -analyze -triple x86_64-unknown-linux-gnu \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-dump-egraph=%t.dot %s +// RUN: %exploded_graph_rewriter %t.dot | FileCheck %s +// REQUIRES: asserts + +// FIXME: Substitution doesn't seem to work on Windows. +// UNSUPPORTED: system-windows + +struct S { + S() {} +}; + +void test() { + // CHECK: Objects Under Construction: + // CHECK-SAME: + // CHECK-SAME: #0 Call + // CHECK-SAME: + // CHECK-SAME: test + // CHECK-SAME: + // CHECK-SAME: + // CHECK-SAME: + // CHECK-SAME: S{{[0-9]*}} + // CHECK-SAME: + // CHECK-SAME: (materialize temporary) + // CHECK-SAME: + // CHECK-SAME: S() + // CHECK-SAME: &s + // CHECK-SAME: + // CHECK-SAME: + // CHECK-SAME: S{{[0-9]*}} + // CHECK-SAME: + // CHECK-SAME: (elide constructor) + // CHECK-SAME: + // CHECK-SAME: S() + // CHECK-SAME: &s + // CHECK-SAME: + // CHECK-SAME: + // CHECK-SAME: S{{[0-9]*}} + // CHECK-SAME: + // CHECK-SAME: (construct into local variable) + // CHECK-SAME: + // CHECK-SAME: S s = S(); + // CHECK-SAME: &s + // CHECK-SAME: + S s = S(); +} Index: cfe/trunk/test/Analysis/exploded-graph-rewriter/store.dot =================================================================== --- cfe/trunk/test/Analysis/exploded-graph-rewriter/store.dot +++ cfe/trunk/test/Analysis/exploded-graph-rewriter/store.dot @@ -30,6 +30,7 @@ "environment": null, "constraints": null, "dynamic_types": null, + "constructing_objects": null, "store": { "pointer": "0x2", "items": [ Index: cfe/trunk/test/Analysis/exploded-graph-rewriter/store_diff.dot =================================================================== --- cfe/trunk/test/Analysis/exploded-graph-rewriter/store_diff.dot +++ cfe/trunk/test/Analysis/exploded-graph-rewriter/store_diff.dot @@ -13,6 +13,7 @@ "environment": null, "constraints": null, "dynamic_types": null, + "constructing_objects": null, "store": { "pointer": "0x2", "items": [ @@ -59,6 +60,7 @@ "environment": null, "constraints": null, "dynamic_types": null, + "constructing_objects": null, "store": { "pointer": "0x5", "items": [ Index: cfe/trunk/utils/analyzer/exploded-graph-rewriter.py =================================================================== --- cfe/trunk/utils/analyzer/exploded-graph-rewriter.py +++ cfe/trunk/utils/analyzer/exploded-graph-rewriter.py @@ -71,8 +71,11 @@ class EnvironmentBindingKey(object): def __init__(self, json_ek): super(EnvironmentBindingKey, self).__init__() - self.stmt_id = json_ek['stmt_id'] + # CXXCtorInitializer is not a Stmt! + self.stmt_id = json_ek['stmt_id'] if 'stmt_id' in json_ek \ + else json_ek['init_id'] self.pretty = json_ek['pretty'] + self.kind = json_ek['kind'] if 'kind' in json_ek else None def _key(self): return self.stmt_id @@ -122,12 +125,12 @@ return len(removed) != 0 or len(added) != 0 -# A deserialized Environment. -class Environment(object): +# A deserialized Environment. This class can also hold other entities that +# are similar to Environment, such as Objects Under Construction. +class GenericEnvironment(object): def __init__(self, json_e): - super(Environment, self).__init__() - self.ptr = json_e['pointer'] - self.frames = [EnvironmentFrame(f) for f in json_e['items']] + super(GenericEnvironment, self).__init__() + self.frames = [EnvironmentFrame(f) for f in json_e] def diff_frames(self, prev): # TODO: It's difficult to display a good diff when frame numbers shift. @@ -214,13 +217,18 @@ logging.debug('Adding ProgramState ' + str(state_id)) self.state_id = state_id + self.store = Store(json_ps['store']) \ if json_ps['store'] is not None else None - self.environment = Environment(json_ps['environment']) \ + + self.environment = \ + GenericEnvironment(json_ps['environment']['items']) \ if json_ps['environment'] is not None else None + self.constraints = GenericMap([ (c['symbol'], c['range']) for c in json_ps['constraints'] ]) if json_ps['constraints'] is not None else None + self.dynamic_types = GenericMap([ (t['region'], '%s%s' % (t['dyn_type'], ' (or a sub-class)' @@ -228,7 +236,10 @@ for t in json_ps['dynamic_types']]) \ if json_ps['dynamic_types'] is not None else None - # TODO: Objects under construction. + self.constructing_objects = \ + GenericEnvironment(json_ps['constructing_objects']) \ + if json_ps['constructing_objects'] is not None else None + # TODO: Checker messages. @@ -416,10 +427,15 @@ def dump_binding(f, b, is_added=None): self._dump('%s' 'S%s' + '%s' '%s' '%s' % (self._diff_plus_minus(is_added), - b.stmt_id, b.pretty, f.bindings[b])) + b.stmt_id, + '' + '(%s)' % b.kind + if b.kind is not None else '', + b.pretty, f.bindings[b])) frames_updated = e.diff_frames(prev_e) if prev_e is not None else None if frames_updated: @@ -440,20 +456,25 @@ self._dump('') - def visit_environment_in_state(self, s, prev_s=None): - self._dump('
Environment: ') - if s.environment is None: + def visit_environment_in_state(self, selector, title, s, prev_s=None): + e = getattr(s, selector) + prev_e = getattr(prev_s, selector) if prev_s is not None else None + if e is None and prev_e is None: + return + + self._dump('
%s: ' % title) + if e is None: self._dump(' Nothing!') else: - if prev_s is not None and prev_s.environment is not None: - if s.environment.is_different(prev_s.environment): + if prev_e is not None: + if e.is_different(prev_e): self._dump('') - self.visit_environment(s.environment, prev_s.environment) + self.visit_environment(e, prev_e) else: self._dump(' No changes!') else: self._dump('') - self.visit_environment(s.environment) + self.visit_environment(e) self._dump('') @@ -496,19 +517,24 @@ self._dump('') def visit_store_in_state(self, s, prev_s=None): + st = s.store + prev_st = prev_s.store if prev_s is not None else None + if st is None and prev_st is None: + return + self._dump('
Store: ') - if s.store is None: + if st is None: self._dump(' Nothing!') else: - if prev_s is not None and prev_s.store is not None: - if s.store.is_different(prev_s.store): + if prev_st is not None: + if s.store.is_different(prev_st): self._dump('') - self.visit_store(s.store, prev_s.store) + self.visit_store(st, prev_st) else: self._dump(' No changes!') else: self._dump('') - self.visit_store(s.store) + self.visit_store(st) self._dump('') def visit_generic_map(self, m, prev_m=None): @@ -559,11 +585,15 @@ def visit_state(self, s, prev_s): self.visit_store_in_state(s, prev_s) - self.visit_environment_in_state(s, prev_s) + self.visit_environment_in_state('environment', 'Environment', + s, prev_s) self.visit_generic_map_in_state('constraints', 'Ranges', s, prev_s) self.visit_generic_map_in_state('dynamic_types', 'Dynamic Types', s, prev_s) + self.visit_environment_in_state('constructing_objects', + 'Objects Under Construction', + s, prev_s) def visit_node(self, node): self._dump('%s [shape=record,label=<'