| Index: pkg/analyzer/test/src/task/strong/strong_test_helper.dart
|
| diff --git a/pkg/analyzer/test/src/task/strong/strong_test_helper.dart b/pkg/analyzer/test/src/task/strong/strong_test_helper.dart
|
| index c2debb8bddcbe088e9e8f2b7c568c668e6c33777..8361c123e80264d4d20ac68a0d1d9ad942e1b493 100644
|
| --- a/pkg/analyzer/test/src/task/strong/strong_test_helper.dart
|
| +++ b/pkg/analyzer/test/src/task/strong/strong_test_helper.dart
|
| @@ -6,6 +6,8 @@
|
| // package:dev_compiler's tests
|
| library analyzer.test.src.task.strong.strong_test_helper;
|
|
|
| +import 'dart:async';
|
| +
|
| import 'package:analyzer/dart/ast/ast.dart';
|
| import 'package:analyzer/dart/ast/standard_resolution_map.dart';
|
| import 'package:analyzer/dart/ast/token.dart';
|
| @@ -15,6 +17,7 @@ import 'package:analyzer/error/listener.dart';
|
| import 'package:analyzer/file_system/file_system.dart';
|
| import 'package:analyzer/file_system/memory_file_system.dart';
|
| import 'package:analyzer/source/error_processor.dart';
|
| +import 'package:analyzer/src/dart/analysis/driver.dart';
|
| import 'package:analyzer/src/dart/ast/token.dart';
|
| import 'package:analyzer/src/error/codes.dart';
|
| import 'package:analyzer/src/generated/engine.dart';
|
| @@ -24,119 +27,6 @@ import 'package:test/test.dart';
|
|
|
| import '../../context/mock_sdk.dart';
|
|
|
| -MemoryResourceProvider files;
|
| -bool _checkCalled;
|
| -
|
| -/// Adds a file to check. The file should contain:
|
| -///
|
| -/// * all expected failures are listed in the source code using comments
|
| -/// immediately in front of the AST node that should contain the error.
|
| -///
|
| -/// * errors are formatted as a token `severity:ErrorCode`, where
|
| -/// `severity` is the ErrorSeverity the error would be reported at, and
|
| -/// `ErrorCode` is the error code's name.
|
| -///
|
| -/// For example to check that an assignment produces a type error, you can
|
| -/// create a file like:
|
| -///
|
| -/// addFile('''
|
| -/// String x = /*error:STATIC_TYPE_ERROR*/3;
|
| -/// ''');
|
| -/// check();
|
| -///
|
| -/// For a single file, you may also use [checkFile].
|
| -void addFile(String content, {String name: '/main.dart'}) {
|
| - name = name.replaceFirst('^package:', '/packages/');
|
| - files.newFile(files.convertPath(name), content);
|
| -}
|
| -
|
| -/// Run the checker on a program, staring from '/main.dart', and verifies that
|
| -/// errors/warnings/hints match the expected value.
|
| -///
|
| -/// See [addFile] for more information about how to encode expectations in
|
| -/// the file text.
|
| -///
|
| -/// Returns the main resolved library. This can be used for further checks.
|
| -CompilationUnit check(
|
| - {bool implicitCasts: true,
|
| - bool implicitDynamic: true,
|
| - List<String> nonnullableTypes: AnalysisOptionsImpl.NONNULLABLE_TYPES}) {
|
| - _checkCalled = true;
|
| -
|
| - File mainFile = files.getFile(files.convertPath('/main.dart'));
|
| - expect(mainFile.exists, true, reason: '`/main.dart` is missing');
|
| -
|
| - var uriResolver = new _TestUriResolver(files);
|
| - // Enable task model strong mode
|
| - var context = AnalysisEngine.instance.createAnalysisContext();
|
| - AnalysisOptionsImpl options = context.analysisOptions as AnalysisOptionsImpl;
|
| - options.strongMode = true;
|
| - options.strongModeHints = true;
|
| - options.implicitCasts = implicitCasts;
|
| - options.implicitDynamic = implicitDynamic;
|
| - options.nonnullableTypes = nonnullableTypes;
|
| - var mockSdk = new MockSdk(resourceProvider: files);
|
| - (mockSdk.context.analysisOptions as AnalysisOptionsImpl).strongMode = true;
|
| - context.sourceFactory =
|
| - new SourceFactory([new DartUriResolver(mockSdk), uriResolver]);
|
| -
|
| - // Run the checker on /main.dart.
|
| - Source mainSource = uriResolver.resolveAbsolute(mainFile.toUri());
|
| - var initialLibrary = context.resolveCompilationUnit2(mainSource, mainSource);
|
| -
|
| - var collector = new _ErrorCollector(context);
|
| -
|
| - // Extract expectations from the comments in the test files, and
|
| - // check that all errors we emit are included in the expected map.
|
| - var allLibraries = _reachableLibraries(
|
| - resolutionMap.elementDeclaredByCompilationUnit(initialLibrary).library);
|
| - for (var lib in allLibraries) {
|
| - for (var unit in lib.units) {
|
| - var errors = <AnalysisError>[];
|
| - collector.errors = errors;
|
| -
|
| - var source = unit.source;
|
| - if (source.uri.scheme == 'dart') continue;
|
| -
|
| - var librarySource = context.getLibrariesContaining(source).single;
|
| - var resolved = context.resolveCompilationUnit2(source, librarySource);
|
| -
|
| - errors.addAll(context.computeErrors(source).where((e) =>
|
| - // TODO(jmesserly): these are usually intentional dynamic calls.
|
| - e.errorCode.name != 'UNDEFINED_METHOD' &&
|
| - // We don't care about any of these:
|
| - e.errorCode != HintCode.UNUSED_ELEMENT &&
|
| - e.errorCode != HintCode.UNUSED_FIELD &&
|
| - e.errorCode != HintCode.UNUSED_IMPORT &&
|
| - e.errorCode != HintCode.UNUSED_LOCAL_VARIABLE &&
|
| - e.errorCode != TodoCode.TODO));
|
| - _expectErrors(context, resolved, errors);
|
| - }
|
| - }
|
| -
|
| - return initialLibrary;
|
| -}
|
| -
|
| -/// Adds a file using [addFile] and calls [check].
|
| -///
|
| -/// Also returns the resolved compilation unit.
|
| -CompilationUnit checkFile(String content) {
|
| - addFile(content);
|
| - return check();
|
| -}
|
| -
|
| -void doSetUp() {
|
| - AnalysisEngine.instance.processRequiredPlugins();
|
| - files = new MemoryResourceProvider();
|
| - _checkCalled = false;
|
| -}
|
| -
|
| -void doTearDown() {
|
| - // This is a sanity check, in case only addFile is called.
|
| - expect(_checkCalled, true, reason: 'must call check() method in test case');
|
| - files = null;
|
| -}
|
| -
|
| SourceSpanWithContext _createSpanHelper(
|
| LineInfo lineInfo, int start, Source source, String content,
|
| {int end}) {
|
| @@ -173,15 +63,13 @@ String _errorCodeName(ErrorCode errorCode) {
|
| }
|
| }
|
|
|
| -ErrorSeverity _errorSeverity(AnalysisContext context, AnalysisError error) {
|
| - // Attempt to process severity in a similar way to analyzer_cli and server.
|
| - return ErrorProcessor
|
| - .getProcessor(context.analysisOptions, error)
|
| - ?.severity ??
|
| +ErrorSeverity _errorSeverity(
|
| + AnalysisOptions analysisOptions, AnalysisError error) {
|
| + return ErrorProcessor.getProcessor(analysisOptions, error)?.severity ??
|
| error.errorCode.errorSeverity;
|
| }
|
|
|
| -void _expectErrors(AnalysisContext context, CompilationUnit unit,
|
| +void _expectErrors(AnalysisOptions analysisOptions, CompilationUnit unit,
|
| List<AnalysisError> actualErrors) {
|
| var expectedErrors = _findExpectedErrors(unit.beginToken);
|
|
|
| @@ -190,7 +78,8 @@ void _expectErrors(AnalysisContext context, CompilationUnit unit,
|
| int delta = x.offset.compareTo(y.offset);
|
| if (delta != 0) return delta;
|
|
|
| - delta = _errorSeverity(context, x).compareTo(_errorSeverity(context, y));
|
| + delta = _errorSeverity(analysisOptions, x)
|
| + .compareTo(_errorSeverity(analysisOptions, y));
|
| if (delta != 0) return delta;
|
|
|
| return _errorCodeName(x.errorCode).compareTo(_errorCodeName(y.errorCode));
|
| @@ -212,7 +101,7 @@ void _expectErrors(AnalysisContext context, CompilationUnit unit,
|
| for (var expected in expectedErrors) {
|
| AnalysisError actual = expected._removeMatchingActual(actualErrors);
|
| if (actual != null) {
|
| - if (_errorSeverity(context, actual) != expected.severity ||
|
| + if (_errorSeverity(analysisOptions, actual) != expected.severity ||
|
| _errorCodeName(actual.errorCode) != expected.typeName) {
|
| different[expected] = actual;
|
| }
|
| @@ -225,7 +114,7 @@ void _expectErrors(AnalysisContext context, CompilationUnit unit,
|
| List<AnalysisError> unexpected = actualErrors;
|
|
|
| if (unreported.isNotEmpty || unexpected.isNotEmpty || different.isNotEmpty) {
|
| - _reportFailure(context, unit, unreported, unexpected, different);
|
| + _reportFailure(analysisOptions, unit, unreported, unexpected, different);
|
| }
|
| }
|
|
|
| @@ -264,15 +153,14 @@ SourceLocation _locationForOffset(LineInfo lineInfo, Uri uri, int offset) {
|
| }
|
|
|
| /// Returns all libraries transitively imported or exported from [start].
|
| -List<LibraryElement> _reachableLibraries(LibraryElement start) {
|
| - var results = <LibraryElement>[];
|
| - var seen = new Set();
|
| - void find(LibraryElement lib) {
|
| - if (seen.contains(lib)) return;
|
| - seen.add(lib);
|
| - results.add(lib);
|
| - lib.importedLibraries.forEach(find);
|
| - lib.exportedLibraries.forEach(find);
|
| +Set<LibraryElement> _reachableLibraries(LibraryElement start) {
|
| + Set<LibraryElement> results = new Set<LibraryElement>();
|
| +
|
| + void find(LibraryElement library) {
|
| + if (results.add(library)) {
|
| + library.importedLibraries.forEach(find);
|
| + library.exportedLibraries.forEach(find);
|
| + }
|
| }
|
|
|
| find(start);
|
| @@ -280,7 +168,7 @@ List<LibraryElement> _reachableLibraries(LibraryElement start) {
|
| }
|
|
|
| void _reportFailure(
|
| - AnalysisContext context,
|
| + AnalysisOptions analysisOptions,
|
| CompilationUnit unit,
|
| List<_ErrorExpectation> unreported,
|
| List<AnalysisError> unexpected,
|
| @@ -296,7 +184,7 @@ void _reportFailure(
|
| var span = _createSpanHelper(unit.lineInfo, offset,
|
| resolutionMap.elementDeclaredByCompilationUnit(unit).source, sourceCode,
|
| end: offset + length);
|
| - var levelName = _errorSeverity(context, error).displayName;
|
| + var levelName = _errorSeverity(analysisOptions, error).displayName;
|
| return '@$offset $levelName:${_errorCodeName(error.errorCode)}\n' +
|
| span.message(error.message);
|
| }
|
| @@ -334,17 +222,163 @@ void _reportFailure(
|
| fail('Checker errors do not match expected errors:\n\n$message');
|
| }
|
|
|
| +class AbstractStrongTest {
|
| + MemoryResourceProvider _resourceProvider = new MemoryResourceProvider();
|
| + bool _checkCalled = false;
|
| +
|
| + bool get enableNewAnalysisDriver => false;
|
| +
|
| + /// Adds a file to check. The file should contain:
|
| + ///
|
| + /// * all expected failures are listed in the source code using comments
|
| + /// immediately in front of the AST node that should contain the error.
|
| + ///
|
| + /// * errors are formatted as a token `severity:ErrorCode`, where
|
| + /// `severity` is the ErrorSeverity the error would be reported at, and
|
| + /// `ErrorCode` is the error code's name.
|
| + ///
|
| + /// For example to check that an assignment produces a type error, you can
|
| + /// create a file like:
|
| + ///
|
| + /// addFile('''
|
| + /// String x = /*error:STATIC_TYPE_ERROR*/3;
|
| + /// ''');
|
| + /// check();
|
| + ///
|
| + /// For a single file, you may also use [checkFile].
|
| + void addFile(String content, {String name: '/main.dart'}) {
|
| + name = name.replaceFirst('^package:', '/packages/');
|
| + _resourceProvider.newFile(_resourceProvider.convertPath(name), content);
|
| + }
|
| +
|
| + /// Run the checker on a program, staring from '/main.dart', and verifies that
|
| + /// errors/warnings/hints match the expected value.
|
| + ///
|
| + /// See [addFile] for more information about how to encode expectations in
|
| + /// the file text.
|
| + ///
|
| + /// Returns the main resolved library. This can be used for further checks.
|
| + Future<CompilationUnit> check(
|
| + {bool implicitCasts: true,
|
| + bool implicitDynamic: true,
|
| + List<String> nonnullableTypes:
|
| + AnalysisOptionsImpl.NONNULLABLE_TYPES}) async {
|
| + _checkCalled = true;
|
| +
|
| + File mainFile =
|
| + _resourceProvider.getFile(_resourceProvider.convertPath('/main.dart'));
|
| + expect(mainFile.exists, true, reason: '`/main.dart` is missing');
|
| +
|
| + AnalysisOptionsImpl analysisOptions = new AnalysisOptionsImpl();
|
| + analysisOptions.strongMode = true;
|
| + analysisOptions.strongModeHints = true;
|
| + analysisOptions.implicitCasts = implicitCasts;
|
| + analysisOptions.implicitDynamic = implicitDynamic;
|
| + analysisOptions.nonnullableTypes = nonnullableTypes;
|
| +
|
| + var mockSdk = new MockSdk(resourceProvider: _resourceProvider);
|
| + mockSdk.context.analysisOptions = analysisOptions;
|
| +
|
| + SourceFactory sourceFactory;
|
| + {
|
| + var uriResolver = new _TestUriResolver(_resourceProvider);
|
| + sourceFactory =
|
| + new SourceFactory([new DartUriResolver(mockSdk), uriResolver]);
|
| + }
|
| +
|
| + CompilationUnit mainUnit;
|
| + AnalysisContext context = null;
|
| + AnalysisDriver driver = null;
|
| + if (enableNewAnalysisDriver) {
|
| + // TODO(scheglov)
|
| + } else {
|
| + context = AnalysisEngine.instance.createAnalysisContext();
|
| + context.analysisOptions = analysisOptions;
|
| + context.sourceFactory = sourceFactory;
|
| +
|
| + // Run the checker on /main.dart.
|
| + Source mainSource = sourceFactory.forUri2(mainFile.toUri());
|
| + mainUnit = context.resolveCompilationUnit2(mainSource, mainSource);
|
| + }
|
| +
|
| + var collector = new _ErrorCollector(analysisOptions);
|
| +
|
| + // Extract expectations from the comments in the test files, and
|
| + // check that all errors we emit are included in the expected map.
|
| + LibraryElement mainLibrary =
|
| + resolutionMap.elementDeclaredByCompilationUnit(mainUnit).library;
|
| + Set<LibraryElement> allLibraries = _reachableLibraries(mainLibrary);
|
| + for (LibraryElement library in allLibraries) {
|
| + for (CompilationUnitElement unit in library.units) {
|
| + var errors = <AnalysisError>[];
|
| + collector.errors = errors;
|
| +
|
| + var source = unit.source;
|
| + if (source.uri.scheme == 'dart') {
|
| + continue;
|
| + }
|
| +
|
| + var analysisResult = await _resolve(context, driver, source);
|
| +
|
| + errors.addAll(analysisResult.errors.where((e) =>
|
| + // TODO(jmesserly): these are usually intentional dynamic calls.
|
| + e.errorCode.name != 'UNDEFINED_METHOD' &&
|
| + // We don't care about any of these:
|
| + e.errorCode != HintCode.UNUSED_ELEMENT &&
|
| + e.errorCode != HintCode.UNUSED_FIELD &&
|
| + e.errorCode != HintCode.UNUSED_IMPORT &&
|
| + e.errorCode != HintCode.UNUSED_LOCAL_VARIABLE &&
|
| + e.errorCode != TodoCode.TODO));
|
| + _expectErrors(analysisOptions, analysisResult.unit, errors);
|
| + }
|
| + }
|
| +
|
| + return mainUnit;
|
| + }
|
| +
|
| + /// Adds a file using [addFile] and calls [check].
|
| + ///
|
| + /// Also returns the resolved compilation unit.
|
| + Future<CompilationUnit> checkFile(String content) async {
|
| + addFile(content);
|
| + return check();
|
| + }
|
| +
|
| + void setUp() {
|
| + AnalysisEngine.instance.processRequiredPlugins();
|
| + }
|
| +
|
| + void tearDown() {
|
| + // This is a sanity check, in case only addFile is called.
|
| + expect(_checkCalled, true, reason: 'must call check() method in test case');
|
| + AnalysisEngine.instance.clearCaches();
|
| + }
|
| +
|
| + Future<_TestAnalysisResult> _resolve(
|
| + AnalysisContext context, AnalysisDriver driver, Source source) async {
|
| + if (enableNewAnalysisDriver) {
|
| + // TODO(scheglov) implement
|
| + throw new UnimplementedError();
|
| + } else {
|
| + List<Source> libraries = context.getLibrariesContaining(source);
|
| + var unit = context.resolveCompilationUnit2(source, libraries.single);
|
| + var errors = context.computeErrors(source);
|
| + return new _TestAnalysisResult(source, unit, errors);
|
| + }
|
| + }
|
| +}
|
| +
|
| class _ErrorCollector implements AnalysisErrorListener {
|
| - final AnalysisContext _context;
|
| + final AnalysisOptions analysisOptions;
|
| List<AnalysisError> errors;
|
| final bool hints;
|
|
|
| - _ErrorCollector(this._context, {this.hints: true});
|
| + _ErrorCollector(this.analysisOptions, {this.hints: true});
|
|
|
| void onError(AnalysisError error) {
|
| // Unless DDC hints are requested, filter them out.
|
| var HINT = ErrorSeverity.INFO.ordinal;
|
| - if (hints || _errorSeverity(_context, error).ordinal > HINT) {
|
| + if (hints || _errorSeverity(analysisOptions, error).ordinal > HINT) {
|
| errors.add(error);
|
| }
|
| }
|
| @@ -399,6 +433,13 @@ class _ErrorExpectation {
|
| }
|
| }
|
|
|
| +class _TestAnalysisResult {
|
| + final Source source;
|
| + final CompilationUnit unit;
|
| + final List<AnalysisError> errors;
|
| + _TestAnalysisResult(this.source, this.unit, this.errors);
|
| +}
|
| +
|
| class _TestUriResolver extends ResourceUriResolver {
|
| final MemoryResourceProvider provider;
|
| _TestUriResolver(provider)
|
|
|