Respect transitive imports when resolving moor converters

Might fix an error discovered in #482
This commit is contained in:
Simon Binder 2020-04-09 22:28:40 +02:00
parent af5333db3c
commit 72e65611a7
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
3 changed files with 82 additions and 9 deletions

View File

@ -1203,7 +1203,7 @@ abstract class _$CustomTablesDb extends GeneratedDatabase {
Selectable<MultipleResult> multiple(Expression<bool> predicate) {
final generatedpredicate = $write(predicate, hasMultipleTables: true);
return customSelect(
'SELECT d.*, "c.a" AS "nested_0.a", "c.b" AS "nested_0.b", "c.c" AS "nested_0.c" FROM with_constraints c\n INNER JOIN with_defaults d\n ON d.a = c.a AND d.b = c.b\n WHERE ${generatedpredicate.sql}',
'SELECT d.*, "c"."a" AS "nested_0.a", "c"."b" AS "nested_0.b", "c"."c" AS "nested_0.c" FROM with_constraints c\n INNER JOIN with_defaults d\n ON d.a = c.a AND d.b = c.b\n WHERE ${generatedpredicate.sql}',
variables: [...generatedpredicate.introducedVariables],
readsFrom: {withConstraints, withDefaults}).map(_rowToMultipleResult);
}

View File

@ -38,26 +38,51 @@ class PreprocessBuilder extends Builder {
final moorFileContent = await buildStep.readAsString(input);
final engine = SqlEngine(EngineOptions(useMoorExtensions: true));
final parsed = engine.parseMoorFile(moorFileContent);
final parsedInput = engine.parseMoorFile(moorFileContent);
final dartLexemes = parsed.tokens
final dartLexemes = parsedInput.tokens
.whereType<InlineDartToken>()
.map((token) => token.dartCode)
.toList();
if (dartLexemes.isEmpty) return; // nothing to do, no Dart in this moor file
final importedFiles = parsed.rootNode.allDescendants
.whereType<ImportStatement>()
.map((stmt) => stmt.importedFile)
.where((import) => import.endsWith('.dart'));
// Crawl through transitive imports and find all Dart libraries
final seenFiles = <AssetId>{};
final queue = <AssetId>[input];
while (queue.isNotEmpty) {
final asset = queue.removeLast();
if (!seenFiles.contains(asset)) {
seenFiles.add(asset);
if (asset.extension == '.moor') {
final parsed = asset == input
? parsedInput
: engine.parseMoorFile(await buildStep.readAsString(asset));
parsed.rootNode.allDescendants
.whereType<ImportStatement>()
.map((stmt) => AssetId.resolve(stmt.importedFile, from: asset))
.where((importedId) =>
!seenFiles.contains(importedId) &&
!queue.contains(importedId))
.forEach(queue.add);
}
}
}
final importedDartFiles =
seenFiles.where((asset) => asset.extension == '.dart');
// to analyze the expressions, generate a fake Dart file that declares each
// expression in a `var`, we can then read the static type.
final dartBuffer = StringBuffer();
for (final import in importedFiles) {
dartBuffer.write('import ${asDartLiteral(import)};\n');
for (final import in importedDartFiles) {
final importUri = import.uri.toString();
dartBuffer.write('import ${asDartLiteral(importUri)};\n');
}
for (var i = 0; i < dartLexemes.length; i++) {

View File

@ -51,4 +51,52 @@ class MyConverter extends TypeConverter<DateTime, int> {
'type_args': [],
});
});
test('finds dart files over transitive imports', () async {
final writer = InMemoryAssetWriter();
final reader = await PackageAssetReader.currentIsolate();
await testBuilder(
PreprocessBuilder(),
{
'foo|main.moor': '''
import 'indirection.moor';
CREATE TABLE foo (
id INT NOT NULL MAPPED BY `const MyConverter()`
);
''',
'foo|indirection.moor': '''
import 'converter.dart';
''',
'foo|converter.dart': '''
import 'package:moor/moor.dart';
class MyConverter extends TypeConverter<DateTime, int> {
const MyConverter();
int mapToSql(DateTime time) => time?.millisecondsSinceEpoch;
DateTime mapToDart(int fromSql) {
if (fromSql == null) return null;
return DateTime.fromMillisecondsSinceEpoch(fromSql);
}
}
''',
},
writer: writer,
reader: reader,
);
final output =
utf8.decode(writer.assets[AssetId.parse('foo|main.dart_in_moor')]);
final serialized = json.decode(output);
expect(serialized['const MyConverter()'], {
'type': 'interface',
'library': 'asset:foo/converter.dart',
'class_name': 'MyConverter',
'type_args': [],
});
});
}