diff --git a/CHANGELOG.md b/CHANGELOG.md index 069fea5e1d6..2d3c48fdd69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,9 @@ https://github.com/dart-lang/sdk/issues/46100 'recommended' set of lints from `package:lints`. See https://dart.dev/go/core-lints for more information about these lints. +* The `dart analyze` command has been extended to support specifying multiple + files or directories to analyze; see also https://github.com/dart-lang/sdk/issues/45352. + #### Linter Updated the Linter to `1.5.0`, which includes: diff --git a/pkg/dartdev/lib/src/analysis_server.dart b/pkg/dartdev/lib/src/analysis_server.dart index d8fd2b237a7..0e0f59c2801 100644 --- a/pkg/dartdev/lib/src/analysis_server.dart +++ b/pkg/dartdev/lib/src/analysis_server.dart @@ -20,12 +20,12 @@ import 'utils.dart'; class AnalysisServer { AnalysisServer( this.sdkPath, - this.analysisRoot, { + this.analysisRoots, { @required this.commandName, }); final Directory sdkPath; - final FileSystemEntity analysisRoot; + final List analysisRoots; final String commandName; Process _process; @@ -107,10 +107,10 @@ class AnalysisServer { // // The call to absolute.resolveSymbolicLinksSync() canonicalizes the path to // be passed to the analysis server. - var analysisRootPath = trimEnd( - analysisRoot.absolute.resolveSymbolicLinksSync(), - path.context.separator, - ); + List analysisRootPaths = analysisRoots.map((root) { + return trimEnd( + root.absolute.resolveSymbolicLinksSync(), path.context.separator); + }).toList(); onAnalyzing.listen((bool isAnalyzing) { if (isAnalyzing && _analysisFinished.isCompleted) { @@ -124,7 +124,7 @@ class AnalysisServer { // ignore: unawaited_futures _sendCommand('analysis.setAnalysisRoots', params: { - 'included': [analysisRootPath], + 'included': analysisRootPaths, 'excluded': [] }); } diff --git a/pkg/dartdev/lib/src/commands/analyze.dart b/pkg/dartdev/lib/src/commands/analyze.dart index ba5cf2c032f..254a78035d5 100644 --- a/pkg/dartdev/lib/src/commands/analyze.dart +++ b/pkg/dartdev/lib/src/commands/analyze.dart @@ -69,26 +69,19 @@ class AnalyzeCommand extends DartdevCommand { @override FutureOr run() async { - if (argResults.rest.length > 1) { - usageException('Only one directory or file is expected.'); - } - - // find target from argResults.rest - io.FileSystemEntity target; - io.Directory relativeToDir; + // Find targets from the 'rest' params. + final List targets = []; if (argResults.rest.isEmpty) { - target = io.Directory.current; - relativeToDir = target; + targets.add(io.Directory.current); } else { - var targetPath = argResults.rest.single; - if (io.Directory(targetPath).existsSync()) { - target = io.Directory(targetPath); - relativeToDir = target; - } else if (io.File(targetPath).existsSync()) { - target = io.File(targetPath); - relativeToDir = target.parent; - } else { - usageException("Directory or file doesn't exist: $targetPath"); + for (String targetPath in argResults.rest) { + if (io.Directory(targetPath).existsSync()) { + targets.add(io.Directory(targetPath)); + } else if (io.File(targetPath).existsSync()) { + targets.add(io.File(targetPath)); + } else { + usageException("Directory or file doesn't exist: $targetPath"); + } } } @@ -97,13 +90,15 @@ class AnalyzeCommand extends DartdevCommand { final machineFormat = argResults['format'] == 'machine'; final jsonFormat = argResults['format'] == 'json'; - var progress = machineFormat - ? null - : log.progress('Analyzing ${path.basename(target.path)}'); + final targetsNames = + targets.map((entity) => path.basename(entity.path)).join(', '); + + var progress = + machineFormat ? null : log.progress('Analyzing $targetsNames'); final AnalysisServer server = AnalysisServer( io.Directory(sdk.sdkPath), - target, + targets, commandName: 'analyze', ); @@ -145,8 +140,14 @@ class AnalyzeCommand extends DartdevCommand { } else if (jsonFormat) { emitJsonFormat(log, errors); } else { - emitDefaultFormat(log, errors, - relativeToDir: relativeToDir, verbose: verbose); + var relativeTo = targets.length == 1 ? targets.single : null; + + emitDefaultFormat( + log, + errors, + relativeToDir: relativeTo is io.File ? relativeTo.parent : relativeTo, + verbose: verbose, + ); } bool hasErrors = false; diff --git a/pkg/dartdev/lib/src/commands/fix.dart b/pkg/dartdev/lib/src/commands/fix.dart index dcbffccaf47..c053a29282e 100644 --- a/pkg/dartdev/lib/src/commands/fix.dart +++ b/pkg/dartdev/lib/src/commands/fix.dart @@ -91,7 +91,7 @@ To use the tool, run either ['dart fix --dry-run'] for a preview of the proposed var server = AnalysisServer( io.Directory(sdk.sdkPath), - dir, + [dir], commandName: 'fix', ); diff --git a/pkg/dartdev/test/analysis_server_test.dart b/pkg/dartdev/test/analysis_server_test.dart index 0e76e99fb5f..dba2cccb4c8 100644 --- a/pkg/dartdev/test/analysis_server_test.dart +++ b/pkg/dartdev/test/analysis_server_test.dart @@ -24,14 +24,14 @@ void main() { tearDown(() => p?.dispose()); test('can start', () async { - AnalysisServer server = AnalysisServer(io.Directory(sdk.sdkPath), p.dir, + AnalysisServer server = AnalysisServer(io.Directory(sdk.sdkPath), [p.dir], commandName: 'testing'); await server.start(); await server.shutdown(); }); test('can send message', () async { - AnalysisServer server = AnalysisServer(io.Directory(sdk.sdkPath), p.dir, + AnalysisServer server = AnalysisServer(io.Directory(sdk.sdkPath), [p.dir], commandName: 'testing'); await server.start(); diff --git a/pkg/dartdev/test/commands/analyze_test.dart b/pkg/dartdev/test/commands/analyze_test.dart index d098f5377a4..293c13903af 100644 --- a/pkg/dartdev/test/commands/analyze_test.dart +++ b/pkg/dartdev/test/commands/analyze_test.dart @@ -85,14 +85,36 @@ void defineAnalyze() { expect(result.stdout, contains(_analyzeVerboseUsageText)); }); - test('multiple directories', () { - p = project(); - var result = p.runSync(['analyze', '/no/such/dir1/', '/no/such/dir2/']); + group('multiple items', () { + TestProject secondProject; - expect(result.exitCode, 64); - expect(result.stdout, isEmpty); - expect(result.stderr, contains('Only one directory or file is expected.')); - expect(result.stderr, contains(_analyzeUsageText)); + tearDown(() => secondProject?.dispose()); + + test('folder and file', () { + p = project(mainSrc: "int get foo => 'str';\n"); + secondProject = project(mainSrc: "int get foo => 'str';\n"); + var result = p.runSync(['analyze', p.dirPath, secondProject.mainPath]); + + expect(result.exitCode, 3); + expect(result.stderr, isEmpty); + expect(result.stdout, contains('A value of type ')); + expect(result.stdout, contains('lib/main.dart:1:16 ')); + expect(result.stdout, contains('return_of_invalid_type')); + expect(result.stdout, contains('2 issues found.')); + }); + + test('two folders', () { + p = project(mainSrc: "int get foo => 'str';\n"); + secondProject = project(mainSrc: "int get foo => 'str';\n"); + var result = p.runSync(['analyze', p.dirPath, secondProject.dirPath]); + + expect(result.exitCode, 3); + expect(result.stderr, isEmpty); + expect(result.stdout, contains('A value of type ')); + expect(result.stdout, contains('main.dart:1:16 ')); + expect(result.stdout, contains('return_of_invalid_type')); + expect(result.stdout, contains('2 issues found.')); + }); }); test('no such directory', () {