Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(752)

Unified Diff: pkg/analyzer/test/generated/incremental_resolver_test.dart

Issue 2887443002: Revert "cleanup unused incremental resolution" (Closed)
Patch Set: Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « pkg/analyzer/lib/src/generated/incremental_resolver.dart ('k') | pkg/analyzer/test/generated/test_all.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: pkg/analyzer/test/generated/incremental_resolver_test.dart
diff --git a/pkg/analyzer/test/generated/incremental_resolver_test.dart b/pkg/analyzer/test/generated/incremental_resolver_test.dart
new file mode 100644
index 0000000000000000000000000000000000000000..e3605f7dc7a56e161c450759260d5f340caa1146
--- /dev/null
+++ b/pkg/analyzer/test/generated/incremental_resolver_test.dart
@@ -0,0 +1,2411 @@
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library analyzer.test.generated.incremental_resolver_test;
+
+import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/ast/token.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/error/error.dart';
+import 'package:analyzer/error/listener.dart';
+import 'package:analyzer/exception/exception.dart';
+import 'package:analyzer/src/context/cache.dart';
+import 'package:analyzer/src/dart/ast/utilities.dart';
+import 'package:analyzer/src/dart/element/element.dart';
+import 'package:analyzer/src/dart/scanner/reader.dart';
+import 'package:analyzer/src/dart/scanner/scanner.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/incremental_logger.dart' as logging;
+import 'package:analyzer/src/generated/incremental_resolution_validator.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/source_io.dart';
+import 'package:analyzer/src/generated/testing/ast_test_factory.dart';
+import 'package:analyzer/src/generated/testing/element_factory.dart';
+import 'package:analyzer/src/generated/utilities_collection.dart';
+import 'package:analyzer/src/task/dart.dart';
+import 'package:analyzer/task/dart.dart';
+import 'package:analyzer/task/model.dart';
+import 'package:test/test.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+
+import 'analysis_context_factory.dart';
+import 'resolver_test_case.dart';
+import 'test_support.dart';
+
+main() {
+ initializeTestEnvironment();
+ defineReflectiveSuite(() {
+ defineReflectiveTests(IncrementalResolverTest);
+ defineReflectiveTests(PoorMansIncrementalResolutionTest);
+ defineReflectiveTests(ResolutionContextBuilderTest);
+ });
+}
+
+void initializeTestEnvironment() {}
+
+void _assertEqualError(AnalysisError incError, AnalysisError fullError) {
+ if (incError.errorCode != fullError.errorCode ||
+ incError.source != fullError.source ||
+ incError.offset != fullError.offset ||
+ incError.length != fullError.length ||
+ incError.message != fullError.message) {
+ StringBuffer buffer = new StringBuffer();
+ buffer.writeln('Found error does not match expected error:');
+ if (incError.errorCode == fullError.errorCode) {
+ buffer.write(' errorCode = ');
+ buffer.write(fullError.errorCode.uniqueName);
+ } else {
+ buffer.write(' Expected errorCode = ');
+ buffer.write(fullError.errorCode.uniqueName);
+ buffer.write(' found ');
+ buffer.write(incError.errorCode.uniqueName);
+ }
+ buffer.writeln();
+ if (incError.source == fullError.source) {
+ buffer.write(' source = ');
+ buffer.write(fullError.source);
+ } else {
+ buffer.write(' Expected source = ');
+ buffer.write(fullError.source);
+ buffer.write(' found ');
+ buffer.write(incError.source);
+ }
+ buffer.writeln();
+ if (incError.offset == fullError.offset) {
+ buffer.write(' offset = ');
+ buffer.write(fullError.offset);
+ } else {
+ buffer.write(' Expected offset = ');
+ buffer.write(fullError.offset);
+ buffer.write(' found ');
+ buffer.write(incError.offset);
+ }
+ buffer.writeln();
+ if (incError.length == fullError.length) {
+ buffer.write(' length = ');
+ buffer.write(fullError.length);
+ } else {
+ buffer.write(' Expected length = ');
+ buffer.write(fullError.length);
+ buffer.write(' found ');
+ buffer.write(incError.length);
+ }
+ buffer.writeln();
+ if (incError.message == fullError.message) {
+ buffer.write(' message = ');
+ buffer.write(fullError.message);
+ } else {
+ buffer.write(' Expected message = ');
+ buffer.write(fullError.message);
+ buffer.write(' found ');
+ buffer.write(incError.message);
+ }
+ fail(buffer.toString());
+ }
+}
+
+void _assertEqualErrors(
+ List<AnalysisError> incErrors, List<AnalysisError> fullErrors) {
+ expect(incErrors, hasLength(fullErrors.length));
+ if (incErrors.isNotEmpty) {
+ incErrors.sort((a, b) => a.offset - b.offset);
+ }
+ if (fullErrors.isNotEmpty) {
+ fullErrors.sort((a, b) => a.offset - b.offset);
+ }
+ int length = incErrors.length;
+ for (int i = 0; i < length; i++) {
+ AnalysisError incError = incErrors[i];
+ AnalysisError fullError = fullErrors[i];
+ _assertEqualError(incError, fullError);
+ }
+}
+
+void _checkCacheEntries(AnalysisCache cache) {
+ Set seen = new Set();
+ MapIterator<AnalysisTarget, CacheEntry> it = cache.iterator();
+ while (it.moveNext()) {
+ AnalysisTarget key = it.key;
+ if (cache.get(key) == null) {
+ fail("cache corrupted: value of $key changed to null");
+ }
+ if (!seen.add(key)) {
+ fail("cache corrupted: $key appears more than once");
+ }
+ }
+}
+
+@reflectiveTest
+class IncrementalResolverTest extends ResolverTestCase {
+ Source source;
+ String code;
+ LibraryElement library;
+ CompilationUnit unit;
+
+ void setUp() {
+ super.setUp();
+ logging.logger = logging.NULL_LOGGER;
+ }
+
+ void test_classMemberAccessor_body() {
+ _resolveUnit(r'''
+class A {
+ int get test {
+ return 1 + 2;
+ }
+}''');
+ _resolve(_editString('+', '*'), _isFunctionBody);
+ }
+
+ void test_computeConstants_offsetChanged() {
+ _resolveUnit(r'''
+int f() => 0;
+main() {
+ const x1 = f();
+ const x2 = f();
+ const x3 = f();
+ const x4 = f();
+ const x5 = f();
+ print(x1 + x2 + x3 + x4 + x5 + 1);
+}
+''');
+ _resolve(_editString('x1', ' x1'), _isFunctionBody);
+ }
+
+ void test_constructor_body() {
+ _resolveUnit(r'''
+class A {
+ int f;
+ A(int a, int b) {
+ f = a + b;
+ }
+}''');
+ _resolve(_editString('+', '*'), _isFunctionBody);
+ }
+
+ void test_constructor_label_add() {
+ _resolveUnit(r'''
+class A {
+ A() {
+ return 42;
+ }
+}
+''');
+ _resolve(_editString('return', 'label: return'), _isBlock);
+ }
+
+ void test_constructor_localVariable_add() {
+ _resolveUnit(r'''
+class A {
+ A() {
+ 42;
+ }
+}
+''');
+ _resolve(_editString('42;', 'var res = 42;'), _isBlock);
+ }
+
+ void test_function_localFunction_add() {
+ _resolveUnit(r'''
+int main() {
+ return 0;
+}
+callIt(f) {}
+''');
+ _resolve(_editString('return 0;', 'callIt((p) {});'), _isBlock);
+ }
+
+ void test_functionBody_body() {
+ _resolveUnit(r'''
+main(int a, int b) {
+ return a + b;
+}''');
+ _resolve(_editString('+', '*'), _isFunctionBody);
+ }
+
+ void test_functionBody_statement() {
+ _resolveUnit(r'''
+main(int a, int b) {
+ return a + b;
+}''');
+ _resolve(_editString('+', '*'), _isStatement);
+ }
+
+ void test_method_body() {
+ _resolveUnit(r'''
+class A {
+ m(int a, int b) {
+ return a + b;
+ }
+}''');
+ _resolve(_editString('+', '*'), _isFunctionBody);
+ }
+
+ void test_method_label_add() {
+ _resolveUnit(r'''
+class A {
+ int m(int a, int b) {
+ return a + b;
+ }
+}
+''');
+ _resolve(_editString('return', 'label: return'), _isBlock);
+ }
+
+ void test_method_localFunction_add() {
+ _resolveUnit(r'''
+class A {
+ int m() {
+ return 0;
+ }
+}
+callIt(f) {}
+''');
+ _resolve(_editString('return 0;', 'callIt((p) {});'), _isBlock);
+ }
+
+ void test_method_localVariable_add() {
+ _resolveUnit(r'''
+class A {
+ int m(int a, int b) {
+ return a + b;
+ }
+}
+''');
+ _resolve(
+ _editString(
+ ' return a + b;',
+ r'''
+ int res = a + b;
+ return res;
+'''),
+ _isBlock);
+ }
+
+ void test_superInvocation() {
+ _resolveUnit(r'''
+class A {
+ foo(p) {}
+}
+class B extends A {
+ bar() {
+ super.foo(1 + 2);
+ }
+}''');
+ _resolve(_editString('+', '*'), _isFunctionBody);
+ }
+
+ void test_topLevelAccessor_body() {
+ _resolveUnit(r'''
+int get test {
+ return 1 + 2;
+}''');
+ _resolve(_editString('+', '*'), _isFunctionBody);
+ }
+
+ void test_topLevelFunction_label_add() {
+ _resolveUnit(r'''
+int main(int a, int b) {
+ return a + b;
+}
+''');
+ _resolve(_editString(' return', 'label: return a + b;'), _isBlock);
+ }
+
+ void test_topLevelFunction_label_remove() {
+ _resolveUnit(r'''
+int main(int a, int b) {
+ label: return a + b;
+}
+''');
+ _resolve(_editString('label: ', ''), _isBlock);
+ }
+
+ void test_topLevelFunction_localVariable_add() {
+ _resolveUnit(r'''
+int main(int a, int b) {
+ return a + b;
+}
+''');
+ _resolve(
+ _editString(
+ ' return a + b;',
+ r'''
+ int res = a + b;
+ return res;
+'''),
+ _isBlock);
+ }
+
+ void test_topLevelFunction_localVariable_remove() {
+ _resolveUnit(r'''
+int main(int a, int b) {
+ int res = a * b;
+ return a + b;
+}
+''');
+ _resolve(_editString('int res = a * b;', ''), _isBlock);
+ }
+
+ void test_updateElementOffset() {
+ _resolveUnit(r'''
+class A {
+ int am(String ap) {
+ int av = 1;
+ return av;
+ }
+}
+main(int a, int b) {
+ return a + b;
+}
+class B {
+ int bm(String bp) {
+ int bv = 1;
+ return bv;
+ }
+}
+''');
+ _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);
+ AnalysisCache cache = analysisContext2.analysisCache;
+ _checkCacheEntries(cache);
+
+ // replace the node
+ AstNode oldNode = _findNodeAt(unit, offset, predicate);
+ AstNode newNode = _findNodeAt(newUnit, offset, predicate);
+ {
+ bool success = NodeReplacer.replace(oldNode, newNode);
+ expect(success, isTrue);
+ }
+ // update tokens
+ {
+ int delta = edit.replacement.length - edit.length;
+ _shiftTokens(unit.beginToken, offset, delta);
+ Token oldBeginToken = oldNode.beginToken;
+ Token oldEndTokenNext = oldNode.endToken.next;
+ oldBeginToken.previous.setNext(newNode.beginToken);
+ newNode.endToken.setNext(oldEndTokenNext);
+ }
+ // do incremental resolution
+ int updateOffset = edit.offset;
+ int updateEndOld = updateOffset + edit.length;
+ int updateOldNew = updateOffset + edit.replacement.length;
+ IncrementalResolver resolver;
+ LibrarySpecificUnit lsu = new LibrarySpecificUnit(source, source);
+ resolver = new IncrementalResolver(cache, cache.get(source), cache.get(lsu),
+ unit.element, updateOffset, updateEndOld, updateOldNew);
+
+ BlockFunctionBody body = newNode.getAncestor((n) => n is BlockFunctionBody);
+ expect(body, isNotNull);
+
+ resolver.resolve(body);
+ _checkCacheEntries(cache);
+
+ List<AnalysisError> newErrors = analysisContext.computeErrors(source);
+ // resolve "newCode" from scratch
+ CompilationUnit fullNewUnit;
+ {
+ source = addSource(newCode);
+ _runTasks();
+ LibraryElement library = resolve2(source);
+ fullNewUnit = resolveCompilationUnit(source, library);
+ }
+ _checkCacheEntries(cache);
+
+ assertSameResolution(unit, fullNewUnit);
+ // errors
+ List<AnalysisError> newFullErrors =
+ analysisContext.getErrors(source).errors;
+ _assertEqualErrors(newErrors, newFullErrors);
+ // prepare for the next cycle
+ code = newCode;
+ }
+
+ void _resolveUnit(String code) {
+ this.code = code;
+ source = addSource(code);
+ library = resolve2(source);
+ unit = resolveCompilationUnit(source, library);
+ _runTasks();
+ _checkCacheEntries(analysisContext2.analysisCache);
+ }
+
+ void _runTasks() {
+ AnalysisResult result = analysisContext.performAnalysisTask();
+ while (result.changeNotices != null) {
+ result = analysisContext.performAnalysisTask();
+ }
+ }
+
+ static AstNode _findNodeAt(
+ CompilationUnit oldUnit, int offset, Predicate<AstNode> predicate) {
+ NodeLocator locator = new NodeLocator(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);
+ }
+
+ static void _shiftTokens(Token token, int afterOffset, int delta) {
+ while (true) {
+ if (token.offset > afterOffset) {
+ token.applyDelta(delta);
+ }
+ if (token.type == TokenType.EOF) {
+ break;
+ }
+ token = token.next;
+ }
+ }
+}
+
+/**
+ * The test for [poorMansIncrementalResolution] function and its integration
+ * into [AnalysisContext].
+ */
+@reflectiveTest
+class PoorMansIncrementalResolutionTest extends ResolverTestCase {
+ final _TestLogger logger = new _TestLogger();
+
+ Source source;
+ String code;
+ LibraryElement oldLibrary;
+ CompilationUnit oldUnit;
+ CompilationUnitElement oldUnitElement;
+
+ @override
+ void setUp() {
+ super.setUp();
+ _resetWithIncremental(true);
+ }
+
+ void test_computeConstants() {
+ _resolveUnit(r'''
+int f() => 0;
+main() {
+ const x = f();
+ print(x + 1);
+}
+''');
+ _updateAndValidate(
+ r'''
+int f() => 0;
+main() {
+ const x = f();
+ print(x + 2);
+}
+''',
+ expectCachePostConstantsValid: false);
+ }
+
+ void test_dartDoc_beforeField() {
+ _resolveUnit(r'''
+class A {
+ /**
+ * A field [field] of type [int] in class [A].
+ */
+ int field;
+}
+''');
+ _updateAndValidate(r'''
+class A {
+ /**
+ * A field [field] of the type [int] in the class [A].
+ * Updated, with a reference to the [String] type.
+ */
+ int field;
+}
+''');
+ }
+
+ void test_dartDoc_beforeTopLevelVariable() {
+ _resolveUnit(r'''
+/**
+ * Variables [V1] and [V2] of type [int].
+ */
+int V1, V2;
+''');
+ _updateAndValidate(r'''
+/**
+ * Variables [V1] and [V2] of type [int].
+ * Updated, with a reference to the [String] type.
+ */
+int V1, V2;
+''');
+ }
+
+ void test_dartDoc_clumsy_addReference() {
+ _resolveUnit(r'''
+/**
+ * aaa bbbb
+ */
+main() {
+}
+''');
+ _updateAndValidate(r'''
+/**
+ * aaa [main] bbbb
+ */
+main() {
+}
+''');
+ }
+
+ void test_dartDoc_clumsy_removeReference() {
+ _resolveUnit(r'''
+/**
+ * aaa [main] bbbb
+ */
+main() {
+}
+''');
+ _updateAndValidate(r'''
+/**
+ * aaa bbbb
+ */
+main() {
+}
+''');
+ }
+
+ void test_dartDoc_clumsy_updateText_beforeKeywordToken() {
+ _resolveUnit(r'''
+/**
+ * A comment with the [int] type reference.
+ */
+class A {}
+''');
+ _updateAndValidate(r'''
+/**
+ * A comment with the [int] type reference.
+ * Plus reference to [A] itself.
+ */
+class A {}
+''');
+ }
+
+ void test_dartDoc_clumsy_updateText_insert() {
+ _resolveUnit(r'''
+/**
+ * A function [main] with a parameter [p] of type [int].
+ */
+main(int p) {
+ unresolvedFunctionProblem();
+}
+/**
+ * Other comment with [int] reference.
+ */
+foo() {}
+''');
+ _updateAndValidate(r'''
+/**
+ * A function [main] with a parameter [p] of type [int].
+ * Inserted text with [String] reference.
+ */
+main(int p) {
+ unresolvedFunctionProblem();
+}
+/**
+ * Other comment with [int] reference.
+ */
+foo() {}
+''');
+ }
+
+ void test_dartDoc_clumsy_updateText_remove() {
+ _resolveUnit(r'''
+/**
+ * A function [main] with a parameter [p] of type [int].
+ * Some text with [String] reference to remove.
+ */
+main(int p) {
+}
+/**
+ * Other comment with [int] reference.
+ */
+foo() {}
+''');
+ _updateAndValidate(r'''
+/**
+ * A function [main] with a parameter [p] of type [int].
+ */
+main(int p) {
+}
+/**
+ * Other comment with [int] reference.
+ */
+foo() {}
+''');
+ }
+
+ void test_dartDoc_elegant_addReference() {
+ _resolveUnit(r'''
+/// aaa bbb
+main() {
+ return 1;
+}
+''');
+ _updateAndValidate(r'''
+/// aaa [main] bbb
+/// ccc [int] ddd
+main() {
+ return 1;
+}
+''');
+ }
+
+ void test_dartDoc_elegant_removeReference() {
+ _resolveUnit(r'''
+/// aaa [main] bbb
+/// ccc [int] ddd
+main() {
+ return 1;
+}
+''');
+ _updateAndValidate(r'''
+/// aaa bbb
+main() {
+ return 1;
+}
+''');
+ }
+
+ void test_dartDoc_elegant_updateText_insertToken() {
+ _resolveUnit(r'''
+/// A
+/// [int]
+class Test {
+}
+''');
+ _updateAndValidate(r'''
+/// A
+///
+/// [int]
+class Test {
+}
+''');
+ }
+
+ void test_dartDoc_elegant_updateText_removeToken() {
+ _resolveUnit(r'''
+/// A
+///
+/// [int]
+class Test {
+}
+''');
+ _updateAndValidate(r'''
+/// A
+/// [int]
+class Test {
+}
+''');
+ }
+
+ void test_endOfLineComment_add_beforeKeywordToken() {
+ _resolveUnit(r'''
+main() {
+ var v = 42;
+}
+''');
+ _updateAndValidate(r'''
+main() {
+ // some comment
+ var v = 42;
+}
+''');
+ }
+
+ void test_endOfLineComment_add_beforeStringToken() {
+ _resolveUnit(r'''
+main() {
+ print(0);
+}
+''');
+ _updateAndValidate(r'''
+main() {
+ // some comment
+ print(0);
+}
+''');
+ }
+
+ void test_endOfLineComment_edit() {
+ _resolveUnit(r'''
+main() {
+ // some comment
+ print(0);
+}
+''');
+ _updateAndValidate(r'''
+main() {
+ // edited comment text
+ print(0);
+}
+''');
+ }
+
+ void test_endOfLineComment_outBody_add() {
+ _resolveUnit(r'''
+main() {
+ Object x;
+ x.foo();
+}
+''');
+ _updateAndValidate(
+ r'''
+// 000
+main() {
+ Object x;
+ x.foo();
+}
+''',
+ expectedSuccess: false);
+ }
+
+ void test_endOfLineComment_outBody_remove() {
+ _resolveUnit(r'''
+// 000
+main() {
+ Object x;
+ x.foo();
+}
+''');
+ _updateAndValidate(
+ r'''
+main() {
+ Object x;
+ x.foo();
+}
+''',
+ expectedSuccess: false);
+ }
+
+ void test_endOfLineComment_outBody_update() {
+ _resolveUnit(r'''
+// 000
+main() {
+ Object x;
+ x.foo();
+}
+''');
+ _updateAndValidate(
+ r'''
+// 10
+main() {
+ Object x;
+ x.foo();
+}
+''',
+ expectedSuccess: false);
+ }
+
+ void test_endOfLineComment_remove() {
+ _resolveUnit(r'''
+main() {
+ // some comment
+ print(0);
+}
+''');
+ _updateAndValidate(r'''
+main() {
+ print(0);
+}
+''');
+ }
+
+ void test_endOfLineComment_toDartDoc() {
+ _resolveUnit(r'''
+class A {
+ // text
+ main() {
+ print(42);
+ }
+}''');
+ _updateAndValidate(
+ r'''
+class A {
+ /// text
+ main() {
+ print(42);
+ }
+}''',
+ expectedSuccess: false);
+ }
+
+ void test_false_constConstructor_initializer() {
+ _resolveUnit(r'''
+class C {
+ final int x;
+ const C(this.x);
+ const C.foo() : x = 0;
+}
+main() {
+ const {const C(0): 0, const C.foo(): 1};
+}
+''');
+ _updateAndValidate(
+ r'''
+class C {
+ final int x;
+ const C(this.x);
+ const C.foo() : x = 1;
+}
+main() {
+ const {const C(0): 0, const C.foo(): 1};
+}
+''',
+ expectedSuccess: false);
+ }
+
+ void test_false_constructor_initializer_damage() {
+ _resolveUnit(r'''
+class Problem {
+ final Map location;
+ final String message;
+
+ Problem(Map json)
+ : location = json["location"],
+ message = json["message"];
+}''');
+ _updateAndValidate(
+ r'''
+class Problem {
+ final Map location;
+ final String message;
+
+ Problem(Map json)
+ : location = json["location],
+ message = json["message"];
+}''',
+ expectedSuccess: false);
+ }
+
+ void test_false_constructor_initializer_remove() {
+ _resolveUnit(r'''
+class Problem {
+ final String severity;
+ final Map location;
+ final String message;
+
+ Problem(Map json)
+ : severity = json["severity"],
+ location = json["location"],
+ message = json["message"];
+}''');
+ _updateAndValidate(
+ r'''
+class Problem {
+ final String severity;
+ final Map location;
+ final String message;
+
+ Problem(Map json)
+ : severity = json["severity"],
+ message = json["message"];
+}''',
+ expectedSuccess: false);
+ }
+
+ void test_false_endOfLineComment_localFunction_inTopLevelVariable() {
+ _resolveUnit(r'''
+typedef int Binary(one, two, three);
+
+int Global = f((a, b, c) {
+ return 0; // Some comment
+});
+''');
+ _updateAndValidate(
+ r'''
+typedef int Binary(one, two, three);
+
+int Global = f((a, b, c) {
+ return 0; // Some comment
+});
+''',
+ expectedSuccess: false);
+ }
+
+ void test_false_expressionBody() {
+ _resolveUnit(r'''
+class A {
+ final f = (() => 1)();
+}
+''');
+ _updateAndValidate(
+ r'''
+class A {
+ final f = (() => 2)();
+}
+''',
+ expectedSuccess: false);
+ }
+
+ void test_false_expressionBody2() {
+ _resolveUnit(r'''
+class A {
+ int m() => 10 * 10;
+}
+''');
+ _updateAndValidate(
+ r'''
+class A {
+ int m() => 10 * 100;
+}
+''',
+ expectedSuccess: false);
+ }
+
+ void test_false_inBody_addAsync() {
+ _resolveUnit(r'''
+class C {
+ test() {}
+}
+''');
+ _updateAndValidate(
+ r'''
+class C {
+ test() async {}
+}
+''',
+ expectedSuccess: false);
+ }
+
+ void test_false_inBody_async_addStar() {
+ _resolveUnit(r'''
+import 'dart:async';
+class C {
+ Stream test() async {}
+}
+''');
+ _updateAndValidate(
+ r'''
+import 'dart:async';
+class C {
+ Stream test() async* {}
+}
+''',
+ expectedSuccess: false);
+ }
+
+ void test_false_inBody_async_removeStar() {
+ _resolveUnit(r'''
+import 'dart:async';
+class C {
+ Stream test() async* {}
+}
+''');
+ _updateAndValidate(
+ r'''
+import 'dart:async';
+class C {
+ Stream test() async {}
+}
+''',
+ expectedSuccess: false);
+ }
+
+ void test_false_inBody_functionExpression() {
+ _resolveUnit(r'''
+class C extends D {
+ static final f = () {
+ var x = 0;
+ }();
+}
+
+class D {}
+''');
+ _updateAndValidate(
+ r'''
+class C extends D {
+ static final f = () {
+ var x = 01;
+ }();
+}
+
+class D {}
+''',
+ expectedSuccess: false);
+ }
+
+ void test_false_inBody_removeAsync() {
+ _resolveUnit(r'''
+class C {
+ test() async {}
+}
+''');
+ _updateAndValidate(
+ r'''
+class C {
+ test() {}
+}
+''',
+ expectedSuccess: false);
+ }
+
+ void test_false_inBody_sync_addStar() {
+ _resolveUnit(r'''
+class C {
+ test() {}
+}
+''');
+ _updateAndValidate(
+ r'''
+class C {
+ test() sync* {}
+}
+''',
+ expectedSuccess: false);
+ }
+
+ void test_false_topLevelFunction_name() {
+ _resolveUnit(r'''
+a() {}
+b() {}
+''');
+ _updateAndValidate(
+ r'''
+a() {}
+bb() {}
+''',
+ expectedSuccess: false);
+ }
+
+ void test_false_unbalancedCurlyBrackets_inNew() {
+ _resolveUnit(r'''
+class A {
+ aaa() {
+ if (true) {
+ 1;
+ }
+ }
+
+ bbb() {
+ print(0123456789);
+ }
+}''');
+ _updateAndValidate(
+ r'''
+class A {
+ aaa() {
+ 1;
+ }
+ }
+
+ bbb() {
+ print(0123456789);
+ }
+}''',
+ expectedSuccess: false);
+ }
+
+ void test_false_unbalancedCurlyBrackets_inOld() {
+ _resolveUnit(r'''
+class A {
+ aaa() {
+ 1;
+ }
+ }
+
+ bbb() {
+ print(0123456789);
+ }
+}''');
+ _updateAndValidate(
+ r'''
+class A {
+ aaa() {
+ if (true) {
+ 1;
+ }
+ }
+
+ bbb() {
+ print(0123456789);
+ }
+}''',
+ expectedSuccess: false);
+ }
+
+ void test_false_wholeConstructor() {
+ _resolveUnit(r'''
+class A {
+ A(int a) {
+ print(a);
+ }
+}
+''');
+ _updateAndValidate(
+ r'''
+class A {
+ A(int b) {
+ print(b);
+ }
+}
+''',
+ expectedSuccess: false);
+ }
+
+ void test_false_wholeConstructor_addInitializer() {
+ _resolveUnit(r'''
+class A {
+ int field;
+ A();
+}
+''');
+ _updateAndValidate(
+ r'''
+class A {
+ int field;
+ A() : field = 5;
+}
+''',
+ expectedSuccess: false);
+ }
+
+ void test_false_wholeFunction() {
+ _resolveUnit(r'''
+foo() {}
+main(int a) {
+ print(a);
+}
+''');
+ _updateAndValidate(
+ r'''
+foo() {}
+main(int b) {
+ print(b);
+}
+''',
+ expectedSuccess: false);
+ }
+
+ void test_false_wholeMethod() {
+ _resolveUnit(r'''
+class A {
+ main(int a) {
+ print(a);
+ }
+}
+''');
+ _updateAndValidate(
+ r'''
+class A {
+ main(int b) {
+ print(b);
+ }
+}
+''',
+ expectedSuccess: false);
+ }
+
+ void test_fieldClassField_propagatedType() {
+ _resolveUnit(r'''
+class A {
+ static const A b = const B();
+ const A();
+}
+
+class B extends A {
+ const B();
+}
+
+main() {
+ print(12);
+ A.b;
+}
+''');
+ _updateAndValidate(r'''
+class A {
+ static const A b = const B();
+ const A();
+}
+
+class B extends A {
+ const B();
+}
+
+main() {
+ print(123);
+ A.b;
+}
+''');
+ }
+
+ void test_hasElementAfter_defaultParameter() {
+ _resolveUnit(r'''
+main() {
+ print(1);
+}
+otherFunction([p = 0]) {}
+''');
+ _updateAndValidate(r'''
+main() {
+ print(2);
+}
+otherFunction([p = 0]) {}
+''');
+ }
+
+ void test_inBody_expression() {
+ _resolveUnit(r'''
+class A {
+ m() {
+ print(1);
+ }
+}
+''');
+ _updateAndValidate(r'''
+class A {
+ m() {
+ print(2 + 3);
+ }
+}
+''');
+ }
+
+ void test_inBody_insertStatement() {
+ _resolveUnit(r'''
+main() {
+ print(1);
+}
+''');
+ _updateAndValidate(r'''
+main() {
+ print(0);
+ print(1);
+}
+''');
+ }
+
+ void test_inBody_tokenToNode() {
+ _resolveUnit(r'''
+main() {
+ var v = 42;
+ print(v);
+}
+''');
+ _updateAndValidate(r'''
+main() {
+ int v = 42;
+ print(v);
+}
+''');
+ }
+
+ void test_multiple_emptyLine() {
+ _resolveUnit(r'''
+class A {
+ m() {
+ return true;
+ }
+}''');
+ for (int i = 0; i < 6; i++) {
+ if (i.isEven) {
+ _updateAndValidate(
+ r'''
+class A {
+ m() {
+ return true;
+
+ }
+}''',
+ compareWithFull: false);
+ } else {
+ _updateAndValidate(
+ r'''
+class A {
+ m() {
+ return true;
+ }
+}''',
+ compareWithFull: false);
+ }
+ }
+ }
+
+ void test_multiple_expression() {
+ _resolveUnit(r'''
+main() {
+ print(1);
+}''');
+ for (int i = 0; i < 6; i++) {
+ if (i.isEven) {
+ _updateAndValidate(
+ r'''
+main() {
+ print(12);
+}''',
+ compareWithFull: false);
+ } else {
+ _updateAndValidate(
+ r'''
+main() {
+ print(1);
+}''',
+ compareWithFull: false);
+ }
+ }
+ }
+
+ void test_strongMode_typeComments_insertWhitespace() {
+ _resolveUnit(r'''
+import 'dart:async';
+
+void fadeIn(int milliseconds) {
+ Future<String> f;
+ f.then/*<String>*/((e) {print("hello");});
+}
+''');
+ _updateAndValidate(r'''
+import 'dart:async';
+
+void fadeIn(int milliseconds) {
+ Future<String> f;
+ f.then/*<String>*/((e) {print("hello") ;});
+}
+''');
+ }
+
+ void test_true_emptyLine_betweenClassMembers_insert() {
+ _resolveUnit(r'''
+class A {
+ a() {}
+ b() {}
+}
+''');
+ _updateAndValidate(r'''
+class A {
+ a() {}
+
+ b() {}
+}
+''');
+ }
+
+ void test_true_emptyLine_betweenClassMembers_insert_beforeComment() {
+ _resolveUnit(r'''
+class A {
+ a() {}
+ /// BBB
+ b() {}
+}
+''');
+ _updateAndValidate(r'''
+class A {
+ a() {}
+
+ /// BBB
+ b() {}
+}
+''');
+ }
+
+ void test_true_emptyLine_betweenClassMembers_remove() {
+ _resolveUnit(r'''
+class A {
+ a() {}
+
+ b() {}
+}
+''');
+ _updateAndValidate(r'''
+class A {
+ a() {}
+ b() {}
+}
+''');
+ }
+
+ void test_true_emptyLine_betweenClassMembers_remove_beforeComment() {
+ _resolveUnit(r'''
+class A {
+ a() {}
+
+ /// BBB
+ b() {}
+}
+''');
+ _updateAndValidate(r'''
+class A {
+ a() {}
+ /// BBB
+ b() {}
+}
+''');
+ }
+
+ void test_true_emptyLine_betweenUnitMembers_insert() {
+ _resolveUnit(r'''
+a() {}
+b() {}
+''');
+ _updateAndValidate(r'''
+a() {}
+
+b() {}
+''');
+ }
+
+ void test_true_emptyLine_betweenUnitMembers_insert_beforeComment() {
+ _resolveUnit(r'''
+a() {}
+
+// BBB
+b() {}
+''');
+ _updateAndValidate(r'''
+a() {}
+
+
+// BBB
+b() {}
+''');
+ }
+
+ void test_true_emptyLine_betweenUnitMembers_remove() {
+ _resolveUnit(r'''
+a() {
+ print(1)
+}
+
+b() {
+ foo(42);
+}
+foo(String p) {}
+''');
+ _updateAndValidate(r'''
+a() {
+ print(1)
+}
+b() {
+ foo(42);
+}
+foo(String p) {}
+''');
+ }
+
+ void test_true_emptyLine_betweenUnitMembers_remove_beforeComment() {
+ _resolveUnit(r'''
+a() {}
+
+// BBB
+b() {}
+''');
+ _updateAndValidate(r'''
+a() {}
+// BBB
+b() {}
+''');
+ }
+
+ void test_true_todoHint() {
+ _resolveUnit(r'''
+main() {
+ print(1);
+}
+foo() {
+ // TODO
+}
+''');
+ List<AnalysisError> oldErrors = analysisContext.computeErrors(source);
+ _updateAndValidate(r'''
+main() {
+ print(2);
+}
+foo() {
+ // TODO
+}
+''');
+ List<AnalysisError> newErrors = analysisContext.computeErrors(source);
+ _assertEqualErrors(newErrors, oldErrors);
+ }
+
+ void test_unusedHint_add_wasUsedOnlyInPart() {
+ Source partSource = addNamedSource(
+ '/my_unit.dart',
+ r'''
+part of lib;
+
+f(A a) {
+ a._foo();
+}
+''');
+ _resolveUnit(r'''
+library lib;
+part 'my_unit.dart';
+class A {
+ _foo() {
+ print(1);
+ }
+}
+''');
+ _runTasks();
+ // perform incremental resolution
+ _resetWithIncremental(true);
+ analysisContext2.setContents(
+ partSource,
+ r'''
+part of lib;
+
+f(A a) {
+// a._foo();
+}
+''');
+ // no hints right now, because we delay hints computing
+ {
+ List<AnalysisError> errors = analysisContext.getErrors(source).errors;
+ expect(errors, isEmpty);
+ }
+ // a new hint should be added
+ List<AnalysisError> errors = analysisContext.computeErrors(source);
+ expect(errors, hasLength(1));
+ expect(errors[0].errorCode.type, ErrorType.HINT);
+ // the same hint should be reported using a ChangeNotice
+ bool noticeFound = false;
+ AnalysisResult result = analysisContext2.performAnalysisTask();
+ for (ChangeNotice notice in result.changeNotices) {
+ if (notice.source == source) {
+ expect(notice.errors, contains(errors[0]));
+ noticeFound = true;
+ }
+ }
+ expect(noticeFound, isTrue);
+ }
+
+ void test_unusedHint_false_stillUsedInPart() {
+ addNamedSource(
+ '/my_unit.dart',
+ r'''
+part of lib;
+
+f(A a) {
+ a._foo();
+}
+''');
+ _resolveUnit(r'''
+library lib;
+part 'my_unit.dart';
+class A {
+ _foo() {
+ print(1);
+ }
+}
+''');
+ // perform incremental resolution
+ _resetWithIncremental(true);
+ analysisContext2.setContents(
+ source,
+ r'''
+library lib;
+part 'my_unit.dart';
+class A {
+ _foo() {
+ print(12);
+ }
+}
+''');
+ // no hints
+ List<AnalysisError> errors = analysisContext.getErrors(source).errors;
+ expect(errors, isEmpty);
+ }
+
+ void test_updateConstantInitializer() {
+ _resolveUnit(r'''
+main() {
+ const v = const [Unknown];
+}
+''');
+ _updateAndValidate(
+ r'''
+main() {
+ const v = const [Unknown];
+}
+''',
+ expectCachePostConstantsValid: false);
+ }
+
+ void test_updateErrors_addNew_hint1() {
+ _resolveUnit(r'''
+int main() {
+ return 42;
+}
+''');
+ _updateAndValidate(r'''
+int main() {
+}
+''');
+ }
+
+ void test_updateErrors_addNew_hint2() {
+ _resolveUnit(r'''
+main() {
+ int v = 0;
+ print(v);
+}
+''');
+ _updateAndValidate(r'''
+main() {
+ int v = 0;
+}
+''');
+ }
+
+ void test_updateErrors_addNew_parse() {
+ _resolveUnit(r'''
+main() {
+ print(42);
+}
+''');
+ _updateAndValidate(r'''
+main() {
+ print(42)
+}
+''');
+ }
+
+ void test_updateErrors_addNew_resolve() {
+ _resolveUnit(r'''
+main() {
+ foo();
+}
+foo() {}
+''');
+ _updateAndValidate(r'''
+main() {
+ bar();
+}
+foo() {}
+''');
+ }
+
+ void test_updateErrors_addNew_resolve2() {
+ _resolveUnit(r'''
+// this comment is important to reproduce the problem
+main() {
+ int vvv = 42;
+ print(vvv);
+}
+''');
+ _updateAndValidate(r'''
+// this comment is important to reproduce the problem
+main() {
+ int vvv = 42;
+ print(vvv2);
+}
+''');
+ }
+
+ void test_updateErrors_addNew_scan() {
+ _resolveUnit(r'''
+main() {
+ 1;
+}
+''');
+ _updateAndValidate(r'''
+main() {
+ 1e;
+}
+''');
+ }
+
+ void test_updateErrors_addNew_verify() {
+ _resolveUnit(r'''
+main() {
+ foo(0);
+}
+foo(int p) {}
+''');
+ _updateAndValidate(r'''
+main() {
+ foo('abc');
+}
+foo(int p) {}
+''');
+ }
+
+ void test_updateErrors_invalidVerifyErrors() {
+ _resolveUnit(r'''
+main() {
+ foo('aaa');
+}
+main2() {
+ foo('bbb');
+}
+foo(int p) {}
+''');
+ // Complete analysis, e.g. compute VERIFY_ERRORS.
+ _runTasks();
+ // Invalidate VERIFY_ERRORS.
+ AnalysisCache cache = analysisContext2.analysisCache;
+ LibrarySpecificUnit target = new LibrarySpecificUnit(source, source);
+ CacheEntry cacheEntry = cache.get(target);
+ expect(cacheEntry.getValue(VERIFY_ERRORS), hasLength(2));
+ cacheEntry.setState(VERIFY_ERRORS, CacheState.INVALID);
+ // Perform incremental resolution.
+ _resetWithIncremental(true);
+ analysisContext2.setContents(
+ source,
+ r'''
+main() {
+ foo(0);
+}
+main2() {
+ foo('bbb');
+}
+foo(int p) {}
+''');
+ // VERIFY_ERRORS is still invalid.
+ expect(cacheEntry.getState(VERIFY_ERRORS), CacheState.INVALID);
+ // Continue analysis - run tasks, so recompute VERIFY_ERRORS.
+ _runTasks();
+ expect(cacheEntry.getState(VERIFY_ERRORS), CacheState.VALID);
+ expect(cacheEntry.getValue(VERIFY_ERRORS), hasLength(1));
+ }
+
+ void test_updateErrors_removeExisting_hint() {
+ _resolveUnit(r'''
+int main() {
+}
+''');
+ _updateAndValidate(r'''
+int main() {
+ return 42;
+}
+''');
+ }
+
+ void test_updateErrors_removeExisting_verify() {
+ _resolveUnit(r'''
+f1() {
+ print(1)
+}
+f2() {
+ print(22)
+}
+f3() {
+ print(333)
+}
+''');
+ _updateAndValidate(r'''
+f1() {
+ print(1)
+}
+f2() {
+ print(22);
+}
+f3() {
+ print(333)
+}
+''');
+ }
+
+ void test_updateErrors_shiftExisting() {
+ _resolveUnit(r'''
+f1() {
+ print(1)
+}
+f2() {
+ print(2);
+}
+f3() {
+ print(333)
+}
+''');
+ _updateAndValidate(r'''
+f1() {
+ print(1)
+}
+f2() {
+ print(22);
+}
+f3() {
+ print(333)
+}
+''');
+ }
+
+ void test_updateFunctionToForLoop() {
+ _resolveUnit(r'''
+class PlayDrag {
+ final List<num> times = new List<num>();
+
+ PlayDrag.start() {}
+
+ void update(num pos) {
+ fo (int i = times.length - 2; i >= 0; i--) {}
+ }
+}
+''');
+
+ _updateAndValidate(
+ r'''
+class PlayDrag {
+ final List<num> times = new List<num>();
+
+ PlayDrag.start() {}
+
+ void update(num pos) {
+ for (int i = times.length - 2; i >= 0; i--) {}
+ }
+}
+''',
+ expectLibraryUnchanged: false);
+ }
+
+ void test_visibleRange() {
+ _resolveUnit(r'''
+class Test {
+ method1(p1) {
+ var v1;
+ f1() {}
+ return 1;
+ }
+ method2(p2) {
+ var v2;
+ f2() {}
+ return 2;
+ }
+ method3(p3) {
+ var v3;
+ f3() {}
+ return 3;
+ }
+}
+''');
+ _updateAndValidate(r'''
+class Test {
+ method1(p1) {
+ var v1;
+ f1() {}
+ return 1;
+ }
+ method2(p2) {
+ var v2;
+ f2() {}
+ return 2222;
+ }
+ method3(p3) {
+ var v3;
+ f3() {}
+ return 3;
+ }
+}
+''');
+ }
+
+ void test_whitespace_getElementAt() {
+ _resolveUnit(r'''
+class A {}
+class B extends A {}
+''');
+ {
+ ClassElement typeA = oldUnitElement.getType('A');
+ expect(oldUnitElement.getElementAt(typeA.nameOffset), typeA);
+ }
+ {
+ ClassElement typeB = oldUnitElement.getType('B');
+ expect(oldUnitElement.getElementAt(typeB.nameOffset), typeB);
+ }
+ _updateAndValidate(r'''
+class A {}
+
+class B extends A {}
+''');
+ // getElementAt() caches results, it should be notified when offset
+ // are changed.
+ {
+ ClassElement typeA = oldUnitElement.getType('A');
+ expect(oldUnitElement.getElementAt(typeA.nameOffset), typeA);
+ }
+ {
+ ClassElement typeB = oldUnitElement.getType('B');
+ expect(oldUnitElement.getElementAt(typeB.nameOffset), typeB);
+ }
+ }
+
+ void _assertCacheResults(
+ {bool expectLibraryUnchanged: true,
+ bool expectCachePostConstantsValid: true}) {
+ _assertCacheSourceResult(TOKEN_STREAM);
+ _assertCacheSourceResult(SCAN_ERRORS);
+ _assertCacheSourceResult(PARSED_UNIT);
+ _assertCacheSourceResult(PARSE_ERRORS);
+ if (!expectLibraryUnchanged) {
+ return;
+ }
+ _assertCacheSourceResult(LIBRARY_ELEMENT1);
+ _assertCacheSourceResult(LIBRARY_ELEMENT2);
+ _assertCacheSourceResult(LIBRARY_ELEMENT3);
+ _assertCacheSourceResult(LIBRARY_ELEMENT4);
+ _assertCacheSourceResult(LIBRARY_ELEMENT5);
+ _assertCacheSourceResult(LIBRARY_ELEMENT6);
+ _assertCacheSourceResult(LIBRARY_ELEMENT7);
+ _assertCacheSourceResult(LIBRARY_ELEMENT8);
+ _assertCacheSourceResult(LIBRARY_ELEMENT9);
+ if (expectCachePostConstantsValid) {
+ _assertCacheSourceResult(LIBRARY_ELEMENT);
+ }
+ _assertCacheUnitResult(RESOLVED_UNIT1);
+ _assertCacheUnitResult(RESOLVED_UNIT2);
+ _assertCacheUnitResult(RESOLVED_UNIT3);
+ _assertCacheUnitResult(RESOLVED_UNIT4);
+ _assertCacheUnitResult(RESOLVED_UNIT5);
+ _assertCacheUnitResult(RESOLVED_UNIT6);
+ _assertCacheUnitResult(RESOLVED_UNIT7);
+ _assertCacheUnitResult(RESOLVED_UNIT8);
+ _assertCacheUnitResult(RESOLVED_UNIT9);
+ _assertCacheUnitResult(RESOLVED_UNIT10);
+ _assertCacheUnitResult(RESOLVED_UNIT11);
+ if (expectCachePostConstantsValid) {
+ _assertCacheUnitResult(RESOLVED_UNIT12);
+ _assertCacheUnitResult(RESOLVED_UNIT);
+ }
+ }
+
+ /**
+ * Assert that the [result] of [source] is not INVALID.
+ */
+ void _assertCacheSourceResult(ResultDescriptor result) {
+ AnalysisCache cache = analysisContext2.analysisCache;
+ CacheState state = cache.getState(source, result);
+ expect(state, isNot(CacheState.INVALID), reason: result.toString());
+ }
+
+ /**
+ * Assert that the [result] of the defining unit [source] is not INVALID.
+ */
+ void _assertCacheUnitResult(ResultDescriptor result) {
+ AnalysisCache cache = analysisContext2.analysisCache;
+ LibrarySpecificUnit target = new LibrarySpecificUnit(source, source);
+ CacheState state = cache.getState(target, result);
+ expect(state, isNot(CacheState.INVALID), reason: result.toString());
+ }
+
+ void _assertEqualLineInfo(LineInfo incLineInfo, LineInfo fullLineInfo) {
+ for (int offset = 0; offset < 1000; offset++) {
+ LineInfo_Location incLocation = incLineInfo.getLocation(offset);
+ LineInfo_Location fullLocation = fullLineInfo.getLocation(offset);
+ if (incLocation.lineNumber != fullLocation.lineNumber ||
+ incLocation.columnNumber != fullLocation.columnNumber) {
+ fail('At offset $offset ' +
+ '(${incLocation.lineNumber}, ${incLocation.columnNumber})' +
+ ' != ' +
+ '(${fullLocation.lineNumber}, ${fullLocation.columnNumber})');
+ }
+ }
+ }
+
+ /**
+ * Reset the analysis context to have the 'incremental' option set to the
+ * given value.
+ */
+ void _resetWithIncremental(bool enable) {
+ AnalysisOptionsImpl analysisOptions = new AnalysisOptionsImpl();
+ analysisOptions.strongMode = true;
+ analysisOptions.incremental = enable;
+ analysisOptions.incrementalApi = enable;
+ logging.logger = logger;
+ analysisContext2.analysisOptions = analysisOptions;
+ }
+
+ void _resolveUnit(String code) {
+ this.code = code;
+ source = addSource(code);
+ oldLibrary = resolve2(source);
+ oldUnit = resolveCompilationUnit(source, oldLibrary);
+ oldUnitElement = oldUnit.element;
+ }
+
+ void _runTasks() {
+ AnalysisResult result = analysisContext.performAnalysisTask();
+ while (result.changeNotices != null) {
+ result = analysisContext.performAnalysisTask();
+ }
+ }
+
+ void _updateAndValidate(String newCode,
+ {bool expectedSuccess: true,
+ bool expectLibraryUnchanged: true,
+ bool expectCachePostConstantsValid: true,
+ bool compareWithFull: true,
+ bool runTasksBeforeIncremental: true}) {
+ // Run any pending tasks tasks.
+ if (runTasksBeforeIncremental) {
+ _runTasks();
+ }
+ // Update the source - currently this may cause incremental resolution.
+ // Then request the updated resolved unit.
+ _resetWithIncremental(true);
+ analysisContext2.setContents(source, newCode);
+ CompilationUnit newUnit = resolveCompilationUnit(source, oldLibrary);
+ logger.expectNoErrors();
+ List<AnalysisError> newErrors = analysisContext.computeErrors(source);
+ LineInfo newLineInfo = analysisContext.getLineInfo(source);
+ // check for expected failure
+ if (!expectedSuccess) {
+ expect(newUnit.element, isNot(same(oldUnitElement)));
+ return;
+ }
+ // The cache must still have enough results to make the incremental
+ // resolution useful.
+ _assertCacheResults(
+ expectLibraryUnchanged: expectLibraryUnchanged,
+ expectCachePostConstantsValid: expectCachePostConstantsValid);
+ // The existing CompilationUnit[Element] should be updated.
+ expect(newUnit, same(oldUnit));
+ expect(newUnit.element, same(oldUnitElement));
+ expect(analysisContext.getResolvedCompilationUnit(source, oldLibrary),
+ same(oldUnit));
+ // The only expected pending task should return the same resolved
+ // "newUnit", so all clients will get it using the usual way.
+ AnalysisResult analysisResult = analysisContext.performAnalysisTask();
+ ChangeNotice notice = analysisResult.changeNotices[0];
+ expect(notice.resolvedDartUnit, same(newUnit));
+ // Resolve "newCode" from scratch.
+ if (compareWithFull) {
+ _resetWithIncremental(false);
+ changeSource(source, '');
+ changeSource(source, newCode);
+ _runTasks();
+ LibraryElement library = resolve2(source);
+ CompilationUnit fullNewUnit = resolveCompilationUnit(source, library);
+ // Validate tokens.
+ _assertEqualTokens(newUnit, fullNewUnit);
+ // Validate LineInfo
+ _assertEqualLineInfo(newLineInfo, analysisContext.getLineInfo(source));
+ // Validate that "incremental" and "full" units have the same resolution.
+ try {
+ assertSameResolution(newUnit, fullNewUnit, validateTypes: true);
+ } on IncrementalResolutionMismatch catch (mismatch) {
+ fail(mismatch.message);
+ }
+ List<AnalysisError> newFullErrors =
+ analysisContext.getErrors(source).errors;
+ _assertEqualErrors(newErrors, newFullErrors);
+ }
+ _checkCacheEntries(analysisContext2.analysisCache);
+ }
+
+ static void _assertEqualToken(Token incToken, Token fullToken) {
+// print('[${incToken.offset}] |$incToken| vs. [${fullToken.offset}] |$fullToken|');
+ expect(incToken.type, fullToken.type);
+ expect(incToken.offset, fullToken.offset);
+ expect(incToken.length, fullToken.length);
+ expect(incToken.lexeme, fullToken.lexeme);
+ }
+
+ static void _assertEqualTokens(
+ CompilationUnit incUnit, CompilationUnit fullUnit) {
+ Token incToken = incUnit.beginToken;
+ Token fullToken = fullUnit.beginToken;
+ while (incToken.type != TokenType.EOF && fullToken.type != TokenType.EOF) {
+ _assertEqualToken(incToken, fullToken);
+ // comments
+ {
+ Token incComment = incToken.precedingComments;
+ Token fullComment = fullToken.precedingComments;
+ while (true) {
+ if (fullComment == null) {
+ expect(incComment, isNull);
+ break;
+ }
+ expect(incComment, isNotNull);
+ _assertEqualToken(incComment, fullComment);
+ incComment = incComment.next;
+ fullComment = fullComment.next;
+ }
+ }
+ // next tokens
+ incToken = incToken.next;
+ fullToken = fullToken.next;
+ }
+ }
+}
+
+@reflectiveTest
+class ResolutionContextBuilderTest extends EngineTestCase {
+ void test_scopeFor_ClassDeclaration() {
+ Scope scope = _scopeFor(_createResolvedClassDeclaration());
+ EngineTestCase.assertInstanceOf(
+ (obj) => obj is LibraryScope, LibraryScope, scope);
+ }
+
+ void test_scopeFor_ClassTypeAlias() {
+ Scope scope = _scopeFor(_createResolvedClassTypeAlias());
+ EngineTestCase.assertInstanceOf(
+ (obj) => obj is LibraryScope, LibraryScope, scope);
+ }
+
+ void test_scopeFor_CompilationUnit() {
+ Scope scope = _scopeFor(_createResolvedCompilationUnit());
+ EngineTestCase.assertInstanceOf(
+ (obj) => obj is LibraryScope, LibraryScope, scope);
+ }
+
+ void test_scopeFor_ConstructorDeclaration() {
+ Scope scope = _scopeFor(_createResolvedConstructorDeclaration());
+ EngineTestCase.assertInstanceOf(
+ (obj) => obj is ClassScope, ClassScope, scope);
+ }
+
+ void test_scopeFor_ConstructorDeclaration_parameters() {
+ Scope scope = _scopeFor(_createResolvedConstructorDeclaration().parameters);
+ EngineTestCase.assertInstanceOf(
+ (obj) => obj is FunctionScope, FunctionScope, scope);
+ }
+
+ void test_scopeFor_FunctionDeclaration() {
+ Scope scope = _scopeFor(_createResolvedFunctionDeclaration());
+ EngineTestCase.assertInstanceOf(
+ (obj) => obj is LibraryScope, LibraryScope, scope);
+ }
+
+ void test_scopeFor_FunctionDeclaration_parameters() {
+ Scope scope = _scopeFor(
+ _createResolvedFunctionDeclaration().functionExpression.parameters);
+ EngineTestCase.assertInstanceOf(
+ (obj) => obj is FunctionScope, FunctionScope, scope);
+ }
+
+ void test_scopeFor_FunctionTypeAlias() {
+ Scope scope = _scopeFor(_createResolvedFunctionTypeAlias());
+ EngineTestCase.assertInstanceOf(
+ (obj) => obj is LibraryScope, LibraryScope, scope);
+ }
+
+ void test_scopeFor_FunctionTypeAlias_parameters() {
+ Scope scope = _scopeFor(_createResolvedFunctionTypeAlias().parameters);
+ EngineTestCase.assertInstanceOf(
+ (obj) => obj is FunctionTypeScope, FunctionTypeScope, scope);
+ }
+
+ void test_scopeFor_MethodDeclaration() {
+ Scope scope = _scopeFor(_createResolvedMethodDeclaration());
+ EngineTestCase.assertInstanceOf(
+ (obj) => obj is ClassScope, ClassScope, scope);
+ }
+
+ void test_scopeFor_MethodDeclaration_body() {
+ Scope scope = _scopeFor(_createResolvedMethodDeclaration().body);
+ EngineTestCase.assertInstanceOf(
+ (obj) => obj is FunctionScope, FunctionScope, scope);
+ }
+
+ void test_scopeFor_notInCompilationUnit() {
+ try {
+ _scopeFor(AstTestFactory.identifier3("x"));
+ fail("Expected AnalysisException");
+ } on AnalysisException {
+ // Expected
+ }
+ }
+
+ void test_scopeFor_null() {
+ try {
+ _scopeFor(null);
+ fail("Expected AnalysisException");
+ } on AnalysisException {
+ // Expected
+ }
+ }
+
+ void test_scopeFor_unresolved() {
+ try {
+ _scopeFor(AstTestFactory.compilationUnit());
+ fail("Expected AnalysisException");
+ } on AnalysisException {
+ // Expected
+ }
+ }
+
+ ClassDeclaration _createResolvedClassDeclaration() {
+ CompilationUnit unit = _createResolvedCompilationUnit();
+ String className = "C";
+ ClassDeclaration classNode = AstTestFactory.classDeclaration(
+ null, className, AstTestFactory.typeParameterList(), null, null, null);
+ unit.declarations.add(classNode);
+ ClassElement classElement = ElementFactory.classElement2(className);
+ classNode.name.staticElement = classElement;
+ (unit.element as CompilationUnitElementImpl).types = <ClassElement>[
+ classElement
+ ];
+ return classNode;
+ }
+
+ ClassTypeAlias _createResolvedClassTypeAlias() {
+ CompilationUnit unit = _createResolvedCompilationUnit();
+ String className = "C";
+ ClassTypeAlias classNode = AstTestFactory.classTypeAlias(
+ className, AstTestFactory.typeParameterList(), null, null, null, null);
+ unit.declarations.add(classNode);
+ ClassElement classElement = ElementFactory.classElement2(className);
+ classNode.name.staticElement = classElement;
+ (unit.element as CompilationUnitElementImpl).types = <ClassElement>[
+ classElement
+ ];
+ return classNode;
+ }
+
+ CompilationUnit _createResolvedCompilationUnit() {
+ CompilationUnit unit = AstTestFactory.compilationUnit();
+ LibraryElementImpl library =
+ ElementFactory.library(AnalysisContextFactory.contextWithCore(), "lib");
+ unit.element = library.definingCompilationUnit;
+ return unit;
+ }
+
+ ConstructorDeclaration _createResolvedConstructorDeclaration() {
+ ClassDeclaration classNode = _createResolvedClassDeclaration();
+ String constructorName = "f";
+ ConstructorDeclaration constructorNode =
+ AstTestFactory.constructorDeclaration(
+ AstTestFactory.identifier3(constructorName),
+ null,
+ AstTestFactory.formalParameterList(),
+ null);
+ classNode.members.add(constructorNode);
+ ConstructorElement constructorElement =
+ ElementFactory.constructorElement2(classNode.element, null);
+ constructorNode.element = constructorElement;
+ (classNode.element as ClassElementImpl).constructors = <ConstructorElement>[
+ constructorElement
+ ];
+ return constructorNode;
+ }
+
+ FunctionDeclaration _createResolvedFunctionDeclaration() {
+ CompilationUnit unit = _createResolvedCompilationUnit();
+ String functionName = "f";
+ FunctionDeclaration functionNode = AstTestFactory.functionDeclaration(
+ null, null, functionName, AstTestFactory.functionExpression());
+ unit.declarations.add(functionNode);
+ FunctionElement functionElement =
+ ElementFactory.functionElement(functionName);
+ functionNode.name.staticElement = functionElement;
+ (unit.element as CompilationUnitElementImpl).functions = <FunctionElement>[
+ functionElement
+ ];
+ return functionNode;
+ }
+
+ FunctionTypeAlias _createResolvedFunctionTypeAlias() {
+ CompilationUnit unit = _createResolvedCompilationUnit();
+ FunctionTypeAlias aliasNode = AstTestFactory.typeAlias(
+ AstTestFactory.typeName4("A"),
+ "F",
+ AstTestFactory.typeParameterList(),
+ AstTestFactory.formalParameterList());
+ unit.declarations.add(aliasNode);
+ SimpleIdentifier aliasName = aliasNode.name;
+ FunctionTypeAliasElement aliasElement =
+ new FunctionTypeAliasElementImpl.forNode(aliasName);
+ aliasName.staticElement = aliasElement;
+ (unit.element as CompilationUnitElementImpl).typeAliases =
+ <FunctionTypeAliasElement>[aliasElement];
+ return aliasNode;
+ }
+
+ MethodDeclaration _createResolvedMethodDeclaration() {
+ ClassDeclaration classNode = _createResolvedClassDeclaration();
+ String methodName = "f";
+ MethodDeclaration methodNode = AstTestFactory.methodDeclaration(
+ null,
+ null,
+ null,
+ null,
+ AstTestFactory.identifier3(methodName),
+ AstTestFactory.formalParameterList());
+ classNode.members.add(methodNode);
+ MethodElement methodElement =
+ ElementFactory.methodElement(methodName, null);
+ methodNode.name.staticElement = methodElement;
+ (classNode.element as ClassElementImpl).methods = <MethodElement>[
+ methodElement
+ ];
+ return methodNode;
+ }
+
+ Scope _scopeFor(AstNode node) {
+ return ResolutionContextBuilder.contextFor(node).scope;
+ }
+}
+
+class _Edit {
+ final int offset;
+ final int length;
+ final String replacement;
+ _Edit(this.offset, this.length, this.replacement);
+}
+
+class _TestLogger implements logging.Logger {
+ Object lastException;
+ Object lastStackTrace;
+
+ @override
+ void enter(String name) {}
+
+ @override
+ void exit() {}
+
+ void expectNoErrors() {
+ if (lastException != null) {
+ fail("logged an exception:\n$lastException\n$lastStackTrace\n");
+ }
+ }
+
+ @override
+ void log(Object obj) {}
+
+ @override
+ void logException(Object exception, [Object stackTrace]) {
+ lastException = exception;
+ lastStackTrace = stackTrace;
+ }
+
+ @override
+ logging.LoggingTimer startTimer() {
+ return new logging.LoggingTimer(this);
+ }
+}
« no previous file with comments | « pkg/analyzer/lib/src/generated/incremental_resolver.dart ('k') | pkg/analyzer/test/generated/test_all.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698