Skip to content

Conversation

@devoncarew
Copy link
Contributor

Re-write flutter analyze (the single-shot mode and --flutter-repo) to use the analysis server.

not yet ready for review -

@devoncarew
Copy link
Contributor Author

OK, this is now ready for review. Previously, we had:

  • flutter analyze implemented using the dartanalyzer command-line tool
  • flutter analyze --flutter-repo implemented using the package:analyzer APIs
  • flutter analyze --watch implemented using the analysis server

This was a lot of different code paths and implementations for doing flutter analysis. Additionally, the specific package:analyzer APIs that flutter analyze was using are deprecated, and won't support the full fidelity of the changes for the Dart 2.0 type system; it's important to move flutter analyze off of them.

This PR then:

  • re-writes flutter analyze and flutter analyze --flutter-repo to be based on the analysis server
  • for flutter analyze --flutter-repo, changes from passing 29 different directories into the analysis server to 3 top level ones; we see a substantial performance benefit from this (initial analysis of the repo using the analysis server drops from 90 seconds to 40)
  • some clean up to the 3 different analysis options files in this repo to reduce duplication. We now have one top level analysis_options.yaml file for the repo (analysis_options_repo.yaml is deleted); one small delta to that in /packages/ to turn on public_member_api_docs, and one in /packages/flutter_tools to reference the top level options file, and not enable public_member_api_docs
  • removed the feature of flutter analyzeing individual files (just directories are supported now)

Locally, I see a perf speedup in the initial (cold) repo analysis of 25% (50s to 40s), and additional (warm) analysis speedup of 3x (50s to 16s).

Since we're using the analysis server, flutter analyze is analyzing the user's pubspec.yaml file, and will now catch issues in the flutter: section (we have some lints for the assets section).

