[flutter_tools] support enable-experiment in flutter analyze (#54613)
This commit is contained in:
parent
9cb9bfbd7c
commit
ee43de0476
@ -168,7 +168,7 @@ class AnsiTerminal implements Terminal {
|
||||
static String colorCode(TerminalColor color) => _colorMap[color];
|
||||
|
||||
@override
|
||||
bool get supportsColor => _platform.stdoutSupportsAnsi ?? false;
|
||||
bool get supportsColor => _platform?.stdoutSupportsAnsi ?? false;
|
||||
|
||||
// Assume unicode emojis are supported when not on Windows.
|
||||
// If we are on Windows, unicode emojis are supported in Windows Terminal,
|
||||
|
@ -30,6 +30,7 @@ class AnalyzeCommand extends FlutterCommand {
|
||||
_logger = logger,
|
||||
_terminal = terminal,
|
||||
_platform = platform {
|
||||
addEnableExperimentation(verbose: verboseHelp);
|
||||
argParser.addFlag('flutter-repo',
|
||||
negatable: false,
|
||||
help: 'Include all the examples and tests from the Flutter repository.',
|
||||
@ -118,6 +119,7 @@ class AnalyzeCommand extends FlutterCommand {
|
||||
platform: _platform,
|
||||
processManager: _processManager,
|
||||
terminal: _terminal,
|
||||
experiments: stringsArg('enable-experiment'),
|
||||
).analyze();
|
||||
} else {
|
||||
await AnalyzeOnce(
|
||||
@ -132,6 +134,7 @@ class AnalyzeCommand extends FlutterCommand {
|
||||
platform: _platform,
|
||||
processManager: _processManager,
|
||||
terminal: _terminal,
|
||||
experiments: stringsArg('enable-experiment'),
|
||||
).analyze();
|
||||
}
|
||||
return FlutterCommandResult.success();
|
||||
|
@ -28,6 +28,7 @@ abstract class AnalyzeBase {
|
||||
@required this.platform,
|
||||
@required this.processManager,
|
||||
@required this.terminal,
|
||||
@required this.experiments,
|
||||
});
|
||||
|
||||
/// The parsed argument results for execution.
|
||||
@ -46,6 +47,8 @@ abstract class AnalyzeBase {
|
||||
final Platform platform;
|
||||
@protected
|
||||
final AnsiTerminal terminal;
|
||||
@protected
|
||||
final List<String> experiments;
|
||||
|
||||
/// Called by [AnalyzeCommand] to start the analysis process.
|
||||
Future<void> analyze();
|
||||
|
@ -27,6 +27,7 @@ class AnalyzeContinuously extends AnalyzeBase {
|
||||
@required AnsiTerminal terminal,
|
||||
@required Platform platform,
|
||||
@required ProcessManager processManager,
|
||||
@required List<String> experiments,
|
||||
}) : super(
|
||||
argResults,
|
||||
repoPackages: repoPackages,
|
||||
@ -36,6 +37,7 @@ class AnalyzeContinuously extends AnalyzeBase {
|
||||
platform: platform,
|
||||
terminal: terminal,
|
||||
processManager: processManager,
|
||||
experiments: experiments,
|
||||
);
|
||||
|
||||
String analysisTarget;
|
||||
@ -74,6 +76,7 @@ class AnalyzeContinuously extends AnalyzeBase {
|
||||
platform: platform,
|
||||
processManager: processManager,
|
||||
terminal: terminal,
|
||||
experiments: experiments,
|
||||
);
|
||||
server.onAnalyzing.listen((bool isAnalyzing) => _handleAnalysisStatus(server, isAnalyzing));
|
||||
server.onErrors.listen(_handleAnalysisErrors);
|
||||
|
@ -31,6 +31,7 @@ class AnalyzeOnce extends AnalyzeBase {
|
||||
@required Platform platform,
|
||||
@required ProcessManager processManager,
|
||||
@required AnsiTerminal terminal,
|
||||
@required List<String> experiments,
|
||||
this.workingDirectory,
|
||||
}) : super(
|
||||
argResults,
|
||||
@ -41,6 +42,7 @@ class AnalyzeOnce extends AnalyzeBase {
|
||||
platform: platform,
|
||||
processManager: processManager,
|
||||
terminal: terminal,
|
||||
experiments: experiments,
|
||||
);
|
||||
|
||||
/// The working directory for testing analysis using dartanalyzer.
|
||||
@ -98,6 +100,7 @@ class AnalyzeOnce extends AnalyzeBase {
|
||||
logger: logger,
|
||||
processManager: processManager,
|
||||
terminal: terminal,
|
||||
experiments: experiments,
|
||||
);
|
||||
|
||||
StreamSubscription<bool> subscription;
|
||||
|
@ -24,12 +24,14 @@ class AnalysisServer {
|
||||
@required ProcessManager processManager,
|
||||
@required Logger logger,
|
||||
@required Platform platform,
|
||||
@required AnsiTerminal terminal,
|
||||
@required Terminal terminal,
|
||||
@required List<String> experiments,
|
||||
}) : _fileSystem = fileSystem,
|
||||
_processManager = processManager,
|
||||
_logger = logger,
|
||||
_platform = platform,
|
||||
_terminal = terminal;
|
||||
_terminal = terminal,
|
||||
_experiments = experiments;
|
||||
|
||||
final String sdkPath;
|
||||
final List<String> directories;
|
||||
@ -37,7 +39,8 @@ class AnalysisServer {
|
||||
final ProcessManager _processManager;
|
||||
final Logger _logger;
|
||||
final Platform _platform;
|
||||
final AnsiTerminal _terminal;
|
||||
final Terminal _terminal;
|
||||
final List<String> _experiments;
|
||||
|
||||
Process _process;
|
||||
final StreamController<bool> _analyzingController =
|
||||
@ -49,11 +52,20 @@ class AnalysisServer {
|
||||
int _id = 0;
|
||||
|
||||
Future<void> start() async {
|
||||
final String snapshot =
|
||||
_fileSystem.path.join(sdkPath, 'bin/snapshots/analysis_server.dart.snapshot');
|
||||
final String snapshot = _fileSystem.path.join(
|
||||
sdkPath,
|
||||
'bin',
|
||||
'snapshots',
|
||||
'analysis_server.dart.snapshot',
|
||||
);
|
||||
final List<String> command = <String>[
|
||||
_fileSystem.path.join(sdkPath, 'bin', 'dart'),
|
||||
snapshot,
|
||||
for (String experiment in _experiments)
|
||||
...<String>[
|
||||
'--enable-experiment',
|
||||
experiment,
|
||||
],
|
||||
'--disable-server-feature-completion',
|
||||
'--disable-server-feature-search',
|
||||
'--sdk',
|
||||
@ -181,14 +193,14 @@ enum _AnalysisSeverity {
|
||||
class AnalysisError implements Comparable<AnalysisError> {
|
||||
AnalysisError(this.json, {
|
||||
@required Platform platform,
|
||||
@required AnsiTerminal terminal,
|
||||
@required Terminal terminal,
|
||||
@required FileSystem fileSystem,
|
||||
}) : _platform = platform,
|
||||
_terminal = terminal,
|
||||
_fileSystem = fileSystem;
|
||||
|
||||
final Platform _platform;
|
||||
final AnsiTerminal _terminal;
|
||||
final Terminal _terminal;
|
||||
final FileSystem _fileSystem;
|
||||
|
||||
static final Map<String, _AnalysisSeverity> _severityMap = <String, _AnalysisSeverity>{
|
||||
|
@ -462,6 +462,17 @@ abstract class FlutterCommand extends Command<void> {
|
||||
);
|
||||
}
|
||||
|
||||
void addEnableExperimentation({ bool verbose }) {
|
||||
argParser.addMultiOption(
|
||||
FlutterOptions.kEnableExperiment,
|
||||
help:
|
||||
'The name of an experimental Dart feature to enable. For more info '
|
||||
'see: https://github.com/dart-lang/sdk/blob/master/docs/process/'
|
||||
'experimental-flags.md',
|
||||
hide: !verbose,
|
||||
);
|
||||
}
|
||||
|
||||
set defaultBuildMode(BuildMode value) {
|
||||
_defaultBuildMode = value;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/io.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
@ -72,6 +73,7 @@ void main() {
|
||||
processManager: processManager,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
experiments: <String>[],
|
||||
);
|
||||
|
||||
int errorCount = 0;
|
||||
@ -96,6 +98,7 @@ void main() {
|
||||
processManager: processManager,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
experiments: <String>[],
|
||||
);
|
||||
|
||||
int errorCount = 0;
|
||||
@ -119,6 +122,7 @@ void main() {
|
||||
processManager: processManager,
|
||||
logger: logger,
|
||||
terminal: terminal,
|
||||
experiments: <String>[],
|
||||
);
|
||||
|
||||
int errorCount = 0;
|
||||
@ -130,4 +134,39 @@ void main() {
|
||||
await onDone;
|
||||
expect(errorCount, 0);
|
||||
});
|
||||
|
||||
testWithoutContext('Can forward null-safety experiments to the AnalysisServer', () async {
|
||||
final Completer<void> completer = Completer<void>();
|
||||
final StreamController<List<int>> stdin = StreamController<List<int>>();
|
||||
const String fakeSdkPath = 'dart-sdk';
|
||||
final FakeCommand fakeCommand = FakeCommand(
|
||||
command: const <String>[
|
||||
'dart-sdk/bin/dart',
|
||||
'dart-sdk/bin/snapshots/analysis_server.dart.snapshot',
|
||||
'--enable-experiment',
|
||||
'non-nullable',
|
||||
'--disable-server-feature-completion',
|
||||
'--disable-server-feature-search',
|
||||
'--sdk',
|
||||
'dart-sdk',
|
||||
],
|
||||
completer: completer,
|
||||
stdin: IOSink(stdin.sink),
|
||||
);
|
||||
|
||||
server = AnalysisServer(fakeSdkPath, <String>[''],
|
||||
fileSystem: MemoryFileSystem.test(),
|
||||
platform: FakePlatform(),
|
||||
processManager: FakeProcessManager.list(<FakeCommand>[
|
||||
fakeCommand,
|
||||
]),
|
||||
logger: BufferLogger.test(),
|
||||
terminal: Terminal.test(),
|
||||
experiments: <String>[
|
||||
'non-nullable'
|
||||
],
|
||||
);
|
||||
|
||||
await server.start();
|
||||
});
|
||||
}
|
||||
|
@ -11,54 +11,44 @@ import '../../src/common.dart';
|
||||
const String _kFlutterRoot = '/data/flutter';
|
||||
|
||||
void main() {
|
||||
FileSystem fs;
|
||||
Directory tempDir;
|
||||
|
||||
setUp(() {
|
||||
fs = MemoryFileSystem();
|
||||
fs.directory(_kFlutterRoot).createSync(recursive: true);
|
||||
testWithoutContext('analyze inRepo', () {
|
||||
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||
fileSystem.directory(_kFlutterRoot).createSync(recursive: true);
|
||||
final Directory tempDir = fileSystem.systemTempDirectory
|
||||
.createTempSync('flutter_analysis_test.');
|
||||
Cache.flutterRoot = _kFlutterRoot;
|
||||
tempDir = fs.systemTempDirectory.createTempSync('flutter_analysis_test.');
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
tryToDelete(tempDir);
|
||||
});
|
||||
// Absolute paths
|
||||
expect(inRepo(<String>[tempDir.path], fileSystem), isFalse);
|
||||
expect(inRepo(<String>[fileSystem.path.join(tempDir.path, 'foo')], fileSystem), isFalse);
|
||||
expect(inRepo(<String>[Cache.flutterRoot], fileSystem), isTrue);
|
||||
expect(inRepo(<String>[fileSystem.path.join(Cache.flutterRoot, 'foo')], fileSystem), isTrue);
|
||||
|
||||
group('analyze', () {
|
||||
testWithoutContext('inRepo', () {
|
||||
bool inRepo(List<String> fileList) {
|
||||
if (fileList == null || fileList.isEmpty) {
|
||||
fileList = <String>[fs.path.current];
|
||||
}
|
||||
final String root = fs.path.normalize(fs.path.absolute(Cache.flutterRoot));
|
||||
final String prefix = root + fs.path.separator;
|
||||
for (String file in fileList) {
|
||||
file = fs.path.normalize(fs.path.absolute(file));
|
||||
if (file == root || file.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// Relative paths
|
||||
fileSystem.currentDirectory = Cache.flutterRoot;
|
||||
expect(inRepo(<String>['.'], fileSystem), isTrue);
|
||||
expect(inRepo(<String>['foo'], fileSystem), isTrue);
|
||||
fileSystem.currentDirectory = tempDir.path;
|
||||
expect(inRepo(<String>['.'], fileSystem), isFalse);
|
||||
expect(inRepo(<String>['foo'], fileSystem), isFalse);
|
||||
|
||||
// Absolute paths
|
||||
expect(inRepo(<String>[tempDir.path]), isFalse);
|
||||
expect(inRepo(<String>[fs.path.join(tempDir.path, 'foo')]), isFalse);
|
||||
expect(inRepo(<String>[Cache.flutterRoot]), isTrue);
|
||||
expect(inRepo(<String>[fs.path.join(Cache.flutterRoot, 'foo')]), isTrue);
|
||||
|
||||
// Relative paths
|
||||
fs.currentDirectory = Cache.flutterRoot;
|
||||
expect(inRepo(<String>['.']), isTrue);
|
||||
expect(inRepo(<String>['foo']), isTrue);
|
||||
fs.currentDirectory = tempDir.path;
|
||||
expect(inRepo(<String>['.']), isFalse);
|
||||
expect(inRepo(<String>['foo']), isFalse);
|
||||
|
||||
// Ensure no exceptions
|
||||
inRepo(null);
|
||||
inRepo(<String>[]);
|
||||
});
|
||||
// Ensure no exceptions
|
||||
inRepo(null, fileSystem);
|
||||
inRepo(<String>[], fileSystem);
|
||||
});
|
||||
}
|
||||
|
||||
bool inRepo(List<String> fileList, FileSystem fileSystem) {
|
||||
if (fileList == null || fileList.isEmpty) {
|
||||
fileList = <String>[fileSystem.path.current];
|
||||
}
|
||||
final String root = fileSystem.path.normalize(fileSystem.path.absolute(Cache.flutterRoot));
|
||||
final String prefix = root + fileSystem.path.separator;
|
||||
for (String file in fileList) {
|
||||
file = fileSystem.path.normalize(fileSystem.path.absolute(file));
|
||||
if (file == root || file.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -304,6 +304,32 @@ StringBuffer bar = StringBuffer('baz');
|
||||
}
|
||||
});
|
||||
|
||||
testUsingContext('analyze once supports analyzing null-safe code', () async {
|
||||
const String contents = '''
|
||||
int? bar;
|
||||
''';
|
||||
final Directory tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_analyze_once_test_null_safety.');
|
||||
_createDotPackages(tempDir.path);
|
||||
|
||||
tempDir.childFile('main.dart').writeAsStringSync(contents);
|
||||
try {
|
||||
await runCommand(
|
||||
command: AnalyzeCommand(
|
||||
workingDirectory: fileSystem.directory(tempDir),
|
||||
platform: _kNoColorTerminalPlatform,
|
||||
fileSystem: fileSystem,
|
||||
logger: logger,
|
||||
processManager: processManager,
|
||||
terminal: terminal,
|
||||
),
|
||||
arguments: <String>['analyze', '--no-pub', '--enable-experiment=non-nullable'],
|
||||
statusTextContains: <String>['No issues found!'],
|
||||
);
|
||||
} finally {
|
||||
tryToDelete(tempDir);
|
||||
}
|
||||
});
|
||||
|
||||
testUsingContext('analyze once returns no issues for todo comments', () async {
|
||||
const String contents = '''
|
||||
// TODO(foobar):
|
||||
|
@ -28,6 +28,7 @@ class FakeCommand {
|
||||
this.stdout = '',
|
||||
this.stderr = '',
|
||||
this.completer,
|
||||
this.stdin,
|
||||
}) : assert(command != null),
|
||||
assert(duration != null),
|
||||
assert(exitCode != null);
|
||||
@ -82,6 +83,10 @@ class FakeCommand {
|
||||
/// resolves.
|
||||
final Completer<void> completer;
|
||||
|
||||
/// An optional stdin sink that will be exposed through the resulting
|
||||
/// [FakeProcess].
|
||||
final IOSink stdin;
|
||||
|
||||
void _matches(List<String> command, String workingDirectory, Map<String, String> environment) {
|
||||
expect(command, equals(this.command));
|
||||
if (this.workingDirectory != null) {
|
||||
@ -198,7 +203,7 @@ abstract class FakeProcessManager implements ProcessManager {
|
||||
fakeCommand.onRun,
|
||||
_pid,
|
||||
fakeCommand.stderr,
|
||||
null, // stdin
|
||||
fakeCommand.stdin,
|
||||
fakeCommand.stdout,
|
||||
fakeCommand.completer,
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user