anchor.diff:    }
   }
   
  +
   class IncrementalResolverTest extends ResolverTestCase {
  -  void test_resolve() {
  -    MethodDeclaration method = _resolveMethod(r'''
  -class C {
  -  int m(int a) {
  -    return a + a;
  -  }
  +  Source source;
  +  String code;
  +  LibraryElement library;
  +  CompilationUnit unit;
  +
  +  void fail_test_functionBody_addLocalVariable() {
  +    // TODO(scheglov) this test fails, because we don't create element models
  +    // for new local variables
  +    _resolveUnit(r'''
  +main(int a, int b) {
  +  return a + b;
  +}
  +''');
  +    _resolve(_editString('  return a + b;', r'''
  +  int res = a + b;
  +  return res;
  +'''), _isBlock);
  +  }
  +
  +  void test_functionBody_body() {
  +    _resolveUnit(r'''
  +main(int a, int b) {
  +  return a + b;
   }''');
  -    BlockFunctionBody body = method.body as BlockFunctionBody;
  -    ReturnStatement statement = body.block.statements[0] as ReturnStatement;
  -    BinaryExpression expression = statement.expression as BinaryExpression;
  -    SimpleIdentifier left = expression.leftOperand as SimpleIdentifier;
  -    Element leftElement = left.staticElement;
  -    SimpleIdentifier right = expression.rightOperand as SimpleIdentifier;
  -    Element rightElement = right.staticElement;
  -    expect(leftElement, isNotNull);
  -    expect(rightElement, same(leftElement));
  -  }
  -
  -  MethodDeclaration _resolveMethod(String content) {
  -    Source source = addSource(content);
  -    LibraryElement library = resolve(source);
  -    CompilationUnit unit = resolveCompilationUnit(source, library);
  -    ClassDeclaration classNode = unit.declarations[0] as ClassDeclaration;
  -    MethodDeclaration method = classNode.members[0] as MethodDeclaration;
  -    method.body.accept(new ResolutionEraser());
  +    _resolve(_editString('+', '*'), _isFunctionBody);
  +  }
  +
  +  void test_functionBody_statement() {
  +    _resolveUnit(r'''
  +main(int a, int b) {
  +  return a + b;
  +}''');
  +    _resolve(_editString('+', '*'), _isStatement);
  +  }
  +
  +  _Edit _editString(String search, String replacement, [int length]) {
  +    int offset = code.indexOf(search);
  +    expect(offset, isNot(-1));
  +    if (length == null) {
  +      length = search.length;
  +    }
  +    return new _Edit(offset, length, replacement);
  +  }
  +
  +  /**
  +   * Applies [edit] to [code], find the [AstNode] specified by [predicate]
  +   * and incrementally resolves it.
  +   *
  +   * Then resolves the new code from scratch and validates that results of
  +   * the incremental resolution and non-incremental resolutions are the same.
  +   */
  +  void _resolve(_Edit edit, Predicate<AstNode> predicate) {
  +    int offset = edit.offset;
  +    // parse "newCode"
  +    String newCode =
  +        code.substring(0, offset) +
  +        edit.replacement +
  +        code.substring(offset + edit.length);
  +    CompilationUnit newUnit = _parseUnit(newCode);
  +    // replace the node
  +    AstNode oldNode = _findNodeAt(unit, offset, predicate);
  +    AstNode newNode = _findNodeAt(newUnit, offset, predicate);
  +    bool success = NodeReplacer.replace(oldNode, newNode);
  +    expect(success, isTrue);
  +    // do incremental resolution
       GatheringErrorListener errorListener = new GatheringErrorListener();
  +    // TODO(scheglov) use 'replacement' to update elements offsets
       IncrementalResolver resolver =
           new IncrementalResolver(library, source, typeProvider, errorListener);
  -    resolver.resolve(method.body);
  -    return method;
  +    resolver.resolve(newNode);
  +    // resolve "newCode" from scratch
  +    CompilationUnit fullNewUnit;
  +    {
  +      source = addSource(newCode);
  +      LibraryElement library = resolve(source);
  +      fullNewUnit = resolveCompilationUnit(source, library);
  +    }
  +    _SameResolutionValidator.assertSameResolution(unit, fullNewUnit);
  +  }
  +
  +  void _resolveUnit(String code) {
  +    this.code = code;
  +    source = addSource(code);
  +    library = resolve(source);
  +    unit = resolveCompilationUnit(source, library);
  +  }
  +
  +  static AstNode _findNodeAt(CompilationUnit oldUnit, int offset,
  +      Predicate<AstNode> predicate) {
  +    NodeLocator locator = new NodeLocator.con1(offset);
  +    AstNode node = locator.searchWithin(oldUnit);
  +    return node.getAncestor(predicate);
  +  }
  +
  +  static bool _isBlock(AstNode node) => node is Block;
  +
  +  static bool _isFunctionBody(AstNode node) => node is FunctionBody;
  +
  +  static bool _isStatement(AstNode node) => node is Statement;
  +
  +  static CompilationUnit _parseUnit(String code) {
  +    var errorListener = new BooleanErrorListener();
  +    var reader = new CharSequenceReader(code);
  +    var scanner = new Scanner(null, reader, errorListener);
  +    var token = scanner.tokenize();
  +    var parser = new Parser(null, errorListener);
  +    return parser.parseCompilationUnit(token);
     }
   }
  +
   
   class ScopeBuilderTest extends EngineTestCase {
     void test_scopeFor_ClassDeclaration() {
  
positive.code:     return methodNode;
    }
  }
  
negative.code: import 'package:analyzer/src/generated/ast.dart';
  import 'package:analyzer/src/generated/element.dart';
  import 'package:analyzer/src/generated/engine.dart';
  import 'package:analyzer/src/generated/error.dart';
  import 'package:analyzer/src/generated/incremental_resolver.dart';
  import 'package:analyzer/src/generated/java_engine.dart';
  import 'package:analyzer/src/generated/parser.dart';
  import 'package:analyzer/src/generated/resolver.dart';
  import 'package:analyzer/src/generated/scanner.dart';
  import 'package:analyzer/src/generated/source_io.dart';
  import 'package:analyzer/src/generated/testing/ast_factory.dart';
  import 'package:analyzer/src/generated/testing/element_factory.dart';
  