mirror of https://github.com/AMT-Cheif/drift.git
Fix Dart reference analysis in new analyzer
This commit is contained in:
parent
0f97b42a43
commit
eabf2a1e03
|
@ -20,19 +20,12 @@ builders:
|
|||
build_to: cache
|
||||
applies_builders: ["drift_dev:cleanup"]
|
||||
|
||||
analysis:
|
||||
import: "package:drift_dev/integrations/build.dart"
|
||||
builder_factories: ["analyzer"]
|
||||
build_extensions:
|
||||
".drift": [".drift.drift_module.json"]
|
||||
".dart": [".dart.drift_module.json"]
|
||||
build_to: cache
|
||||
required_inputs: [".drift_prep.json"]
|
||||
|
||||
drift_dev:
|
||||
import: "package:drift_dev/integrations/build.dart"
|
||||
builder_factories: ["driftBuilder"]
|
||||
build_extensions: {".dart": [".drift.g.part"]}
|
||||
builder_factories: ["analyzer", "driftBuilder"]
|
||||
build_extensions:
|
||||
".dart": [".drift.g.part", ".dart.drift_module.json"]
|
||||
".drift": [".drift.drift_module.json"]
|
||||
auto_apply: dependents
|
||||
build_to: cache
|
||||
required_inputs: [".drift_prep.json"]
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import 'package:meta/meta.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
|
||||
import '../../analyzer/options.dart';
|
||||
|
@ -35,7 +34,6 @@ class DriftAnalysisDriver {
|
|||
return _knownTypes ??= await KnownDriftTypes.resolve(this);
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
Future<FileState> prepareFileForAnalysis(Uri uri) async {
|
||||
var known = cache.knownFiles[uri] ?? cache.notifyFileChanged(uri);
|
||||
|
||||
|
@ -76,7 +74,12 @@ class DriftAnalysisDriver {
|
|||
for (final discovered in state.discovery!.locallyDefinedElements) {
|
||||
if (!state.elementIsAnalyzed(discovered.ownId)) {
|
||||
final resolver = DriftResolver(this);
|
||||
await resolver.resolveDiscovered(discovered);
|
||||
|
||||
try {
|
||||
await resolver.resolveDiscovered(discovered);
|
||||
} catch (e, s) {
|
||||
backend.log.warning('Could not analyze ${discovered.ownId}', e, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ class DartTableResolver extends LocalElementResolver<DiscoveredDartTable> {
|
|||
|
||||
@override
|
||||
Future<DriftElement> resolve() async {
|
||||
final element = discovered.element;
|
||||
final element = discovered.dartElement;
|
||||
|
||||
final pendingColumns = (await _parseColumns(element)).toList();
|
||||
final columns = [for (final column in pendingColumns) column.column];
|
||||
|
@ -27,18 +27,7 @@ class DartTableResolver extends LocalElementResolver<DiscoveredDartTable> {
|
|||
|
||||
final dataClassInfo = _readDataClassInformation(columns, element);
|
||||
|
||||
final table = DriftTable(
|
||||
discovered.ownId,
|
||||
DriftDeclaration.dartElement(element),
|
||||
columns: columns,
|
||||
nameOfRowClass: dataClassInfo.enforcedName,
|
||||
existingRowClass: dataClassInfo.existingClass,
|
||||
customParentClass: dataClassInfo.extending,
|
||||
baseDartName: element.name,
|
||||
primaryKeyFromTableConstraint: primaryKey,
|
||||
uniqueKeysFromTableConstraint: uniqueKeys ?? const [],
|
||||
withoutRowId: await _overrideWithoutRowId(element) ?? false,
|
||||
);
|
||||
final references = <DriftElement>{};
|
||||
|
||||
// Resolve local foreign key references in pending columns
|
||||
for (final column in pendingColumns) {
|
||||
|
@ -49,9 +38,29 @@ class DartTableResolver extends LocalElementResolver<DiscoveredDartTable> {
|
|||
(e) => e.nameInDart == column.referencesColumnInSameTable);
|
||||
|
||||
ref.otherColumn = referencedColumn;
|
||||
} else {
|
||||
for (final constraint in column.column.constraints) {
|
||||
if (constraint is ForeignKeyReference) {
|
||||
references.add(constraint.otherColumn.owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final table = DriftTable(
|
||||
discovered.ownId,
|
||||
DriftDeclaration.dartElement(element),
|
||||
columns: columns,
|
||||
references: references.toList(),
|
||||
nameOfRowClass: dataClassInfo.enforcedName,
|
||||
existingRowClass: dataClassInfo.existingClass,
|
||||
customParentClass: dataClassInfo.extending,
|
||||
baseDartName: element.name,
|
||||
primaryKeyFromTableConstraint: primaryKey,
|
||||
uniqueKeysFromTableConstraint: uniqueKeys ?? const [],
|
||||
withoutRowId: await _overrideWithoutRowId(element) ?? false,
|
||||
);
|
||||
|
||||
if (primaryKey != null &&
|
||||
columns.any((c) => c.constraints.any((e) => e is PrimaryKeyColumn))) {
|
||||
reportError(DriftAnalysisError.forDartElement(
|
||||
|
|
|
@ -15,8 +15,13 @@ class DiscoveredDriftView extends DiscoveredElement {
|
|||
DiscoveredDriftView(super.ownId, this.createView);
|
||||
}
|
||||
|
||||
class DiscoveredDartTable extends DiscoveredElement {
|
||||
final ClassElement element;
|
||||
abstract class DiscoveredDartElement<DE extends Element>
|
||||
extends DiscoveredElement {
|
||||
final DE dartElement;
|
||||
|
||||
DiscoveredDartTable(super.ownId, this.element);
|
||||
DiscoveredDartElement(super.ownId, this.dartElement);
|
||||
}
|
||||
|
||||
class DiscoveredDartTable extends DiscoveredDartElement<ClassElement> {
|
||||
DiscoveredDartTable(super.ownId, super.dartElement);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
import '../driver/driver.dart';
|
||||
import '../driver/error.dart';
|
||||
|
@ -95,9 +96,20 @@ class DriftResolver {
|
|||
Future<ResolveReferencedElementResult> resolveDartReference(
|
||||
DriftElementId owner, Element element) async {
|
||||
final uri = await driver.backend.uriOfDart(element.library!);
|
||||
final id = DriftElementId(uri, element.name!);
|
||||
final state = await driver.prepareFileForAnalysis(uri);
|
||||
|
||||
return resolveReferencedElement(owner, id);
|
||||
final discovered = state.discovery?.locallyDefinedElements
|
||||
.whereType<DiscoveredDartElement>()
|
||||
.firstWhereOrNull((c) => c.dartElement == element);
|
||||
|
||||
if (discovered != null) {
|
||||
return resolveReferencedElement(owner, discovered.ownId);
|
||||
} else {
|
||||
return InvalidReferenceResult(
|
||||
InvalidReferenceError.noElementWichSuchName,
|
||||
'The referenced element is not understood by drift.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<ResolveReferencedElementResult> resolveReference(
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
import 'package:drift_dev/src/analysis/results/results.dart';
|
||||
import 'package:sqlparser/sqlparser.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import '../../test_utils.dart';
|
||||
|
||||
void main() {
|
||||
group('reports a warning', () {
|
||||
test('when the table is not a class type', () async {
|
||||
final backend = TestBackend.inTest({
|
||||
'a|lib/main.dart': '''
|
||||
import 'package:drift/drift.dart';
|
||||
|
||||
class Foo extends Table {
|
||||
TextColumn get foo => text().references(dynamic, #what)();
|
||||
}
|
||||
'''
|
||||
});
|
||||
|
||||
final file =
|
||||
await backend.driver.fullyAnalyze(Uri.parse('package:a/main.dart'));
|
||||
expect(file.errorsDuringDiscovery, isEmpty);
|
||||
|
||||
final result = file.analysis.values.single;
|
||||
expect(result.result, isA<DriftTable>());
|
||||
expect(result.errorsDuringAnalysis, [
|
||||
isDriftError('`dynamic` is not a class!').withSpan('dynamic'),
|
||||
]);
|
||||
});
|
||||
|
||||
test('when the table is not a symbol literal', () async {
|
||||
final backend = TestBackend.inTest({
|
||||
'a|lib/main.dart': '''
|
||||
import 'package:drift/drift.dart';
|
||||
|
||||
const column = #other;
|
||||
|
||||
class Foo extends Table {
|
||||
TextColumn get foo => text().references(Table, column)();
|
||||
}
|
||||
'''
|
||||
});
|
||||
|
||||
final file =
|
||||
await backend.driver.fullyAnalyze(Uri.parse('package:a/main.dart'));
|
||||
expect(file.errorsDuringDiscovery, isEmpty);
|
||||
|
||||
final result = file.analysis.values.single;
|
||||
expect(result.result, isA<DriftTable>());
|
||||
expect(result.errorsDuringAnalysis, [
|
||||
isDriftError(contains('This should be a symbol literal'))
|
||||
.withSpan('column'),
|
||||
]);
|
||||
});
|
||||
|
||||
test('when the referenced table does not exist', () async {
|
||||
final backend = TestBackend.inTest({
|
||||
'a|lib/main.dart': '''
|
||||
import 'package:drift/drift.dart';
|
||||
|
||||
class OtherTable {
|
||||
// not a table!
|
||||
}
|
||||
|
||||
class Foo extends Table {
|
||||
TextColumn get foo => text().references(OtherTable, #column)();
|
||||
}
|
||||
'''
|
||||
});
|
||||
|
||||
final file =
|
||||
await backend.driver.fullyAnalyze(Uri.parse('package:a/main.dart'));
|
||||
expect(file.errorsDuringDiscovery, isEmpty);
|
||||
|
||||
final result = file.analysis.values.single;
|
||||
expect(result.result, isA<DriftTable>());
|
||||
expect(result.errorsDuringAnalysis, [
|
||||
isDriftError('The referenced element is not understood by drift.')
|
||||
.withSpan('OtherTable'),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
test('resolves reference', () async {
|
||||
final backend = TestBackend.inTest({
|
||||
'a|lib/main.dart': '''
|
||||
import 'package:drift/drift.dart';
|
||||
|
||||
class OtherTable extends Table {
|
||||
TextColumn get column => text()();
|
||||
}
|
||||
|
||||
class Foo extends Table {
|
||||
TextColumn get foo => text().references(OtherTable, #column,
|
||||
onUpdate: KeyAction.restrict, onDelete: KeyAction.cascade)();
|
||||
}
|
||||
'''
|
||||
});
|
||||
|
||||
final uri = Uri.parse('package:a/main.dart');
|
||||
final file = await backend.driver.fullyAnalyze(uri);
|
||||
final otherTable =
|
||||
file.analysis[DriftElementId(uri, 'other_table')]!.result as DriftTable;
|
||||
final foo = file.analysis[DriftElementId(uri, 'foo')]!.result as DriftTable;
|
||||
|
||||
expect(foo.references, [otherTable]);
|
||||
|
||||
final column = foo.columns.single;
|
||||
final feature = column.constraints.whereType<ForeignKeyReference>().first;
|
||||
|
||||
expect(feature.otherColumn.nameInDart, 'column');
|
||||
expect(feature.otherColumn.owner, otherTable);
|
||||
expect(feature.onUpdate, ReferenceAction.restrict);
|
||||
expect(feature.onDelete, ReferenceAction.cascade);
|
||||
});
|
||||
|
||||
test('resolves self-references', () async {
|
||||
final backend = TestBackend.inTest({
|
||||
'a|lib/main.dart': '''
|
||||
import 'package:drift/drift.dart';
|
||||
|
||||
class Foo extends Table {
|
||||
IntColumn get id => integer().autoIncrement()();
|
||||
IntColumn get parentId => integer().nullable().references(Foo, #id)();
|
||||
}
|
||||
'''
|
||||
});
|
||||
|
||||
final file =
|
||||
await backend.driver.fullyAnalyze(Uri.parse('package:a/main.dart'));
|
||||
final table = file.analysis.values.single.result as DriftTable;
|
||||
|
||||
expect(table.references, isEmpty);
|
||||
|
||||
final id = table.columns[0];
|
||||
final parentId = table.columns[1];
|
||||
|
||||
expect(
|
||||
parentId.constraints,
|
||||
contains(isA<ForeignKeyReference>()
|
||||
.having((e) => e.otherColumn, 'otherColumn', id)));
|
||||
});
|
||||
}
|
|
@ -9,7 +9,7 @@ void main() {
|
|||
late TestBackend backend;
|
||||
|
||||
setUpAll(() {
|
||||
backend = TestBackend.inTest({
|
||||
backend = TestBackend({
|
||||
'a|lib/main.dart': '''
|
||||
import 'package:drift/drift.dart';
|
||||
|
||||
|
|
|
@ -31,7 +31,8 @@ class TestBackend extends DriftBackend {
|
|||
|
||||
AnalysisContext? _dartContext;
|
||||
|
||||
TestBackend(Map<String, String> sourceContents, DriftOptions options)
|
||||
TestBackend(Map<String, String> sourceContents,
|
||||
{DriftOptions options = const DriftOptions.defaults()})
|
||||
: sourceContents = {
|
||||
for (final entry in sourceContents.entries)
|
||||
AssetId.parse(entry.key).uri.toString(): entry.value,
|
||||
|
@ -41,7 +42,7 @@ class TestBackend extends DriftBackend {
|
|||
|
||||
factory TestBackend.inTest(Map<String, String> sourceContents,
|
||||
{DriftOptions options = const DriftOptions.defaults()}) {
|
||||
final backend = TestBackend(sourceContents, options);
|
||||
final backend = TestBackend(sourceContents, options: options);
|
||||
addTearDown(backend.dispose);
|
||||
|
||||
return backend;
|
||||
|
|
Loading…
Reference in New Issue