flutter analyze command
Other changes in this patch:
- Make the 'flutter' tool say "Updating flutter tool..." when it calls
pub get, to avoid confusion about what the pub get output is about.
- Make the bash flutter tool call pub get when the revision has
changed. (This was already happening on Windows.)
- Fix a raft of bugs found by the analyzer.
- Fix some style nits in various bits of code that happened to be near
things the analyzer noticed.
- Remove the logic in "flutter test" that would run "pub get", since
upon further reflexion it was determined it didn't work anyway.
We'll probably have to add better diagnostics here and say to run the
updater script.
- Remove the native velocity tracker script, since it was testing code
that has since been removed.
Notes on ignored warnings:
- We ignore warnings in any packages that are not in the Flutter repo or
in the author's current directory.
- We ignore various irrelevant Strong Mode warnings. We still enable
strong mode because even though it's not really relevant to our needs,
it does (more or less accidentally) catch a few things that are
helpful to us.
- We allow CONSTANTS_LIKE_THIS, since we get some of those from other
platforms that we are copying for sanity and consistency.
- We allow one-member abstract classes since we have a number of them
where it's perfectly reasonable.
- We unfortunately still ignore warnings in mojom.dart autogenerated
files. We should really fix those but that's a separate patch.
- We verify the actual source file when we see the 'Name non-constant
identifiers using lowerCamelCase.' lint, to allow one-letter variables
that use capital letters (e.g. for physics expressions) and to allow
multiple-underscore variable names.
- We ignore all errors on lines that contain the following magic
incantation and a "#" character:
// analyzer doesn't like constructor tear-offs
- For all remaining errors, if the line contains a comment of the form
// analyzer says "..."
...then we ignore any errors that have that "..." string in them.
diff --git a/bin/flutter b/bin/flutter
index 9d96ad3..f879c22 100755
--- a/bin/flutter
+++ b/bin/flutter
@@ -12,16 +12,10 @@
# TODO(abarth): We shouldn't require dart to be on the user's path.
DART=dart
-if [ "$FLUTTER_TOOLS_DIR/pubspec.yaml" -nt "$FLUTTER_TOOLS_DIR/pubspec.lock" ]; then
- (cd "$FLUTTER_TOOLS_DIR"; pub get)
- if [ -f "$SNAPSHOT_PATH" ]; then
- rm "$SNAPSHOT_PATH"
- fi
-fi
-
REVISION=`(cd "$FLUTTER_ROOT"; git rev-parse HEAD)`
-
-if [ ! -f "$SNAPSHOT_PATH" ] || [ ! -f "$STAMP_PATH" ] || [ `cat "$STAMP_PATH"` != $REVISION ]; then
+if [ ! -f "$SNAPSHOT_PATH" ] || [ ! -f "$STAMP_PATH" ] || [ `cat "$STAMP_PATH"` != $REVISION ] || [ "$FLUTTER_TOOLS_DIR/pubspec.yaml" -nt "$FLUTTER_TOOLS_DIR/pubspec.lock" ]; then
+ echo Updating flutter tool...
+ (cd "$FLUTTER_TOOLS_DIR"; pub get)
$DART --snapshot="$SNAPSHOT_PATH" --package-root="$FLUTTER_TOOLS_DIR/packages" "$SCRIPT_PATH"
echo -n $REVISION > "$STAMP_PATH"
fi
diff --git a/bin/flutter.bat b/bin/flutter.bat
index 2cb79e2..4b0ebbb 100644
--- a/bin/flutter.bat
+++ b/bin/flutter.bat
@@ -29,6 +29,7 @@
:do_snapshot
CD "%flutter_tools_dir%"
+ECHO Updating flutter tool...
CALL pub.bat get
CD "%flutter_root%"
CALL %dart% --snapshot="%snapshot_path%" --package-root="%flutter_tools_dir%\packages" "%script_path%"
@@ -42,4 +43,4 @@
IF /I "%ERRORLEVEL%" EQU "253" (
CALL %dart% --snapshot="%snapshot_path%" --package-root="%flutter_tools_dir%\packages" "%script_path%"
CALL %dart% "%snapshot_path%" %*
-)
\ No newline at end of file
+)
diff --git a/examples/fitness/lib/main.dart b/examples/fitness/lib/main.dart
index a8feff0..029b877 100644
--- a/examples/fitness/lib/main.dart
+++ b/examples/fitness/lib/main.dart
@@ -124,7 +124,7 @@
});
}
- Widget build(BuildContext) {
+ Widget build(BuildContext context) {
return new MaterialApp(
theme: new ThemeData(
brightness: ThemeBrightness.light,
diff --git a/examples/game/example_effect_line.dart b/examples/game/example_effect_line.dart
index c34ebeb..090f508 100644
--- a/examples/game/example_effect_line.dart
+++ b/examples/game/example_effect_line.dart
@@ -38,7 +38,7 @@
);
class TestAppState extends State<TestApp> {
- TestApp() {
+ TestAppState() {
_testBed = new TestBed(_labelTexts[_selectedLine]);
}
diff --git a/examples/widgets/tabs.dart b/examples/widgets/tabs.dart
index c34dab9..cd9c067 100644
--- a/examples/widgets/tabs.dart
+++ b/examples/widgets/tabs.dart
@@ -44,10 +44,10 @@
TabNavigator _buildIconLabelsTabNavigator(int n) {
Iterable<TabNavigatorView> views = ["event", "home", "android", "alarm", "face", "language"]
- .map((icon_name) {
+ .map((String iconName) {
return new TabNavigatorView(
- label: new TabLabel(icon: "action/$icon_name"),
- builder: (BuildContext context) => _buildContent(icon_name)
+ label: new TabLabel(icon: "action/$iconName"),
+ builder: (BuildContext context) => _buildContent(iconName)
);
});
return _buildTabNavigator(n, views.toList(), const ValueKey<String>('iconLabelsTabNavigator'));
diff --git a/packages/cassowary/lib/constraint.dart b/packages/cassowary/lib/constraint.dart
index 83a5946..97820d4 100644
--- a/packages/cassowary/lib/constraint.dart
+++ b/packages/cassowary/lib/constraint.dart
@@ -21,20 +21,20 @@
switch (relation) {
case Relation.equalTo:
- buffer.write(" == 0 ");
+ buffer.write(' == 0 ');
break;
case Relation.greaterThanOrEqualTo:
- buffer.write(" >= 0 ");
+ buffer.write(' >= 0 ');
break;
case Relation.lessThanOrEqualTo:
- buffer.write(" <= 0 ");
+ buffer.write(' <= 0 ');
break;
}
- buffer.write(" | priority = ${priority}");
+ buffer.write(' | priority = $priority');
if (priority == Priority.required) {
- buffer.write(" (required)");
+ buffer.write(' (required)');
}
return buffer.toString();
diff --git a/packages/cassowary/lib/expression.dart b/packages/cassowary/lib/expression.dart
index 0c7f1e9..44fee17 100644
--- a/packages/cassowary/lib/expression.dart
+++ b/packages/cassowary/lib/expression.dart
@@ -58,7 +58,7 @@
_createConstraint(value, Relation.lessThanOrEqualTo);
operator ==(_EquationMember value) =>
- _createConstraint(value, Relation.equalTo);
+ _createConstraint(value, Relation.equalTo); // analyzer says "Type check failed" // analyzer says "The return type 'Constraint' is not a 'bool', as defined by the method '=='"
Expression operator +(_EquationMember m) {
if (m is ConstantMember) {
@@ -140,7 +140,7 @@
if (args == null) {
throw new ParserException(
- "Could not find constant multiplicand or multiplier", [this, m]);
+ 'Could not find constant multiplicand or multiplier', [this, m]);
return null;
}
@@ -150,7 +150,7 @@
_EquationMember operator /(_EquationMember m) {
if (!m.isConstant) {
throw new ParserException(
- "The divisor was not a constant expression", [this, m]);
+ 'The divisor was not a constant expression', [this, m]);
return null;
}
@@ -160,10 +160,10 @@
String toString() {
StringBuffer buffer = new StringBuffer();
- terms.forEach((t) => buffer.write("${t}"));
+ terms.forEach((t) => buffer.write('$t'));
if (constant != 0.0) {
- buffer.write(constant.sign > 0.0 ? "+" : "-");
+ buffer.write(constant.sign > 0.0 ? '+' : '-');
buffer.write(constant.abs());
}
diff --git a/packages/cassowary/lib/solver.dart b/packages/cassowary/lib/solver.dart
index 249560d..3569c7b 100644
--- a/packages/cassowary/lib/solver.dart
+++ b/packages/cassowary/lib/solver.dart
@@ -410,10 +410,10 @@
double temp = row.coefficientForSymbol(entering);
if (temp < 0.0) {
- double temp_ratio = -row.constant / temp;
+ double tempRatio = -row.constant / temp;
- if (temp_ratio < ratio) {
- ratio = temp_ratio;
+ if (tempRatio < ratio) {
+ ratio = tempRatio;
result.first = symbol;
result.second = row;
}
diff --git a/packages/cassowary/lib/symbol.dart b/packages/cassowary/lib/symbol.dart
index cd7bcf4..ab0531a 100644
--- a/packages/cassowary/lib/symbol.dart
+++ b/packages/cassowary/lib/symbol.dart
@@ -13,24 +13,24 @@
_Symbol(this.type, this.tick);
String toString() {
- String typeString = "unknown";
+ String typeString = 'unknown';
switch (type) {
case _SymbolType.invalid:
- typeString = "i";
+ typeString = 'i';
break;
case _SymbolType.external:
- typeString = "v";
+ typeString = 'v';
break;
case _SymbolType.slack:
- typeString = "s";
+ typeString = 's';
break;
case _SymbolType.error:
- typeString = "e";
+ typeString = 'e';
break;
case _SymbolType.dummy:
- typeString = "d";
+ typeString = 'd';
break;
}
- return "${typeString}${tick}";
+ return '$typeString$tick';
}
}
diff --git a/packages/cassowary/lib/variable.dart b/packages/cassowary/lib/variable.dart
index 93850e7..1b278fc 100644
--- a/packages/cassowary/lib/variable.dart
+++ b/packages/cassowary/lib/variable.dart
@@ -21,7 +21,7 @@
return res;
}
- String get debugName => _elvis(name, "variable${_tick}");
+ String get debugName => _elvis(name, 'variable$_tick');
String toString() => debugName;
}
diff --git a/packages/flutter/lib/src/http/http.dart b/packages/flutter/lib/src/http/http.dart
index d6b492f..e70a704 100644
--- a/packages/flutter/lib/src/http/http.dart
+++ b/packages/flutter/lib/src/http/http.dart
@@ -114,7 +114,7 @@
Future<Uint8List> readBytes(url) =>
_withClient((client) => client.readBytes(url));
-Future _withClient(Future fn(MojoClient)) {
+Future _withClient(Future fn(MojoClient client)) {
var client = new MojoClient();
var future = fn(client);
return future.whenComplete(client.close);
diff --git a/packages/flutter/lib/src/rendering/binding.dart b/packages/flutter/lib/src/rendering/binding.dart
index c69da99..bd5fbd4 100644
--- a/packages/flutter/lib/src/rendering/binding.dart
+++ b/packages/flutter/lib/src/rendering/binding.dart
@@ -132,7 +132,11 @@
return 'mouse';
case PointerKind.STYLUS:
return 'stylus';
+ case PointerKind.INVERTED_STYLUS:
+ return 'invertedStylus';
}
+ assert(false);
+ return '';
}
}
diff --git a/packages/flutter_sprites/lib/src/sound.dart b/packages/flutter_sprites/lib/src/sound.dart
index aadf317..1ebd43d 100644
--- a/packages/flutter_sprites/lib/src/sound.dart
+++ b/packages/flutter_sprites/lib/src/sound.dart
@@ -3,7 +3,7 @@
// TODO: The sound effects should probably use Android's SoundPool instead of
// MediaPlayer as it is more efficient and flexible for playing back sound effects
-typedef void SoundEffectStreamCallback(SoundEffectStream);
+typedef void SoundEffectStreamCallback(SoundEffectStream stream);
class SoundEffect {
SoundEffect(this._pipeFuture);
@@ -137,8 +137,8 @@
}
}
-typedef void SoundTrackCallback(SoundTrack);
-typedef void SoundTrackBufferingCallback(SoundTrack, int);
+typedef void SoundTrackCallback(SoundTrack soundTrack);
+typedef void SoundTrackBufferingCallback(SoundTrack soundTrack, int index);
class SoundTrack {
MediaPlayerProxy _player;
diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart
index 39b4e26..44cabd4 100644
--- a/packages/flutter_tools/lib/executable.dart
+++ b/packages/flutter_tools/lib/executable.dart
@@ -9,6 +9,7 @@
import 'package:logging/logging.dart';
import 'package:stack_trace/stack_trace.dart';
+import 'src/commands/analyze.dart';
import 'src/commands/build.dart';
import 'src/commands/cache.dart';
import 'src/commands/daemon.dart';
@@ -46,6 +47,7 @@
});
FlutterCommandRunner runner = new FlutterCommandRunner()
+ ..addCommand(new AnalyzeCommand())
..addCommand(new BuildCommand())
..addCommand(new CacheCommand())
..addCommand(new DaemonCommand())
diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart
index 6310851..c0e4938 100644
--- a/packages/flutter_tools/lib/src/artifacts.dart
+++ b/packages/flutter_tools/lib/src/artifacts.dart
@@ -48,9 +48,9 @@
// In the fullness of time, we'll have a consistent URL pattern for all of
// our artifacts, but, for the time being, Mac OS X artifacts are stored in a
// different cloud storage bucket.
- return 'https://storage.googleapis.com/mojo_infra/flutter/${platform}/${revision}/';
+ return 'https://storage.googleapis.com/mojo_infra/flutter/$platform/$revision/';
}
- return 'https://storage.googleapis.com/mojo/sky/${category}/${platform}/${revision}/';
+ return 'https://storage.googleapis.com/mojo/sky/$category/$platform/$revision/';
}
enum ArtifactType {
diff --git a/packages/flutter_tools/lib/src/commands/analyze.dart b/packages/flutter_tools/lib/src/commands/analyze.dart
new file mode 100644
index 0000000..f4fa293
--- /dev/null
+++ b/packages/flutter_tools/lib/src/commands/analyze.dart
@@ -0,0 +1,302 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:logging/logging.dart';
+import 'package:path/path.dart' as path;
+
+import '../artifacts.dart';
+import '../build_configuration.dart';
+import '../process.dart';
+import 'flutter_command.dart';
+
+final Logger _logging = new Logger('sky_tools.analyze');
+
+class AnalyzeCommand extends FlutterCommand {
+ String get name => 'analyze';
+ String get description => 'Runs a carefully configured dartanalyzer over the current project\'s dart code.';
+
+ AnalyzeCommand() {
+ argParser.addFlag('flutter-repo', help: 'Include all the examples and tests from the Flutter repository.', defaultsTo: false);
+ argParser.addFlag('current-directory', help: 'Include all the Dart files in the current directory, if any.', defaultsTo: true);
+ argParser.addFlag('current-package', help: 'Include the lib/main.dart file from the current directory, if any.', defaultsTo: true);
+ argParser.addFlag('congratulate', help: 'Show output even when there are no errors, warnings, hints, or lints.', defaultsTo: true);
+ }
+
+ bool get requiresProjectRoot => false;
+
+ @override
+ Future<int> runInProject() async {
+ Set<String> pubSpecDirectories = new Set<String>();
+ List<String> dartFiles = argResults.rest.toList();
+
+ for (String file in dartFiles) {
+ // TODO(ianh): figure out how dartanalyzer decides which .packages file to use when given a random file
+ pubSpecDirectories.add(path.dirname(file));
+ }
+
+ if (argResults['flutter-repo']) {
+ // .../examples/*/*.dart
+ // .../examples/*/lib/main.dart
+ Directory examples = new Directory(path.join(ArtifactStore.flutterRoot, 'examples'));
+ for (FileSystemEntity entry in examples.listSync()) {
+ if (entry is Directory) {
+ bool foundOne = false;
+ for (FileSystemEntity subentry in entry.listSync()) {
+ if (subentry is File && subentry.path.endsWith('.dart')) {
+ dartFiles.add(subentry.path);
+ foundOne = true;
+ } else if (subentry is Directory && path.basename(subentry.path) == 'lib') {
+ String mainPath = path.join(subentry.path, 'main.dart');
+ if (FileSystemEntity.isFileSync(mainPath)) {
+ dartFiles.add(mainPath);
+ foundOne = true;
+ }
+ }
+ }
+ if (foundOne)
+ pubSpecDirectories.add(entry.path);
+ }
+ }
+
+ bool foundTest = false;
+ Directory flutterDir = new Directory(path.join(ArtifactStore.flutterRoot, 'packages/unit')); // See https://github.com/flutter/flutter/issues/50
+
+ // .../packages/unit/test/*/*_test.dart
+ Directory tests = new Directory(path.join(flutterDir.path, 'test'));
+ for (FileSystemEntity entry in tests.listSync()) {
+ if (entry is Directory) {
+ for (FileSystemEntity subentry in entry.listSync()) {
+ if (subentry is File && subentry.path.endsWith('_test.dart')) {
+ dartFiles.add(subentry.path);
+ foundTest = true;
+ }
+ }
+ }
+ }
+
+ // .../packages/unit/benchmark/*/*_bench.dart
+ Directory benchmarks = new Directory(path.join(flutterDir.path, 'benchmark'));
+ for (FileSystemEntity entry in benchmarks.listSync()) {
+ if (entry is Directory) {
+ for (FileSystemEntity subentry in entry.listSync()) {
+ if (subentry is File && subentry.path.endsWith('_bench.dart')) {
+ dartFiles.add(subentry.path);
+ foundTest = true;
+ }
+ }
+ }
+ }
+
+ if (foundTest)
+ pubSpecDirectories.add(flutterDir.path);
+
+ // .../packages/*/bin/*.dart
+ Directory packages = new Directory(path.join(ArtifactStore.flutterRoot, 'packages'));
+ for (FileSystemEntity entry in packages.listSync()) {
+ if (entry is Directory) {
+ bool foundOne = false;
+ Directory binDirectory = new Directory(path.join(entry.path, 'bin'));
+ if (binDirectory.existsSync()) {
+ for (FileSystemEntity subentry in binDirectory.listSync()) {
+ if (subentry is File && subentry.path.endsWith('.dart')) {
+ dartFiles.add(subentry.path);
+ foundOne = true;
+ }
+ }
+ }
+ if (foundOne)
+ pubSpecDirectories.add(entry.path);
+ }
+ }
+ }
+
+ if (argResults['current-directory']) {
+ // ./*.dart
+ Directory currentDirectory = new Directory('.');
+ bool foundOne = false;
+ for (FileSystemEntity entry in currentDirectory.listSync()) {
+ if (entry is File && entry.path.endsWith('.dart')) {
+ dartFiles.add(entry.path);
+ foundOne = true;
+ }
+ }
+ if (foundOne)
+ pubSpecDirectories.add('.');
+ }
+
+ if (argResults['current-package']) {
+ // ./lib/main.dart
+ String mainPath = 'lib/main.dart';
+ if (FileSystemEntity.isFileSync(mainPath)) {
+ dartFiles.add(mainPath);
+ pubSpecDirectories.add('.');
+ }
+ }
+
+ // prepare a Dart file that references all the above Dart files
+ StringBuffer mainBody = new StringBuffer();
+ for (int index = 0; index < dartFiles.length; index += 1) {
+ mainBody.writeln('import \'${path.normalize(path.absolute(dartFiles[index]))}\' as file$index;');
+ }
+ mainBody.writeln('void main() { }');
+
+ // prepare a union of all the .packages files
+ Map<String, String> packages = <String, String>{};
+ bool hadInconsistentRequirements = false;
+ for (Directory directory in pubSpecDirectories.map((path) => new Directory(path))) {
+ File dotPackages = new File(path.join(directory.path, '.packages'));
+ if (dotPackages.existsSync()) {
+ Map<String, String> dependencies = <String, String>{};
+ dotPackages
+ .readAsStringSync()
+ .split('\n')
+ .where((line) => !line.startsWith(new RegExp(r'^ *#')))
+ .forEach((line) {
+ int colon = line.indexOf(':');
+ if (colon > 0)
+ dependencies[line.substring(0, colon)] = path.normalize(path.absolute(directory.path, path.fromUri(line.substring(colon+1))));
+ });
+ for (String package in dependencies.keys) {
+ if (packages.containsKey(package)) {
+ if (packages[package] != dependencies[package]) {
+ _logging.warning('Inconsistent requirements for $package; using ${packages[package]} (and not ${dependencies[package]}).');
+ hadInconsistentRequirements = true;
+ }
+ } else {
+ packages[package] = dependencies[package];
+ }
+ }
+ }
+ }
+ if (hadInconsistentRequirements) {
+ if (argResults['flutter-repo'])
+ _logging.warning('You may need to run "dart ${path.normalize(path.relative(path.join(ArtifactStore.flutterRoot, 'dev/update_packages.dart')))}".');
+ if (argResults['current-directory'] || argResults['current-package'])
+ _logging.warning('You may need to run "pub get".');
+ }
+
+ String buildDir = buildConfigurations.firstWhere((BuildConfiguration config) => config.testable, orElse: () => null)?.buildDir;
+ if (buildDir != null) {
+ packages['sky_engine'] = path.join(buildDir, 'gen/dart-pkg/sky_engine/lib');
+ packages['sky_services'] = path.join(buildDir, 'gen/dart-pkg/sky_services/lib');
+ }
+
+ StringBuffer packagesBody = new StringBuffer();
+ for (String package in packages.keys)
+ packagesBody.writeln('$package:${path.toUri(packages[package])}');
+
+ // save the Dart file and the .packages file to disk
+ Directory host = Directory.systemTemp.createTempSync('flutter-analyze-');
+ File mainFile = new File(path.join(host.path, 'main.dart'))..writeAsStringSync(mainBody.toString());
+ File packagesFile = new File(path.join(host.path, '.packages'))..writeAsStringSync(packagesBody.toString());
+
+ List<String> cmd = <String>[
+ sdkBinaryName('dartanalyzer'),
+ // do not set '--warnings', since that will include the entire Dart SDK
+ '--ignore-unrecognized-flags',
+ '--supermixin',
+ '--enable-strict-call-checks',
+ '--enable_type_checks',
+ '--strong',
+ '--package-warnings',
+ '--fatal-warnings',
+ '--strong-hints',
+ '--fatal-hints',
+ '--lints',
+ '--packages', packagesFile.path,
+ mainFile.path
+ ];
+
+ _logging.info(cmd.join(' '));
+ Process process = await Process.start(
+ cmd[0],
+ cmd.sublist(1),
+ workingDirectory: host.path
+ );
+ int errorCount = 0;
+ StringBuffer output = new StringBuffer();
+ process.stdout.transform(UTF8.decoder).listen((String data) {
+ output.write(data);
+ });
+ process.stderr.transform(UTF8.decoder).listen((String data) {
+ // dartanalyzer doesn't seem to ever output anything on stderr
+ errorCount += 1;
+ print(data);
+ });
+
+ // host.deleteSync();
+
+ int exitCode = await process.exitCode;
+
+ List<Pattern> patternsToSkip = <Pattern>[
+ 'Analyzing [${mainFile.path}]...',
+ new RegExp('^\\[hint\\] Unused import \\(${mainFile.path},'),
+ new RegExp(r'^\[.+\] .+ \(.+/\.pub-cache/.+'),
+ new RegExp(r'^\[error\] Invalid override\. The type of [^ ]+ \(.+\) is not a subtype of [^ ]+ \(.+\)\.'), // we allow type narrowing
+ new RegExp(r'^\[warning\] .+ will need runtime check to cast to type .+'), // https://github.com/dart-lang/sdk/issues/24542
+ new RegExp(r'^\[error\] Type check failed: .*\(dynamic\) is not of type'), // allow unchecked casts from dynamic
+ new RegExp('^\\[error\\] Target of URI does not exist: \'dart:ui_internals\''), // https://github.com/flutter/flutter/issues/83
+ new RegExp(r'\[lint\] Prefer using lowerCamelCase for constant names.'), // sometimes we have no choice (e.g. when matching other platforms)
+ new RegExp(r'\[lint\] Avoid defining a one-member abstract class when a simple function will do.'), // too many false-positives; code review should catch real instances
+ new RegExp(r'\[0-9]+ (error|warning|hint|lint).+found\.'),
+ '',
+ ];
+
+ RegExp generalPattern = new RegExp(r'^\[(error|warning|hint|lint)\] (.+) \(([^(),]+), line ([0-9]+), col ([0-9]+)\)$');
+ RegExp ignorePattern = new RegExp(r'// analyzer says "([^"]+)"');
+ RegExp constructorTearOffsPattern = new RegExp('.+#.+// analyzer doesn\'t like constructor tear-offs');
+ RegExp allowedIdentifiers = new RegExp(r'_?([A-Z]|_+)\b');
+
+ List<String> errorLines = output.toString().split('\n');
+ for (String errorLine in errorLines) {
+ if (patternsToSkip.every((Pattern pattern) => pattern.allMatches(errorLine).isEmpty)) {
+ Match groups = generalPattern.firstMatch(errorLine);
+ if (groups != null) {
+ String level = groups[1];
+ String filename = groups[3];
+ String errorMessage = groups[2];
+ int lineNumber = int.parse(groups[4]);
+ int colNumber = int.parse(groups[5]);
+ File source = new File(filename);
+ List<String> sourceLines = source.readAsLinesSync();
+ String sourceLine = sourceLines[lineNumber-1];
+ bool shouldIgnore = false;
+ if (filename.endsWith('.mojom.dart')) {
+ shouldIgnore = true;
+ } else if (level == 'lint' && errorMessage == 'Name non-constant identifiers using lowerCamelCase.') {
+ if (allowedIdentifiers.matchAsPrefix(sourceLine, colNumber-1) != null)
+ shouldIgnore = true;
+ } else if (constructorTearOffsPattern.allMatches(sourceLine).isNotEmpty) {
+ shouldIgnore = true;
+ } else {
+ Iterable<Match> ignoreGroups = ignorePattern.allMatches(sourceLine);
+ for (Match ignoreGroup in ignoreGroups) {
+ if (errorMessage.contains(ignoreGroup[1])) {
+ shouldIgnore = true;
+ break;
+ }
+ }
+ }
+ if (shouldIgnore)
+ continue;
+ }
+ print(errorLine);
+ errorCount += 1;
+ }
+ }
+
+ if (exitCode < 0 || exitCode > 3) // 1 = hints, 2 = warnings, 3 = errors
+ return exitCode;
+
+ if (errorCount > 1)
+ return 1;
+ if (argResults['congratulate'])
+ print('No analyzer warnings!');
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/packages/flutter_tools/lib/src/commands/build.dart b/packages/flutter_tools/lib/src/commands/build.dart
index caee7e6..b892c10 100644
--- a/packages/flutter_tools/lib/src/commands/build.dart
+++ b/packages/flutter_tools/lib/src/commands/build.dart
@@ -93,7 +93,7 @@
}
ArchiveFile _createFile(String key, String assetBase) {
- File file = new File('${assetBase}/${key}');
+ File file = new File('$assetBase/$key');
if (!file.existsSync())
return null;
List<int> content = file.readAsBytesSync();
diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart
index cd82c5f..e391296 100644
--- a/packages/flutter_tools/lib/src/commands/daemon.dart
+++ b/packages/flutter_tools/lib/src/commands/daemon.dart
@@ -60,7 +60,7 @@
stdout.writeln('[${JSON.encode(command)}]');
}, daemonCommand: this);
- return daemon.onExit;
+ return await daemon.onExit;
}
}
@@ -98,19 +98,19 @@
var id = command['id'];
if (id == null) {
- _logging.severe('no id for command: ${command}');
+ _logging.severe('no id for command: $command');
return;
}
try {
String event = command['event'];
if (event.indexOf('.') == -1)
- throw 'command not understood: ${event}';
+ throw 'command not understood: $event';
String prefix = event.substring(0, event.indexOf('.'));
String name = event.substring(event.indexOf('.') + 1);
if (_domains[prefix] == null)
- throw 'no domain for command: ${command}';
+ throw 'no domain for command: $command';
_domains[prefix].handleEvent(name, id, command['params']);
} catch (error, trace) {
@@ -144,7 +144,7 @@
new Future.sync(() {
if (_handlers.containsKey(name))
return _handlers[name](args);
- throw 'command not understood: ${name}';
+ throw 'command not understood: $name';
}).then((result) {
if (result == null) {
_send({'id': id});
@@ -153,7 +153,7 @@
}
}).catchError((error, trace) {
_send({'id': id, 'error': _toJsonable(error)});
- _logging.warning('error handling ${name}', error, trace);
+ _logging.warning('error handling $name', error, trace);
});
}
@@ -210,5 +210,5 @@
dynamic _toJsonable(dynamic obj) {
if (obj is String || obj is int || obj is bool || obj is Map || obj is List || obj == null)
return obj;
- return '${obj}';
+ return '$obj';
}
diff --git a/packages/flutter_tools/lib/src/commands/flutter_command.dart b/packages/flutter_tools/lib/src/commands/flutter_command.dart
index f31ad56..4ff335a 100644
--- a/packages/flutter_tools/lib/src/commands/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/commands/flutter_command.dart
@@ -56,7 +56,7 @@
}
}
- return runInProject();
+ return await runInProject();
}
Future<int> runInProject();
diff --git a/packages/flutter_tools/lib/src/commands/init.dart b/packages/flutter_tools/lib/src/commands/init.dart
index 85ef441..3c70531 100644
--- a/packages/flutter_tools/lib/src/commands/init.dart
+++ b/packages/flutter_tools/lib/src/commands/init.dart
@@ -6,12 +6,15 @@
import 'dart:io';
import 'package:args/command_runner.dart';
+import 'package:logging/logging.dart';
import 'package:mustache4dart/mustache4dart.dart' as mustache;
import 'package:path/path.dart' as p;
import '../artifacts.dart';
import '../process.dart';
+final Logger _logging = new Logger('sky_tools.init');
+
class InitCommand extends Command {
final String name = 'init';
final String description = 'Create a new Flutter project.';
@@ -20,7 +23,7 @@
argParser.addOption('out', abbr: 'o', help: 'The output directory.');
argParser.addFlag('pub',
defaultsTo: true,
- help: 'Whether to run pub after the project has been created.');
+ help: 'Whether to run "pub get" after the project has been created.');
}
@override
@@ -40,7 +43,7 @@
String flutterPackagePath = p.join(flutterRoot, 'packages', 'flutter');
if (!FileSystemEntity.isFileSync(p.join(flutterPackagePath, 'pubspec.yaml'))) {
- print('Unable to find package:flutter in ${flutterPackagePath}');
+ print('Unable to find package:flutter in $flutterPackagePath');
return 2;
}
@@ -58,11 +61,7 @@
''';
if (argResults['pub']) {
- print("Running pub get...");
- int code = await runCommandAndStreamOutput(
- [sdkBinaryName('pub'), 'get'],
- workingDirectory: out.path
- );
+ int code = await pubGet(directory: out.path);
if (code != 0)
return code;
}
@@ -70,6 +69,41 @@
print(message);
return 0;
}
+
+ Future<int> pubGet({
+ String directory: '',
+ bool skipIfAbsent: false,
+ bool verbose: true
+ }) async {
+ File pubSpecYaml = new File(p.join(directory, 'pubspec.yaml'));
+ File pubSpecLock = new File(p.join(directory, 'pubspec.lock'));
+ File dotPackages = new File(p.join(directory, '.packages'));
+
+ if (!pubSpecYaml.existsSync()) {
+ if (skipIfAbsent)
+ return 0;
+ _logging.severe('$directory: no pubspec.yaml found');
+ return 1;
+ }
+
+ if (!pubSpecLock.existsSync() || pubSpecYaml.lastModifiedSync().isAfter(pubSpecLock.lastModifiedSync())) {
+ if (verbose)
+ print("Running pub get in $directory...");
+ int code = await runCommandAndStreamOutput(
+ [sdkBinaryName('pub'), 'get'],
+ workingDirectory: directory
+ );
+ if (code != 0)
+ return code;
+ }
+
+ if ((pubSpecLock.existsSync() && pubSpecLock.lastModifiedSync().isAfter(pubSpecYaml.lastModifiedSync())) &&
+ (dotPackages.existsSync() && dotPackages.lastModifiedSync().isAfter(pubSpecYaml.lastModifiedSync())))
+ return 0;
+
+ _logging.severe('$directory: pubspec.yaml, pubspec.lock, and .packages are in an inconsistent state');
+ return 1;
+ }
}
abstract class Template {
diff --git a/packages/flutter_tools/lib/src/commands/run_mojo.dart b/packages/flutter_tools/lib/src/commands/run_mojo.dart
index ce0a187..516673e 100644
--- a/packages/flutter_tools/lib/src/commands/run_mojo.dart
+++ b/packages/flutter_tools/lib/src/commands/run_mojo.dart
@@ -34,7 +34,7 @@
String _makePathAbsolute(String relativePath) {
File file = new File(relativePath);
if (!file.existsSync()) {
- throw new Exception("Path \"${relativePath}\" does not exist");
+ throw new Exception('Path "$relativePath" does not exist');
}
return file.absolute.path;
}
@@ -79,8 +79,8 @@
final appPath = _makePathAbsolute(argResults['app']);
Artifact artifact = ArtifactStore.getArtifact(type: ArtifactType.viewer, targetPlatform: TargetPlatform.linux);
final viewerPath = _makePathAbsolute(await ArtifactStore.getPath(artifact));
- args.add('file://${appPath}');
- args.add('--url-mappings=mojo:sky_viewer=file://${viewerPath}');
+ args.add('file://$appPath');
+ args.add('--url-mappings=mojo:sky_viewer=file://$viewerPath');
}
if (useDevtools) {
@@ -115,6 +115,6 @@
return 1;
}
- return runCommandAndStreamOutput(await _getShellConfig());
+ return await runCommandAndStreamOutput(await _getShellConfig());
}
}
diff --git a/packages/flutter_tools/lib/src/commands/start.dart b/packages/flutter_tools/lib/src/commands/start.dart
index ce60e43..842d909 100644
--- a/packages/flutter_tools/lib/src/commands/start.dart
+++ b/packages/flutter_tools/lib/src/commands/start.dart
@@ -68,7 +68,7 @@
if (FileSystemEntity.isDirectorySync(target))
mainPath = path.join(target, 'lib', 'main.dart');
if (!FileSystemEntity.isFileSync(mainPath)) {
- String message = 'Tried to run ${mainPath}, but that file does not exist.';
+ String message = 'Tried to run $mainPath, but that file does not exist.';
if (!argResults.wasParsed('target'))
message += '\nConsider using the -t option to specify that Dart file to start.';
stderr.writeln(message);
diff --git a/packages/flutter_tools/lib/src/commands/test.dart b/packages/flutter_tools/lib/src/commands/test.dart
index 8c3202c..5d26834 100644
--- a/packages/flutter_tools/lib/src/commands/test.dart
+++ b/packages/flutter_tools/lib/src/commands/test.dart
@@ -11,7 +11,6 @@
import '../artifacts.dart';
import '../build_configuration.dart';
-import '../process.dart';
import '../test/loader.dart' as loader;
import 'flutter_command.dart';
@@ -49,25 +48,6 @@
testArgs.insert(0, '--no-color');
List<BuildConfiguration> configs = buildConfigurations;
bool foundOne = false;
-
- File pubSpecYaml = new File(path.join(flutterDir.path, 'pubspec.yaml'));
- File pubSpecLock = new File(path.join(flutterDir.path, 'pubspec.lock'));
-
- if (!pubSpecYaml.existsSync()) {
- print('${flutterDir.path} has no pubspec.yaml');
- return 1;
- }
-
- if (!pubSpecLock.existsSync() || pubSpecYaml.lastModifiedSync().isAfter(pubSpecLock.lastModifiedSync())) {
- print("Running pub get...");
- int code = await runCommandAndStreamOutput(
- [sdkBinaryName('pub'), 'get'],
- workingDirectory: flutterDir.path
- );
- if (code != 0)
- return code;
- }
-
String currentDirectory = Directory.current.path;
Directory.current = flutterDir.path;
loader.installHook();
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index d0ef982..47831e8 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -19,36 +19,8 @@
final String id;
static Map<String, Device> _deviceCache = {};
- factory Device._unique(String className, [String id = null]) {
- if (id == null) {
- if (className == AndroidDevice.className) {
- id = AndroidDevice.defaultDeviceID;
- } else if (className == IOSDevice.className) {
- id = IOSDevice.defaultDeviceID;
- } else if (className == IOSSimulator.className) {
- id = IOSSimulator.defaultDeviceID;
- } else {
- throw 'Attempted to create a Device of unknown type $className';
- }
- }
-
- return _deviceCache.putIfAbsent(id, () {
- if (className == AndroidDevice.className) {
- final device = new AndroidDevice._(id);
- _deviceCache[id] = device;
- return device;
- } else if (className == IOSDevice.className) {
- final device = new IOSDevice._(id);
- _deviceCache[id] = device;
- return device;
- } else if (className == IOSSimulator.className) {
- final device = new IOSSimulator._(id);
- _deviceCache[id] = device;
- return device;
- } else {
- throw 'Attempted to create a Device of unknown type $className';
- }
- });
+ static Device _unique(String id, Device constructor(String id)) {
+ return _deviceCache.putIfAbsent(id, () => constructor(id));
}
Device._(this.id);
@@ -74,7 +46,6 @@
}
class IOSDevice extends Device {
- static const String className = 'IOSDevice';
static final String defaultDeviceID = 'default_ios_id';
static const String _macInstructions =
@@ -108,7 +79,7 @@
String get name => _name;
factory IOSDevice({String id, String name}) {
- IOSDevice device = new Device._unique(className, id);
+ IOSDevice device = Device._unique(id ?? defaultDeviceID, new IOSDevice#_); // analyzer doesn't like constructor tear-offs
device._name = name;
return device;
}
@@ -224,14 +195,17 @@
}
// idevicedebug hangs forever after launching the app, so kill it after
// giving it plenty of time to send the launch command.
- return runAndKill(
- [debuggerPath, 'run', app.id], new Duration(seconds: 3)).then(
- (_) {
- return true;
- }, onError: (e) {
- _logging.info('Failure running $debuggerPath: ', e);
- return false;
- });
+ return await runAndKill(
+ [debuggerPath, 'run', app.id],
+ new Duration(seconds: 3)
+ ).then(
+ (_) {
+ return true;
+ }, onError: (e) {
+ _logging.info('Failure running $debuggerPath: ', e);
+ return false;
+ }
+ );
}
@override
@@ -274,13 +248,12 @@
if (!isConnected()) {
return 2;
}
- return runCommandAndStreamOutput([loggerPath],
+ return await runCommandAndStreamOutput([loggerPath],
prefix: 'iOS dev: ', filter: new RegExp(r'.*SkyShell.*'));
}
}
class IOSSimulator extends Device {
- static const String className = 'IOSSimulator';
static final String defaultDeviceID = 'default_ios_sim_id';
static const String _macInstructions =
@@ -299,7 +272,7 @@
String get name => _name;
factory IOSSimulator({String id, String name, String iOSSimulatorPath}) {
- IOSSimulator device = new Device._unique(className, id);
+ IOSSimulator device = Device._unique(id ?? defaultDeviceID, new IOSSimulator#_); // analyzer doesn't like constructor tear-offs
device._name = name;
if (iOSSimulatorPath == null) {
iOSSimulatorPath = path.join('/Applications', 'iOS Simulator.app',
@@ -309,7 +282,7 @@
return device;
}
- IOSSimulator._(String id) : super._(id) {}
+ IOSSimulator._(String id) : super._(id);
static String _getRunningSimulatorID([IOSSimulator mockIOS]) {
String xcrunPath = mockIOS != null ? mockIOS.xcrunPath : _xcrunPath;
@@ -380,7 +353,6 @@
}
if (id == defaultDeviceID) {
runDetached([iOSSimPath]);
-
Future<bool> checkConnection([int attempts = 20]) async {
if (attempts == 0) {
_logging.info('Timed out waiting for iOS Simulator $id to boot.');
@@ -388,12 +360,12 @@
}
if (!isConnected()) {
_logging.info('Waiting for iOS Simulator $id to boot...');
- return new Future.delayed(new Duration(milliseconds: 500),
+ return await new Future.delayed(new Duration(milliseconds: 500),
() => checkConnection(attempts - 1));
}
return true;
}
- return checkConnection();
+ return await checkConnection();
} else {
try {
runCheckedSync([xcrunPath, 'simctl', 'boot', id]);
@@ -496,7 +468,7 @@
if (clear) {
runSync(['rm', logFilePath]);
}
- return runCommandAndStreamOutput(['tail', '-f', logFilePath],
+ return await runCommandAndStreamOutput(['tail', '-f', logFilePath],
prefix: 'iOS sim: ', filter: new RegExp(r'.*SkyShell.*'));
}
}
@@ -505,7 +477,6 @@
static const String _ADB_PATH = 'adb';
static const int _observatoryPort = 8181;
- static const String className = 'AndroidDevice';
static final String defaultDeviceID = 'default_android_device';
String productID;
@@ -522,7 +493,7 @@
String productID: null,
String modelID: null,
String deviceCodeName: null}) {
- AndroidDevice device = new Device._unique(className, id);
+ AndroidDevice device = Device._unique(id ?? defaultDeviceID, new AndroidDevice#_); // analyzer doesn't like constructor tear-offs
device.productID = productID;
device.modelID = modelID;
device.deviceCodeName = deviceCodeName;
@@ -813,7 +784,7 @@
clearLogs();
}
- return runCommandAndStreamOutput(adbCommandForDevice([
+ return await runCommandAndStreamOutput(adbCommandForDevice([
'logcat',
'-v',
'tag', // Only log the tag and the message
diff --git a/packages/flutter_tools/lib/src/process.dart b/packages/flutter_tools/lib/src/process.dart
index 3259b69..c0a547f 100644
--- a/packages/flutter_tools/lib/src/process.dart
+++ b/packages/flutter_tools/lib/src/process.dart
@@ -18,33 +18,35 @@
String workingDirectory
}) async {
_logging.info(cmd.join(' '));
- Process proc = await Process.start(
+ Process process = await Process.start(
cmd[0],
- cmd.getRange(1, cmd.length).toList(),
+ cmd.sublist(1),
workingDirectory: workingDirectory
);
- proc.stdout.transform(UTF8.decoder).listen((String data) {
+ process.stdout.transform(UTF8.decoder).listen((String data) {
List<String> dataLines = data.trimRight().split('\n');
if (filter != null) {
+ // TODO(ianh): This doesn't handle IO buffering (where the data might be split half-way through a line)
dataLines = dataLines.where((String s) => filter.hasMatch(s)).toList();
}
if (dataLines.length > 0) {
stdout.write('$prefix${dataLines.join('\n$prefix')}\n');
}
});
- proc.stderr.transform(UTF8.decoder).listen((String data) {
+ process.stderr.transform(UTF8.decoder).listen((String data) {
List<String> dataLines = data.trimRight().split('\n');
if (filter != null) {
+ // TODO(ianh): This doesn't handle IO buffering (where the data might be split half-way through a line)
dataLines = dataLines.where((String s) => filter.hasMatch(s));
}
if (dataLines.length > 0) {
stderr.write('$prefix${dataLines.join('\n$prefix')}\n');
}
});
- return proc.exitCode;
+ return await process.exitCode;
}
-Future runAndKill(List<String> cmd, Duration timeout) async {
+Future runAndKill(List<String> cmd, Duration timeout) {
Future<Process> proc = runDetached(cmd);
return new Future.delayed(timeout, () async {
_logging.info('Intentionally killing ${cmd[0]}');
@@ -52,7 +54,7 @@
});
}
-Future<Process> runDetached(List<String> cmd) async {
+Future<Process> runDetached(List<String> cmd) {
_logging.info(cmd.join(' '));
Future<Process> proc = Process.start(
cmd[0], cmd.getRange(1, cmd.length).toList(),
@@ -97,6 +99,6 @@
class ProcessExit implements Exception {
final int exitCode;
ProcessExit(this.exitCode);
- String get message => 'ProcessExit: ${exitCode}';
+ String get message => 'ProcessExit: $exitCode';
String toString() => message;
}
diff --git a/packages/newton/lib/src/friction_simulation.dart b/packages/newton/lib/src/friction_simulation.dart
index 38671b9..17d1ccc 100644
--- a/packages/newton/lib/src/friction_simulation.dart
+++ b/packages/newton/lib/src/friction_simulation.dart
@@ -50,8 +50,9 @@
double drag,
double position,
double velocity,
- double this._minX,
- double this._maxX) : super(drag, position, velocity);
+ this._minX,
+ this._maxX
+ ) : super(drag, position, velocity);
final double _minX;
final double _maxX;
diff --git a/packages/newton/lib/src/spring_simulation.dart b/packages/newton/lib/src/spring_simulation.dart
index 143f96c..f0f216a 100644
--- a/packages/newton/lib/src/spring_simulation.dart
+++ b/packages/newton/lib/src/spring_simulation.dart
@@ -17,7 +17,8 @@
final double damping;
SpringDescription(
- {double this.mass, double this.springConstant, double this.damping}) {
+ { this.mass, this.springConstant, this.damping }
+ ) {
assert(mass != null);
assert(springConstant != null);
assert(damping != null);
diff --git a/packages/unit/benchmark/gestures/velocity_tracker_bench.dart b/packages/unit/benchmark/gestures/velocity_tracker_bench.dart
index cf89561..94c6a15 100644
--- a/packages/unit/benchmark/gestures/velocity_tracker_bench.dart
+++ b/packages/unit/benchmark/gestures/velocity_tracker_bench.dart
@@ -1,5 +1,3 @@
-import 'dart:ui' as ui;
-
import 'package:flutter/gestures.dart';
import 'package:test/test.dart';
import 'velocity_tracker_data.dart';
@@ -45,20 +43,4 @@
watch.stop();
print("Dart tracker: " + watch.elapsed.toString());
});
-
- test('Native velocity tracker performance', () {
- ui.VelocityTracker tracker = new ui.VelocityTracker();
- Stopwatch watch = new Stopwatch();
- watch.start();
- for (int i = 0; i < kNumIters; i++) {
- for (PointerInputEvent event in events) {
- if (event.type == 'pointerdown' || event.type == 'pointermove')
- tracker.addPosition((event.timeStamp*1000.0).toInt(), event.x, event.y);
- if (event.type == 'pointerup')
- tracker.getVelocity();
- }
- }
- watch.stop();
- print("Native tracker: " + watch.elapsed.toString());
- });
}
diff --git a/packages/unit/test/rendering/image_test.dart b/packages/unit/test/rendering/image_test.dart
index b794f84..4899673 100644
--- a/packages/unit/test/rendering/image_test.dart
+++ b/packages/unit/test/rendering/image_test.dart
@@ -8,16 +8,19 @@
class SquareImage implements ui.Image {
int get width => 10;
int get height => 10;
+ void dispose() { }
}
class WideImage implements ui.Image {
int get width => 20;
int get height => 10;
+ void dispose() { }
}
class TallImage implements ui.Image {
int get width => 10;
int get height => 20;
+ void dispose() { }
}
void main() {
diff --git a/travis/test.sh b/travis/test.sh
index 2e1597b..fa5e48b 100755
--- a/travis/test.sh
+++ b/travis/test.sh
@@ -1,12 +1,16 @@
#!/bin/bash
set -ex
-(cd packages/cassowary; pub global run tuneup check; pub run test -j1)
-(cd packages/flutter_sprites; pub global run tuneup check) # No tests to run.
-(cd packages/flutter_tools; pub global run tuneup check; pub run test -j1)
-(cd packages/flx; pub global run tuneup check; pub run test -j1)
-(cd packages/newton; pub global run tuneup check; pub run test -j1)
-(cd packages/playfair; pub global run tuneup check) # No tests to run.
-(cd packages/updater; pub global run tuneup check) # No tests to run.
+# analyze all the Dart code in the repo
+./bin/flutter analyze --flutter-repo --no-current-directory --no-current-package --congratulate
+# flutter package tests
./bin/flutter test --engine-src-path bin/cache/travis
+
+(cd packages/cassowary; pub run test -j1)
+# (cd packages/flutter_sprites; ) # No tests to run.
+(cd packages/flutter_tools; pub run test -j1)
+(cd packages/flx; pub run test -j1)
+(cd packages/newton; pub run test -j1)
+# (cd packages/playfair; ) # No tests to run.
+# (cd packages/updater; ) # No tests to run.