[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];
|
static String colorCode(TerminalColor color) => _colorMap[color];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get supportsColor => _platform.stdoutSupportsAnsi ?? false;
|
bool get supportsColor => _platform?.stdoutSupportsAnsi ?? false;
|
||||||
|
|
||||||
// Assume unicode emojis are supported when not on Windows.
|
// Assume unicode emojis are supported when not on Windows.
|
||||||
// If we are on Windows, unicode emojis are supported in Windows Terminal,
|
// If we are on Windows, unicode emojis are supported in Windows Terminal,
|
||||||
|
@ -30,6 +30,7 @@ class AnalyzeCommand extends FlutterCommand {
|
|||||||
_logger = logger,
|
_logger = logger,
|
||||||
_terminal = terminal,
|
_terminal = terminal,
|
||||||
_platform = platform {
|
_platform = platform {
|
||||||
|
addEnableExperimentation(verbose: verboseHelp);
|
||||||
argParser.addFlag('flutter-repo',
|
argParser.addFlag('flutter-repo',
|
||||||
negatable: false,
|
negatable: false,
|
||||||
help: 'Include all the examples and tests from the Flutter repository.',
|
help: 'Include all the examples and tests from the Flutter repository.',
|
||||||
@ -118,6 +119,7 @@ class AnalyzeCommand extends FlutterCommand {
|
|||||||
platform: _platform,
|
platform: _platform,
|
||||||
processManager: _processManager,
|
processManager: _processManager,
|
||||||
terminal: _terminal,
|
terminal: _terminal,
|
||||||
|
experiments: stringsArg('enable-experiment'),
|
||||||
).analyze();
|
).analyze();
|
||||||
} else {
|
} else {
|
||||||
await AnalyzeOnce(
|
await AnalyzeOnce(
|
||||||
@ -132,6 +134,7 @@ class AnalyzeCommand extends FlutterCommand {
|
|||||||
platform: _platform,
|
platform: _platform,
|
||||||
processManager: _processManager,
|
processManager: _processManager,
|
||||||
terminal: _terminal,
|
terminal: _terminal,
|
||||||
|
experiments: stringsArg('enable-experiment'),
|
||||||
).analyze();
|
).analyze();
|
||||||
}
|
}
|
||||||
return FlutterCommandResult.success();
|
return FlutterCommandResult.success();
|
||||||
|
@ -28,6 +28,7 @@ abstract class AnalyzeBase {
|
|||||||
@required this.platform,
|
@required this.platform,
|
||||||
@required this.processManager,
|
@required this.processManager,
|
||||||
@required this.terminal,
|
@required this.terminal,
|
||||||
|
@required this.experiments,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// The parsed argument results for execution.
|
/// The parsed argument results for execution.
|
||||||
@ -46,6 +47,8 @@ abstract class AnalyzeBase {
|
|||||||
final Platform platform;
|
final Platform platform;
|
||||||
@protected
|
@protected
|
||||||
final AnsiTerminal terminal;
|
final AnsiTerminal terminal;
|
||||||
|
@protected
|
||||||
|
final List<String> experiments;
|
||||||
|
|
||||||
/// Called by [AnalyzeCommand] to start the analysis process.
|
/// Called by [AnalyzeCommand] to start the analysis process.
|
||||||
Future<void> analyze();
|
Future<void> analyze();
|
||||||
|
@ -27,6 +27,7 @@ class AnalyzeContinuously extends AnalyzeBase {
|
|||||||
@required AnsiTerminal terminal,
|
@required AnsiTerminal terminal,
|
||||||
@required Platform platform,
|
@required Platform platform,
|
||||||
@required ProcessManager processManager,
|
@required ProcessManager processManager,
|
||||||
|
@required List<String> experiments,
|
||||||
}) : super(
|
}) : super(
|
||||||
argResults,
|
argResults,
|
||||||
repoPackages: repoPackages,
|
repoPackages: repoPackages,
|
||||||
@ -36,6 +37,7 @@ class AnalyzeContinuously extends AnalyzeBase {
|
|||||||
platform: platform,
|
platform: platform,
|
||||||
terminal: terminal,
|
terminal: terminal,
|
||||||
processManager: processManager,
|
processManager: processManager,
|
||||||
|
experiments: experiments,
|
||||||
);
|
);
|
||||||
|
|
||||||
String analysisTarget;
|
String analysisTarget;
|
||||||
@ -74,6 +76,7 @@ class AnalyzeContinuously extends AnalyzeBase {
|
|||||||
platform: platform,
|
platform: platform,
|
||||||
processManager: processManager,
|
processManager: processManager,
|
||||||
terminal: terminal,
|
terminal: terminal,
|
||||||
|
experiments: experiments,
|
||||||
);
|
);
|
||||||
server.onAnalyzing.listen((bool isAnalyzing) => _handleAnalysisStatus(server, isAnalyzing));
|
server.onAnalyzing.listen((bool isAnalyzing) => _handleAnalysisStatus(server, isAnalyzing));
|
||||||
server.onErrors.listen(_handleAnalysisErrors);
|
server.onErrors.listen(_handleAnalysisErrors);
|
||||||
|
@ -31,6 +31,7 @@ class AnalyzeOnce extends AnalyzeBase {
|
|||||||
@required Platform platform,
|
@required Platform platform,
|
||||||
@required ProcessManager processManager,
|
@required ProcessManager processManager,
|
||||||
@required AnsiTerminal terminal,
|
@required AnsiTerminal terminal,
|
||||||
|
@required List<String> experiments,
|
||||||
this.workingDirectory,
|
this.workingDirectory,
|
||||||
}) : super(
|
}) : super(
|
||||||
argResults,
|
argResults,
|
||||||
@ -41,6 +42,7 @@ class AnalyzeOnce extends AnalyzeBase {
|
|||||||
platform: platform,
|
platform: platform,
|
||||||
processManager: processManager,
|
processManager: processManager,
|
||||||
terminal: terminal,
|
terminal: terminal,
|
||||||
|
experiments: experiments,
|
||||||
);
|
);
|
||||||
|
|
||||||
/// The working directory for testing analysis using dartanalyzer.
|
/// The working directory for testing analysis using dartanalyzer.
|
||||||
@ -98,6 +100,7 @@ class AnalyzeOnce extends AnalyzeBase {
|
|||||||
logger: logger,
|
logger: logger,
|
||||||
processManager: processManager,
|
processManager: processManager,
|
||||||
terminal: terminal,
|
terminal: terminal,
|
||||||
|
experiments: experiments,
|
||||||
);
|
);
|
||||||
|
|
||||||
StreamSubscription<bool> subscription;
|
StreamSubscription<bool> subscription;
|
||||||
|
@ -24,12 +24,14 @@ class AnalysisServer {
|
|||||||
@required ProcessManager processManager,
|
@required ProcessManager processManager,
|
||||||
@required Logger logger,
|
@required Logger logger,
|
||||||
@required Platform platform,
|
@required Platform platform,
|
||||||
@required AnsiTerminal terminal,
|
@required Terminal terminal,
|
||||||
|
@required List<String> experiments,
|
||||||
}) : _fileSystem = fileSystem,
|
}) : _fileSystem = fileSystem,
|
||||||
_processManager = processManager,
|
_processManager = processManager,
|
||||||
_logger = logger,
|
_logger = logger,
|
||||||
_platform = platform,
|
_platform = platform,
|
||||||
_terminal = terminal;
|
_terminal = terminal,
|
||||||
|
_experiments = experiments;
|
||||||
|
|
||||||
final String sdkPath;
|
final String sdkPath;
|
||||||
final List<String> directories;
|
final List<String> directories;
|
||||||
@ -37,7 +39,8 @@ class AnalysisServer {
|
|||||||
final ProcessManager _processManager;
|
final ProcessManager _processManager;
|
||||||
final Logger _logger;
|
final Logger _logger;
|
||||||
final Platform _platform;
|
final Platform _platform;
|
||||||
final AnsiTerminal _terminal;
|
final Terminal _terminal;
|
||||||
|
final List<String> _experiments;
|
||||||
|
|
||||||
Process _process;
|
Process _process;
|
||||||
final StreamController<bool> _analyzingController =
|
final StreamController<bool> _analyzingController =
|
||||||
@ -49,11 +52,20 @@ class AnalysisServer {
|
|||||||
int _id = 0;
|
int _id = 0;
|
||||||
|
|
||||||
Future<void> start() async {
|
Future<void> start() async {
|
||||||
final String snapshot =
|
final String snapshot = _fileSystem.path.join(
|
||||||
_fileSystem.path.join(sdkPath, 'bin/snapshots/analysis_server.dart.snapshot');
|
sdkPath,
|
||||||
|
'bin',
|
||||||
|
'snapshots',
|
||||||
|
'analysis_server.dart.snapshot',
|
||||||
|
);
|
||||||
final List<String> command = <String>[
|
final List<String> command = <String>[
|
||||||
_fileSystem.path.join(sdkPath, 'bin', 'dart'),
|
_fileSystem.path.join(sdkPath, 'bin', 'dart'),
|
||||||
snapshot,
|
snapshot,
|
||||||
|
for (String experiment in _experiments)
|
||||||
|
...<String>[
|
||||||
|
'--enable-experiment',
|
||||||
|
experiment,
|
||||||
|
],
|
||||||
'--disable-server-feature-completion',
|
'--disable-server-feature-completion',
|
||||||
'--disable-server-feature-search',
|
'--disable-server-feature-search',
|
||||||
'--sdk',
|
'--sdk',
|
||||||
@ -181,14 +193,14 @@ enum _AnalysisSeverity {
|
|||||||
class AnalysisError implements Comparable<AnalysisError> {
|
class AnalysisError implements Comparable<AnalysisError> {
|
||||||
AnalysisError(this.json, {
|
AnalysisError(this.json, {
|
||||||
@required Platform platform,
|
@required Platform platform,
|
||||||
@required AnsiTerminal terminal,
|
@required Terminal terminal,
|
||||||
@required FileSystem fileSystem,
|
@required FileSystem fileSystem,
|
||||||
}) : _platform = platform,
|
}) : _platform = platform,
|
||||||
_terminal = terminal,
|
_terminal = terminal,
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
|
|
||||||
final Platform _platform;
|
final Platform _platform;
|
||||||
final AnsiTerminal _terminal;
|
final Terminal _terminal;
|
||||||
final FileSystem _fileSystem;
|
final FileSystem _fileSystem;
|
||||||
|
|
||||||
static final Map<String, _AnalysisSeverity> _severityMap = <String, _AnalysisSeverity>{
|
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) {
|
set defaultBuildMode(BuildMode value) {
|
||||||
_defaultBuildMode = value;
|
_defaultBuildMode = value;
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:file/memory.dart';
|
||||||
import 'package:flutter_tools/src/base/file_system.dart';
|
import 'package:flutter_tools/src/base/file_system.dart';
|
||||||
import 'package:flutter_tools/src/base/io.dart';
|
import 'package:flutter_tools/src/base/io.dart';
|
||||||
import 'package:flutter_tools/src/base/logger.dart';
|
import 'package:flutter_tools/src/base/logger.dart';
|
||||||
@ -72,6 +73,7 @@ void main() {
|
|||||||
processManager: processManager,
|
processManager: processManager,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
terminal: terminal,
|
terminal: terminal,
|
||||||
|
experiments: <String>[],
|
||||||
);
|
);
|
||||||
|
|
||||||
int errorCount = 0;
|
int errorCount = 0;
|
||||||
@ -96,6 +98,7 @@ void main() {
|
|||||||
processManager: processManager,
|
processManager: processManager,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
terminal: terminal,
|
terminal: terminal,
|
||||||
|
experiments: <String>[],
|
||||||
);
|
);
|
||||||
|
|
||||||
int errorCount = 0;
|
int errorCount = 0;
|
||||||
@ -119,6 +122,7 @@ void main() {
|
|||||||
processManager: processManager,
|
processManager: processManager,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
terminal: terminal,
|
terminal: terminal,
|
||||||
|
experiments: <String>[],
|
||||||
);
|
);
|
||||||
|
|
||||||
int errorCount = 0;
|
int errorCount = 0;
|
||||||
@ -130,4 +134,39 @@ void main() {
|
|||||||
await onDone;
|
await onDone;
|
||||||
expect(errorCount, 0);
|
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';
|
const String _kFlutterRoot = '/data/flutter';
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
FileSystem fs;
|
testWithoutContext('analyze inRepo', () {
|
||||||
Directory tempDir;
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
||||||
|
fileSystem.directory(_kFlutterRoot).createSync(recursive: true);
|
||||||
setUp(() {
|
final Directory tempDir = fileSystem.systemTempDirectory
|
||||||
fs = MemoryFileSystem();
|
.createTempSync('flutter_analysis_test.');
|
||||||
fs.directory(_kFlutterRoot).createSync(recursive: true);
|
|
||||||
Cache.flutterRoot = _kFlutterRoot;
|
Cache.flutterRoot = _kFlutterRoot;
|
||||||
tempDir = fs.systemTempDirectory.createTempSync('flutter_analysis_test.');
|
|
||||||
});
|
|
||||||
|
|
||||||
tearDown(() {
|
// Absolute paths
|
||||||
tryToDelete(tempDir);
|
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', () {
|
// Relative paths
|
||||||
testWithoutContext('inRepo', () {
|
fileSystem.currentDirectory = Cache.flutterRoot;
|
||||||
bool inRepo(List<String> fileList) {
|
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);
|
||||||
|
|
||||||
|
// Ensure no exceptions
|
||||||
|
inRepo(null, fileSystem);
|
||||||
|
inRepo(<String>[], fileSystem);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool inRepo(List<String> fileList, FileSystem fileSystem) {
|
||||||
if (fileList == null || fileList.isEmpty) {
|
if (fileList == null || fileList.isEmpty) {
|
||||||
fileList = <String>[fs.path.current];
|
fileList = <String>[fileSystem.path.current];
|
||||||
}
|
}
|
||||||
final String root = fs.path.normalize(fs.path.absolute(Cache.flutterRoot));
|
final String root = fileSystem.path.normalize(fileSystem.path.absolute(Cache.flutterRoot));
|
||||||
final String prefix = root + fs.path.separator;
|
final String prefix = root + fileSystem.path.separator;
|
||||||
for (String file in fileList) {
|
for (String file in fileList) {
|
||||||
file = fs.path.normalize(fs.path.absolute(file));
|
file = fileSystem.path.normalize(fileSystem.path.absolute(file));
|
||||||
if (file == root || file.startsWith(prefix)) {
|
if (file == root || file.startsWith(prefix)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
// 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>[]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
testUsingContext('analyze once returns no issues for todo comments', () async {
|
||||||
const String contents = '''
|
const String contents = '''
|
||||||
// TODO(foobar):
|
// TODO(foobar):
|
||||||
|
@ -28,6 +28,7 @@ class FakeCommand {
|
|||||||
this.stdout = '',
|
this.stdout = '',
|
||||||
this.stderr = '',
|
this.stderr = '',
|
||||||
this.completer,
|
this.completer,
|
||||||
|
this.stdin,
|
||||||
}) : assert(command != null),
|
}) : assert(command != null),
|
||||||
assert(duration != null),
|
assert(duration != null),
|
||||||
assert(exitCode != null);
|
assert(exitCode != null);
|
||||||
@ -82,6 +83,10 @@ class FakeCommand {
|
|||||||
/// resolves.
|
/// resolves.
|
||||||
final Completer<void> completer;
|
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) {
|
void _matches(List<String> command, String workingDirectory, Map<String, String> environment) {
|
||||||
expect(command, equals(this.command));
|
expect(command, equals(this.command));
|
||||||
if (this.workingDirectory != null) {
|
if (this.workingDirectory != null) {
|
||||||
@ -198,7 +203,7 @@ abstract class FakeProcessManager implements ProcessManager {
|
|||||||
fakeCommand.onRun,
|
fakeCommand.onRun,
|
||||||
_pid,
|
_pid,
|
||||||
fakeCommand.stderr,
|
fakeCommand.stderr,
|
||||||
null, // stdin
|
fakeCommand.stdin,
|
||||||
fakeCommand.stdout,
|
fakeCommand.stdout,
|
||||||
fakeCommand.completer,
|
fakeCommand.completer,
|
||||||
);
|
);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user