final Process process = await Process.start(
_flutter,
<String>['analyze', '--no-preamble', mainDart.path],
<String>['analyze', '--no-preamble', '--no-congratulate', mainDart.parent.path],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you not analyze just one file anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, that is a use case that this PR removes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(and, it is one that I want to remove, for implementation simplicity, and to reduce the overall use case complexity here)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW, as a user, I find that not being able to analyze a single file (or rather, a single entry point, plus any files it might import directly or indirectly in the same package) is really annoying. I will often, for example, copy a file as a backup and then make backwards-incompatible changes which cause the analyzer to complain about the backup file. I also often will have a dummy package where I have multiple different "main.dart" entry points (named various things), and I only care about the one I'm editing, even though many of the others have tons of problems because I was just copying and pasting other people's code into them.

if (entry == 'Building flutter tool...') {
await for (String entry in analysis.stderr.transform(utf8.decoder).transform(const LineSplitter())) {
print('analyzer stderr: $entry');
if (entry.startsWith('[lint] ')) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lints show up in stderr, but info, warning, and error show up in stdout?? wat?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come this uses the [...] syntax but above you have the bullet point syntax?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The [...] message is specifically generated elsewhere ([lint] $undocumentedMembers public members lack...); I'm just preserving current behavior as there's already a fair amount going on in this PR.

This single message shows in stderr (not infos, warnings, or errors as they're part of the regular tool output). It's part of the exit message if there were undoc'ed members found.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could choose to change this is this PR - to clarify that this is a marker message generated elsewhere - or add some comments to that effect.

@@ -0,0 +1,8 @@
# Take our settings from the repo's main analysis_options.yaml file, but include
# an additional rule to validate that public members are documented.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome (🎉 for not having two almost-duplicated options files)!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, all the copies have comments saying to please make changes to all files, and all the files have separate diffs...

argParser.addOption('write', valueHelp: 'file', help: 'Also output the results to a file. This is useful with --watch if you want a file to always contain the latest results.');
argParser.addOption('dart-sdk', valueHelp: 'path-to-sdk', help: 'The path to the Dart SDK.', hide: !verboseHelp);
AnalyzeCommand({bool verboseHelp: false, this.workingDirectory}) {
argParser.addFlag('flutter-repo',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be hidden

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do

help: 'Include all the examples and tests from the Flutter repository.',
defaultsTo: false);
argParser.addFlag('current-package',
help: 'Analyze the current project, if applicable.', defaultsTo: true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Versus analyzing what? Does it even work to pass --no-current-package?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does work, however I would be happy to remove the option; leaving it in preserves current behavior.

defaultsTo: false);
argParser.addFlag('current-package',
help: 'Analyze the current project, if applicable.', defaultsTo: true);
argParser.addFlag('dartdocs',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this do anything anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, it should have the same behavior as before. For the flutter repo, without it, the cli tool will report the number of undocumented members. With it, it'll report and list them individually (and fail if there are any undoc'd members).

# - analysis_options.yaml (this file)
# - analysis_options_repo.yaml
# - packages/flutter/lib/analysis_options_user.yaml
# - https://github.com/flutter/plugins/blob/master/analysis_options.yaml
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do


void _handleAnalysisErrors(FileAnalysisErrors fileErrors) {
fileErrors.errors.removeWhere(_filterError);
fileErrors.errors.removeWhere((AnalysisError error) => error.type == 'TODO');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't we turn these off in the options file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We now have the same options file for IDE and cli analysis. You do want to see these in the IDE (or use IDE level settings to hide them). They're not valuable on the cli; we filter them out from the dartanalyzer cli tool as well.

if (argResults['flutter-repo']) {
// check for conflicting dependencies
final PackageDependencyTracker dependencies =
new PackageDependencyTracker();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrapping is off here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Copy link
Contributor Author

@devoncarew devoncarew left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking a pass through! PR updated

# - analysis_options.yaml (this file)
# - analysis_options_repo.yaml
# - packages/flutter/lib/analysis_options_user.yaml
# - https://github.com/flutter/plugins/blob/master/analysis_options.yaml
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do

final Process process = await Process.start(
_flutter,
<String>['analyze', '--no-preamble', mainDart.path],
<String>['analyze', '--no-preamble', '--no-congratulate', mainDart.parent.path],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, that is a use case that this PR removes.

if (entry == 'Building flutter tool...') {
await for (String entry in analysis.stderr.transform(utf8.decoder).transform(const LineSplitter())) {
print('analyzer stderr: $entry');
if (entry.startsWith('[lint] ')) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The [...] message is specifically generated elsewhere ([lint] $undocumentedMembers public members lack...); I'm just preserving current behavior as there's already a fair amount going on in this PR.

This single message shows in stderr (not infos, warnings, or errors as they're part of the regular tool output). It's part of the exit message if there were undoc'ed members found.

@@ -0,0 +1,8 @@
# Take our settings from the repo's main analysis_options.yaml file, but include
# an additional rule to validate that public members are documented.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, all the copies have comments saying to please make changes to all files, and all the files have separate diffs...

argParser.addOption('write', valueHelp: 'file', help: 'Also output the results to a file. This is useful with --watch if you want a file to always contain the latest results.');
argParser.addOption('dart-sdk', valueHelp: 'path-to-sdk', help: 'The path to the Dart SDK.', hide: !verboseHelp);
AnalyzeCommand({bool verboseHelp: false, this.workingDirectory}) {
argParser.addFlag('flutter-repo',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do

final Process process = await Process.start(
_flutter,
<String>['analyze', '--no-preamble', mainDart.path],
<String>['analyze', '--no-preamble', '--no-congratulate', mainDart.parent.path],
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(and, it is one that I want to remove, for implementation simplicity, and to reduce the overall use case complexity here)

help: 'Include all the examples and tests from the Flutter repository.',
defaultsTo: false);
argParser.addFlag('current-package',
help: 'Analyze the current project, if applicable.', defaultsTo: true);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does work, however I would be happy to remove the option; leaving it in preserves current behavior.

defaultsTo: false);
argParser.addFlag('current-package',
help: 'Analyze the current project, if applicable.', defaultsTo: true);
argParser.addFlag('dartdocs',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, it should have the same behavior as before. For the flutter repo, without it, the cli tool will report the number of undocumented members. With it, it'll report and list them individually (and fail if there are any undoc'd members).


void _handleAnalysisErrors(FileAnalysisErrors fileErrors) {
fileErrors.errors.removeWhere(_filterError);
fileErrors.errors.removeWhere((AnalysisError error) => error.type == 'TODO');
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We now have the same options file for IDE and cli analysis. You do want to see these in the IDE (or use IDE level settings to hide them). They're not valuable on the cli; we filter them out from the dartanalyzer cli tool as well.

if (argResults['flutter-repo']) {
// check for conflicting dependencies
final PackageDependencyTracker dependencies =
new PackageDependencyTracker();
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

argParser.addOption('write',
valueHelp: 'file',
help:
'Also output the results to a file. This is useful with --watch if you want a file to always contain the latest results.');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like you're going for dartfmt style in this file, in which case let's wrap these long strings:

       help:
           'Also output the results to a file. This is useful with --watch if '
           'you want a file to always contain the latest results.');

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

final PackageDependencyTracker dependencies = new PackageDependencyTracker();
dependencies.checkForConflictingDependencies(repoPackages, dependencies);
directories = repoPackages.map((Directory dir) => dir.path).toList();
directories = repoRoots;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just remove the local variable and refer to repoRoots directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cleaned up the logic here a bit -

Copy link
Contributor

@tvolkert tvolkert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@danrubel danrubel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 1 to simplifying the "flutter analyze" implementation and reducing the number of analysis options files.
    Looks like good progress to me.

@devoncarew devoncarew merged commit 2f41ea5 into flutter:master Apr 11, 2018
devoncarew added a commit that referenced this pull request Apr 11, 2018
… to use the analysis server (#16281)"

This reverts commit 2f41ea5.
devoncarew added a commit that referenced this pull request Apr 11, 2018
… to use the analysis server (#16281)" (#16482)

This reverts commit 2f41ea5.
DaveShuckerow pushed a commit to DaveShuckerow/flutter that referenced this pull request May 14, 2018
…the analysis server (flutter#16281)

re-write flutter analyze (the single-shot and --flutter-repo) to use the analysis server
DaveShuckerow pushed a commit to DaveShuckerow/flutter that referenced this pull request May 14, 2018
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 11, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants