Migrate simple CLI tools to new analyzer

This commit is contained in:
Simon Binder 2022-11-05 18:32:13 +01:00
parent c5504237d5
commit c5a800d4c4
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
10 changed files with 213 additions and 49 deletions

View File

@ -1,4 +1,3 @@
// @dart=2.9
import 'package:drift_dev/src/cli/cli.dart' as cli;
Future main(List<String> args) {

View File

@ -104,6 +104,10 @@ class NoSuchFile extends DiscoveredFileState {
NoSuchFile() : super(const []);
}
class UnknownFile extends DiscoveredFileState {
UnknownFile() : super(const []);
}
abstract class DiscoveredElement {
final DriftElementId ownId;

View File

@ -300,7 +300,7 @@ class DartTableResolver extends LocalElementResolver<DiscoveredDartTable> {
Future<Iterable<PendingColumnInformation>> _parseColumns(
ClassElement element) async {
final columnNames = element.allSupertypes
.map((t) => t.element2)
.map((t) => t.element)
.followedBy([element])
.expand((e) => e.fields)
.where((field) =>

View File

@ -45,7 +45,7 @@ class DartViewResolver extends LocalElementResolver<DiscoveredDartView> {
Future<List<TableReferenceInDartView>> _parseStaticReferences() async {
return await Stream.fromIterable(discovered.dartElement.allSupertypes
.map((t) => t.element2)
.map((t) => t.element)
.followedBy([discovered.dartElement]).expand((e) => e.fields))
.asyncMap((field) => _getStaticReference(field))
.where((ref) => ref != null)
@ -68,13 +68,13 @@ class DartViewResolver extends LocalElementResolver<DiscoveredDartView> {
await resolver.driver.backend.loadElementDeclaration(field.getter!);
if (node is MethodDeclaration && node.body is EmptyFunctionBody) {
final table = await resolveDartReferenceOrReportError<DriftTable>(
type.element2, (msg) {
type.element, (msg) {
return DriftAnalysisError.inDartAst(
field, node.returnType ?? node.name, msg);
});
if (table != null) {
final name = node.name2.lexeme;
final name = node.name.lexeme;
return TableReferenceInDartView(table, name);
}
}

View File

@ -51,6 +51,7 @@ class DiscoverStep {
Future<void> discover() async {
final extension = _file.extension;
_file.discovery = UnknownFile();
switch (extension) {
case '.dart':
@ -170,7 +171,10 @@ class _FindDartElements extends RecursiveElementVisitor<void> {
// implementations as tables.
return _isTable.isAssignableFrom(element) &&
!_isTable.isExactly(element) &&
!_isTableInfo.isAssignableFrom(element);
!_isTableInfo.isAssignableFrom(element) &&
// Temporary workaround until https://github.com/dart-lang/source_gen/pull/628
// gets merged.
!element.mixins.any((e) => e.nameIfInterfaceType == 'TableInfo');
}
bool _isDslView(ClassElement element) {

View File

@ -0,0 +1,160 @@
import 'package:analyzer/dart/analysis/analysis_context.dart';
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/file_system/overlay_file_system.dart';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:logging/logging.dart';
import '../analysis/backend.dart';
import '../analysis/driver/driver.dart';
import '../analysis/driver/state.dart';
import '../analyzer/options.dart';
class PhysicalDriftDriver {
final DriftAnalysisDriver driver;
final AnalysisContextBackend _backend;
PhysicalDriftDriver(this.driver, this._backend);
Uri uriFromPath(String path) => _backend.provider.pathContext.toUri(path);
Future<FileState> analyzeElementsForPath(String path) {
return driver.resolveElements(uriFromPath(path));
}
}
/// A drift analysis backend deferring Dart analysis to the given [context].
class AnalysisContextBackend extends DriftBackend {
@override
final Logger log = Logger('drift.analysis');
final AnalysisContext context;
/// An overlay resource provider, which must also be used by the [context].
///
/// This will be used to create artificial files used to resolve the type of
/// Dart expressions.
final OverlayResourceProvider provider;
AnalysisContextBackend(this.context, this.provider);
static PhysicalDriftDriver createDriver({
DriftOptions options = const DriftOptions.defaults(),
ResourceProvider? resourceProvider,
required String projectDirectory,
}) {
final underlyingProvider =
resourceProvider ?? PhysicalResourceProvider.INSTANCE;
final provider = OverlayResourceProvider(underlyingProvider);
final contextCollection = AnalysisContextCollection(
includedPaths: [projectDirectory],
resourceProvider: provider,
);
final context = contextCollection.contextFor(projectDirectory);
final backend = AnalysisContextBackend(context, provider);
final driver = DriftAnalysisDriver(backend, options);
return PhysicalDriftDriver(driver, backend);
}
String? _pathOfUri(Uri uri) {
final currentSession = context.currentSession;
final path = currentSession.uriConverter.uriToPath(uri);
return path;
}
@override
Future<AstNode?> loadElementDeclaration(Element element) async {
final library = element.library;
if (library == null) return null;
final info =
await context.currentSession.getResolvedLibraryByElement(library);
if (info is ResolvedLibraryResult) {
return info.getElementDeclaration(element)?.node;
} else {
return null;
}
}
@override
Future<String> readAsString(Uri uri) {
final path = _pathOfUri(uri);
if (path == null) return Future.error('Uri $uri could not be resolved');
final resourceProvider = context.currentSession.resourceProvider;
return Future.value(resourceProvider.getFile(path).readAsStringSync());
}
@override
Future<LibraryElement> readDart(Uri uri) async {
final result = await context.currentSession.getLibraryByUri(uri.toString());
if (result is LibraryElementResult) {
return result.element;
}
throw NotALibraryException(uri);
}
@override
Future<Expression> resolveExpression(
Uri context, String dartExpression, Iterable<String> imports) async {
// Create a fake file next to the content
final path = _pathOfUri(context)!;
final pathContext = provider.pathContext;
final pathForTemp = pathContext.join(
pathContext.dirname(path), 'moor_temp_${dartExpression.hashCode}.dart');
final content = StringBuffer();
for (final import in imports) {
content.writeln('import "$import";');
}
content.writeln('var e = $dartExpression;');
provider.setOverlay(
pathForTemp,
content: content.toString(),
modificationStamp: DateTime.now().millisecondsSinceEpoch,
);
try {
final result =
await this.context.currentSession.getResolvedLibrary(pathForTemp);
if (result is! ResolvedLibraryResult) {
throw CannotReadExpressionException(
'Could not resolve temporary helper file');
}
final compilationUnit = result.units.first.unit;
for (final member in compilationUnit.declarations) {
if (member is TopLevelVariableDeclaration) {
return member.variables.variables.first.initializer!;
}
}
throw CannotReadExpressionException(
'Temporary helper file contains no field.');
} finally {
provider.removeOverlay(pathForTemp);
}
}
@override
Uri resolveUri(Uri base, String uriString) {
final resolved = base.resolve(uriString);
final uriConverter = context.currentSession.uriConverter;
// Try to make uris consistent by going to path and back
final path = uriConverter.uriToPath(resolved);
if (path == null) return resolved;
return uriConverter.pathToUri(path) ?? resolved;
}
}

View File

@ -1,16 +1,15 @@
import 'dart:async';
import 'dart:io';
import 'package:analyzer/file_system/physical_file_system.dart';
import 'package:args/command_runner.dart';
import 'package:drift_dev/src/backends/common/driver.dart';
import 'package:drift_dev/src/cli/project.dart';
import 'package:logging/logging.dart';
import '../backends/analyzer_context_backend.dart';
import 'commands/analyze.dart';
import 'commands/identify_databases.dart';
import 'commands/migrate.dart';
import 'commands/schema.dart';
//import 'commands/migrate.dart';
//import 'commands/schema.dart';
import 'logging.dart';
Future run(List<String> args) async {
@ -31,14 +30,14 @@ class MoorCli {
MoorCli() {
_runner = CommandRunner(
'pub run moor_generator',
'CLI utilities for the moor package, currently in an experimental state.',
'dart run drift_dev',
'CLI utilities for the drift package, currently in an experimental state.',
usageLineLength: 80,
)
..addCommand(AnalyzeCommand(this))
..addCommand(IdentifyDatabases(this))
..addCommand(SchemaCommand(this))
..addCommand(MigrateCommand(this));
..addCommand(IdentifyDatabases(this));
// ..addCommand(SchemaCommand(this))
// ..addCommand(MigrateCommand(this));
_runner.argParser
.addFlag('verbose', abbr: 'v', defaultsTo: false, negatable: false);
@ -50,9 +49,11 @@ class MoorCli {
);
}
Future<MoorDriver> createMoorDriver() async {
return MoorDriver(PhysicalResourceProvider.INSTANCE,
options: project.moorOptions, contextRoot: project.directory.path);
Future<PhysicalDriftDriver> createMoorDriver() async {
return AnalysisContextBackend.createDriver(
options: project.moorOptions,
projectDirectory: project.directory.path,
);
}
Future<void> run(Iterable<String> args) async {

View File

@ -1,14 +1,12 @@
import 'dart:io';
import 'package:path/path.dart' as p;
import '../cli.dart';
class AnalyzeCommand extends MoorCommand {
AnalyzeCommand(MoorCli cli) : super(cli);
@override
String get description => 'Analyze and lint moor files';
String get description => 'Analyze and lint drift database code';
@override
String get name => 'analyze';
@ -20,18 +18,14 @@ class AnalyzeCommand extends MoorCommand {
var errorCount = 0;
await for (final file in cli.project.sourceFiles) {
if (p.extension(file.path) != '.moor') continue;
cli.logger.fine('Analyzing $file');
final parsed = (await driver.waitFileParsed(file.path))!;
final results =
await driver.driver.fullyAnalyze(driver.uriFromPath(file.path));
if (parsed.errors.errors.isNotEmpty) {
cli.logger.warning('For file ${p.relative(file.path)}:');
for (final error in parsed.errors.errors) {
error.writeDescription(cli.logger.warning);
errorCount++;
}
for (final error in results.allErrors) {
cli.logger.warning(error.toString());
errorCount++;
}
}

View File

@ -1,9 +1,9 @@
import 'dart:async';
import 'dart:io';
import 'package:drift_dev/src/analyzer/runner/results.dart';
import 'package:path/path.dart' as p;
import '../../analysis/results/results.dart';
import '../cli.dart';
class IdentifyDatabases extends MoorCommand {
@ -11,7 +11,7 @@ class IdentifyDatabases extends MoorCommand {
@override
String get description =>
'Test for the analyzer - list all moor databases in a project';
'Test for the analyzer - list all drift databases in a project';
@override
String get name => 'identify-databases';
@ -28,20 +28,24 @@ class IdentifyDatabases extends MoorCommand {
cli.logger.fine('Scanning $file');
final parsed = (await driver.waitFileParsed(file.path))!;
final result = parsed.currentResult;
final result = await driver.analyzeElementsForPath(file.path);
for (final analyzedElement in result.analysis.values) {
final element = analyzedElement.result;
// might be a `part of` file...
if (result is! ParsedDartFile) continue;
if (element is BaseDriftAccessor) {
final message = StringBuffer(
'Found ${element.id.name} in ${element.id.libraryUri}!');
if (result.dbAccessors.isNotEmpty) {
final displayName = p.relative(file.path, from: directory.path);
if (element is DriftDatabase) {
final daos =
element.accessorTypes.map((e) => e.toString()).join(', ');
message
..writeln()
..write('Schema version: ${element.schemaVersion}, daos: $daos');
}
final names = result.dbAccessors
.map((t) => t.declaration!.fromClass.name)
.join(', ');
cli.logger.info('$displayName has moor databases or daos: $names');
cli.logger.info(message);
}
}
}
}

View File

@ -38,16 +38,14 @@ class DumpSchemaCommand extends Command {
final driver = await cli.createMoorDriver();
final absolute = File(rest[0]).absolute.path;
final input = await driver.waitFileParsed(absolute);
final input =
await driver.driver.fullyAnalyze(driver.uriFromPath(absolute));
if (input == null || !input.isAnalyzed) {
if (!input.isFullyAnalyzed) {
cli.exit('Unexpected error: The input file could not be analyzed');
}
final result = input.currentResult;
if (result is! ParsedDartFile) {
cli.exit('Input file is not a Dart file');
}
final result = input.fileAnalysis;
final db = result.declaredDatabases.single;
final writer = SchemaWriter(db, options: cli.project.moorOptions);