From debf8b30f43e9154ac846b7d5f46ce984c90659a Mon Sep 17 00:00:00 2001 From: Simon Binder Date: Mon, 6 Jan 2020 19:57:44 +0100 Subject: [PATCH] Report error when import can't be resolved --- .../lib/src/analyzer/runner/task.dart | 36 ++++++++++--- moor_generator/lib/src/analyzer/session.dart | 6 ++- moor_generator/lib/src/backends/backend.dart | 2 + .../lib/src/backends/common/backend.dart | 5 +- .../lib/src/backends/common/driver.dart | 3 +- .../test/analyzer/runner/task_test.dart | 51 +++++++++++++++++++ 6 files changed, 92 insertions(+), 11 deletions(-) create mode 100644 moor_generator/test/analyzer/runner/task_test.dart diff --git a/moor_generator/lib/src/analyzer/runner/task.dart b/moor_generator/lib/src/analyzer/runner/task.dart index 61b76695..32588e18 100644 --- a/moor_generator/lib/src/analyzer/runner/task.dart +++ b/moor_generator/lib/src/analyzer/runner/task.dart @@ -89,14 +89,16 @@ class Task { continue; } - final found = session.resolve(file, import.importedFile); - if (!await backend.exists(found.uri)) { + final found = await _resolveOrReportError(file, import.importedFile, + (errorMsg) { step.reportError(ErrorInMoorFile( span: import.importString.span, severity: Severity.error, - message: 'File does not exist: ${import.importedFile}', + message: errorMsg, )); - } else { + }); + + if (found != null) { resolvedImports.add(found); parsed.resolvedImports[import] = found; } @@ -123,14 +125,14 @@ class Task { final resolvedForAccessor = []; for (final import in accessor.declaredIncludes) { - final found = session.resolve(file, import); - if (!await backend.exists(found.uri)) { + final found = await _resolveOrReportError(file, import, (errorMsg) { step.reportError(ErrorInDartCode( affectedElement: accessor.fromClass, severity: Severity.error, - message: 'Include could not be resolved: $import', + message: errorMsg, )); - } else { + }); + if (found != null) { resolvedImports.add(found); resolvedForAccessor.add(found); } @@ -150,6 +152,24 @@ class Task { return createdStep; } + Future _resolveOrReportError( + FoundFile file, + String import, + void Function(String) reporter, + ) async { + final found = session.resolve(file, import); + + if (found == null) { + reporter('Invalid import: $import'); + } else if (!await backend.exists(found.uri)) { + reporter('Imported file does not exist: $import'); + } else { + return found; + } + + return null; + } + /// Crawls through all (transitive) imports of the provided [roots]. Each /// [FoundFile] in the iterable provides queries and tables that are available /// to the entity that imports them. diff --git a/moor_generator/lib/src/analyzer/session.dart b/moor_generator/lib/src/analyzer/session.dart index 9c94ce4e..dadc0614 100644 --- a/moor_generator/lib/src/analyzer/session.dart +++ b/moor_generator/lib/src/analyzer/session.dart @@ -55,9 +55,13 @@ class MoorSession { /// Resolves an import directive in the context of the [source] file. This /// can handle both relative imports and `package:` imports. + /// + /// Returns null if the import could not be resolved. Note that it does not + /// return null if the file doesn't exists - that needs to be checked + /// separately. FoundFile resolve(FoundFile source, String import) { final resolvedUri = backend.resolve(source.uri, import); - return _uriToFile(resolvedUri); + return resolvedUri == null ? null : _uriToFile(resolvedUri); } /// Registers a file by its absolute uri. diff --git a/moor_generator/lib/src/backends/backend.dart b/moor_generator/lib/src/backends/backend.dart index 67feda00..9e413a2b 100644 --- a/moor_generator/lib/src/backends/backend.dart +++ b/moor_generator/lib/src/backends/backend.dart @@ -9,6 +9,8 @@ import 'package:logging/logging.dart'; abstract class Backend { /// Resolves an [import] statement from the context of a [base] uri. This /// should support both relative and `package:` imports. + /// + /// Returns null if the url can't be resolved. Uri resolve(Uri base, String import); } diff --git a/moor_generator/lib/src/backends/common/backend.dart b/moor_generator/lib/src/backends/common/backend.dart index 2218a159..c55154af 100644 --- a/moor_generator/lib/src/backends/common/backend.dart +++ b/moor_generator/lib/src/backends/common/backend.dart @@ -12,7 +12,10 @@ class CommonBackend extends Backend { @override Uri resolve(Uri base, String import) { - return Uri.parse(driver.absolutePath(Uri.parse(import), base: base)); + final absolute = driver.absolutePath(Uri.parse(import), base: base); + if (absolute == null) return null; + + return Uri.parse(absolute); } } diff --git a/moor_generator/lib/src/backends/common/driver.dart b/moor_generator/lib/src/backends/common/driver.dart index 58bf5e46..470affee 100644 --- a/moor_generator/lib/src/backends/common/driver.dart +++ b/moor_generator/lib/src/backends/common/driver.dart @@ -133,12 +133,13 @@ class MoorDriver implements AnalysisDriverGeneric { /// Finds the absolute path of a [reference] url, optionally assuming that the /// [reference] appears in [base]. This supports both "package:"-based uris /// and relative imports. + /// Returns null if the uri can't be parsed. String absolutePath(Uri reference, {Uri base}) { final factory = dartDriver.sourceFactory; final baseSource = base == null ? null : factory.forUri2(base); final source = factory.resolveUri(baseSource, reference.toString()); - return source.fullName; + return source?.fullName; } CommonTask _createTask(Uri uri) { diff --git a/moor_generator/test/analyzer/runner/task_test.dart b/moor_generator/test/analyzer/runner/task_test.dart new file mode 100644 index 00000000..85222d2e --- /dev/null +++ b/moor_generator/test/analyzer/runner/task_test.dart @@ -0,0 +1,51 @@ +import 'package:moor_generator/src/analyzer/errors.dart'; +import 'package:test/test.dart'; + +import '../utils.dart'; + +void main() { + group("reports error when an import can't be found", () { + test('in moor file', () async { + final state = TestState.withContent({ + 'foo|lib/a.moor': ''' +import 'b.moor'; + ''', + }); + + final result = await state.analyze('package:foo/a.moor'); + + expect( + result.errors.errors, + contains(const TypeMatcher().having( + (e) => e.message, + 'message', + allOf(contains('b.moor'), contains('file does not exist')), + )), + ); + }); + + test('in a dart file', () async { + final state = TestState.withContent({ + 'foo|lib/a.dart': ''' +import 'package:moor/moor.dart'; + +@UseMoor(include: {'b.moor'}) +class Database { + +} + ''', + }); + + final result = await state.analyze('package:foo/a.dart'); + + expect( + result.errors.errors, + contains(const TypeMatcher().having( + (e) => e.message, + 'message', + allOf(contains('b.moor'), contains('file does not exist')), + )), + ); + }); + }); +}