Skip to content

Commit e13e780

Browse files
author
Dan Rubel
authored
Flutter analyze watch improvements (flutter#11143)
* flutter analyze --watch auto detect if in flutter repo * move isFlutterLibrary from AnalyzeOnce into AnalyzeBase for use by AnalyzeContinuously * pass --flutter-repo to analysis server when analyzing the flutter repository * enhance flutter analyze --watch to summarize public members lacking documentation
1 parent c186d0d commit e13e780

File tree

4 files changed

+111
-62
lines changed

4 files changed

+111
-62
lines changed

packages/flutter_tools/lib/src/commands/analyze_base.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,25 @@ abstract class AnalyzeBase {
3939
}
4040
}
4141

42+
List<String> flutterRootComponents;
43+
bool isFlutterLibrary(String filename) {
44+
flutterRootComponents ??= fs.path.normalize(fs.path.absolute(Cache.flutterRoot)).split(fs.path.separator);
45+
final List<String> filenameComponents = fs.path.normalize(fs.path.absolute(filename)).split(fs.path.separator);
46+
if (filenameComponents.length < flutterRootComponents.length + 4) // the 4: 'packages', package_name, 'lib', file_name
47+
return false;
48+
for (int index = 0; index < flutterRootComponents.length; index += 1) {
49+
if (flutterRootComponents[index] != filenameComponents[index])
50+
return false;
51+
}
52+
if (filenameComponents[flutterRootComponents.length] != 'packages')
53+
return false;
54+
if (filenameComponents[flutterRootComponents.length + 1] == 'flutter_tools')
55+
return false;
56+
if (filenameComponents[flutterRootComponents.length + 2] != 'lib')
57+
return false;
58+
return true;
59+
}
60+
4261
void writeBenchmark(Stopwatch stopwatch, int errorCount, int membersMissingDocumentation) {
4362
final String benchmarkOut = 'analysis_benchmark.json';
4463
final Map<String, dynamic> data = <String, dynamic>{

packages/flutter_tools/lib/src/commands/analyze_continuously.dart

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,20 @@ class AnalyzeContinuously extends AnalyzeBase {
3131
Stopwatch analysisTimer;
3232
int lastErrorCount = 0;
3333
Status analysisStatus;
34+
bool flutterRepo;
35+
bool showDartDocIssuesIndividually;
3436

3537
@override
3638
Future<Null> analyze() async {
3739
List<String> directories;
3840

39-
if (argResults['dartdocs'])
40-
throwToolExit('The --dartdocs option is currently not supported when using --watch.');
41+
flutterRepo = argResults['flutter-repo'] || inRepo(null);
42+
showDartDocIssuesIndividually = argResults['dartdocs'];
4143

42-
if (argResults['flutter-repo']) {
44+
if (showDartDocIssuesIndividually && !flutterRepo)
45+
throwToolExit('The --dartdocs option is only supported when using --flutter-repo.');
46+
47+
if (flutterRepo) {
4348
final PackageDependencyTracker dependencies = new PackageDependencyTracker();
4449
dependencies.checkForConflictingDependencies(repoPackages, dependencies);
4550
directories = repoPackages.map((Directory dir) => dir.path).toList();
@@ -52,7 +57,7 @@ class AnalyzeContinuously extends AnalyzeBase {
5257
analysisTarget = fs.currentDirectory.path;
5358
}
5459

55-
final AnalysisServer server = new AnalysisServer(dartSdkPath, directories);
60+
final AnalysisServer server = new AnalysisServer(dartSdkPath, directories, flutterRepo: flutterRepo);
5661
server.onAnalyzing.listen((bool isAnalyzing) => _handleAnalysisStatus(server, isAnalyzing));
5762
server.onErrors.listen(_handleAnalysisErrors);
5863

@@ -82,29 +87,52 @@ class AnalyzeContinuously extends AnalyzeBase {
8287
logger.printStatus(terminal.clearScreen(), newline: false);
8388

8489
// Remove errors for deleted files, sort, and print errors.
85-
final List<AnalysisError> errors = <AnalysisError>[];
90+
final List<AnalysisError> allErrors = <AnalysisError>[];
8691
for (String path in analysisErrors.keys.toList()) {
8792
if (fs.isFileSync(path)) {
88-
errors.addAll(analysisErrors[path]);
93+
allErrors.addAll(analysisErrors[path]);
8994
} else {
9095
analysisErrors.remove(path);
9196
}
9297
}
9398

94-
errors.sort();
99+
// Summarize dartdoc issues rather than displaying each individually
100+
int membersMissingDocumentation = 0;
101+
List<AnalysisError> detailErrors;
102+
if (flutterRepo && !showDartDocIssuesIndividually) {
103+
detailErrors = allErrors.where((AnalysisError error) {
104+
if (error.code == 'public_member_api_docs') {
105+
// https://github.com/dart-lang/linter/issues/208
106+
if (isFlutterLibrary(error.file))
107+
membersMissingDocumentation += 1;
108+
return true;
109+
}
110+
return false;
111+
}).toList();
112+
} else {
113+
detailErrors = allErrors;
114+
}
115+
116+
detailErrors.sort();
95117

96-
for (AnalysisError error in errors) {
118+
for (AnalysisError error in detailErrors) {
97119
printStatus(error.toString());
98120
if (error.code != null)
99121
printTrace('error code: ${error.code}');
100122
}
101123

102-
dumpErrors(errors.map<String>((AnalysisError error) => error.toLegacyString()));
124+
dumpErrors(detailErrors.map<String>((AnalysisError error) => error.toLegacyString()));
125+
126+
if (membersMissingDocumentation != 0) {
127+
printStatus(membersMissingDocumentation == 1
128+
? '1 public member lacks documentation'
129+
: '$membersMissingDocumentation public members lack documentation');
130+
}
103131

104132
// Print an analysis summary.
105133
String errorsMessage;
106134

107-
final int issueCount = errors.length;
135+
final int issueCount = detailErrors.length;
108136
final int issueDiff = issueCount - lastErrorCount;
109137
lastErrorCount = issueCount;
110138

@@ -150,10 +178,11 @@ class AnalyzeContinuously extends AnalyzeBase {
150178
}
151179

152180
class AnalysisServer {
153-
AnalysisServer(this.sdk, this.directories);
181+
AnalysisServer(this.sdk, this.directories, { this.flutterRepo: false });
154182

155183
final String sdk;
156184
final List<String> directories;
185+
final bool flutterRepo;
157186

158187
Process _process;
159188
final StreamController<bool> _analyzingController = new StreamController<bool>.broadcast();
@@ -169,6 +198,13 @@ class AnalysisServer {
169198
'--sdk',
170199
sdk,
171200
];
201+
// Let the analysis server know that the flutter repository is being analyzed
202+
// so that it can enable the public_member_api_docs lint even though
203+
// the analysis_options file does not have that lint enabled.
204+
// It is not enabled in the analysis_option file
205+
// because doing so causes too much noise in the IDE.
206+
if (flutterRepo)
207+
command.add('--flutter-repo');
172208

173209
printTrace('dart ${command.skip(1).join(' ')}');
174210
_process = await processManager.start(command);

packages/flutter_tools/lib/src/commands/analyze_once.dart

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -245,25 +245,6 @@ class AnalyzeOnce extends AnalyzeBase {
245245
return dir;
246246
}
247247

248-
List<String> flutterRootComponents;
249-
bool isFlutterLibrary(String filename) {
250-
flutterRootComponents ??= fs.path.normalize(fs.path.absolute(Cache.flutterRoot)).split(fs.path.separator);
251-
final List<String> filenameComponents = fs.path.normalize(fs.path.absolute(filename)).split(fs.path.separator);
252-
if (filenameComponents.length < flutterRootComponents.length + 4) // the 4: 'packages', package_name, 'lib', file_name
253-
return false;
254-
for (int index = 0; index < flutterRootComponents.length; index += 1) {
255-
if (flutterRootComponents[index] != filenameComponents[index])
256-
return false;
257-
}
258-
if (filenameComponents[flutterRootComponents.length] != 'packages')
259-
return false;
260-
if (filenameComponents[flutterRootComponents.length + 1] == 'flutter_tools')
261-
return false;
262-
if (filenameComponents[flutterRootComponents.length + 2] != 'lib')
263-
return false;
264-
return true;
265-
}
266-
267248
List<File> _collectDartFiles(Directory dir, List<File> collected) {
268249
// Bail out in case of a .dartignore.
269250
if (fs.isFileSync(fs.path.join(dir.path, '.dartignore')))

packages/flutter_tools/test/commands/analyze_continuously_test.dart

Lines changed: 45 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -23,51 +23,55 @@ void main() {
2323
tempDir = fs.systemTempDirectory.createTempSync('analysis_test');
2424
});
2525

26+
Future<AnalysisServer> analyzeWithServer({ bool brokenCode: false, bool flutterRepo: false, int expectedErrorCount: 0 }) async {
27+
_createSampleProject(tempDir, brokenCode: brokenCode);
28+
29+
await pubGet(directory: tempDir.path);
30+
31+
server = new AnalysisServer(dartSdkPath, <String>[tempDir.path], flutterRepo: flutterRepo);
32+
33+
final Future<bool> onDone = server.onAnalyzing.where((bool analyzing) => analyzing == false).first;
34+
final List<AnalysisError> errors = <AnalysisError>[];
35+
server.onErrors.listen((FileAnalysisErrors fileErrors) {
36+
errors.addAll(fileErrors.errors);
37+
});
38+
39+
await server.start();
40+
await onDone;
41+
42+
expect(errors, hasLength(expectedErrorCount));
43+
return server;
44+
}
45+
2646
tearDown(() {
2747
tempDir?.deleteSync(recursive: true);
2848
return server?.dispose();
2949
});
3050

3151
group('analyze --watch', () {
32-
testUsingContext('AnalysisServer success', () async {
33-
_createSampleProject(tempDir);
34-
35-
await pubGet(directory: tempDir.path);
36-
37-
server = new AnalysisServer(dartSdkPath, <String>[tempDir.path]);
38-
39-
int errorCount = 0;
40-
final Future<bool> onDone = server.onAnalyzing.where((bool analyzing) => analyzing == false).first;
41-
server.onErrors.listen((FileAnalysisErrors errors) => errorCount += errors.errors.length);
42-
43-
await server.start();
44-
await onDone;
52+
});
4553

46-
expect(errorCount, 0);
54+
group('AnalysisServer', () {
55+
testUsingContext('success', () async {
56+
server = await analyzeWithServer();
4757
}, overrides: <Type, Generator>{
4858
OperatingSystemUtils: () => os
4959
});
50-
});
5160

52-
testUsingContext('AnalysisServer errors', () async {
53-
_createSampleProject(tempDir, brokenCode: true);
54-
55-
await pubGet(directory: tempDir.path);
56-
57-
server = new AnalysisServer(dartSdkPath, <String>[tempDir.path]);
58-
59-
int errorCount = 0;
60-
final Future<bool> onDone = server.onAnalyzing.where((bool analyzing) => analyzing == false).first;
61-
server.onErrors.listen((FileAnalysisErrors errors) {
62-
errorCount += errors.errors.length;
61+
testUsingContext('errors', () async {
62+
server = await analyzeWithServer(brokenCode: true, expectedErrorCount: 1);
63+
}, overrides: <Type, Generator>{
64+
OperatingSystemUtils: () => os
6365
});
6466

65-
await server.start();
66-
await onDone;
67-
68-
expect(errorCount, greaterThan(0));
69-
}, overrides: <Type, Generator>{
70-
OperatingSystemUtils: () => os
67+
testUsingContext('--flutter-repo', () async {
68+
// When a Dart SDK containing support for the --flutter-repo startup flag
69+
// https://github.com/dart-lang/sdk/commit/def1ee6604c4b3385b567cb9832af0dbbaf32e0d
70+
// is rolled into Flutter, then the expectedErrorCount should be set to 1.
71+
server = await analyzeWithServer(flutterRepo: true, expectedErrorCount: 0);
72+
}, overrides: <Type, Generator>{
73+
OperatingSystemUtils: () => os
74+
});
7175
});
7276
}
7377

@@ -77,12 +81,21 @@ void _createSampleProject(Directory directory, { bool brokenCode: false }) {
7781
name: foo_project
7882
''');
7983

84+
final File analysisOptionsFile = fs.file(fs.path.join(directory.path, 'analysis_options.yaml'));
85+
analysisOptionsFile.writeAsStringSync('''
86+
linter:
87+
rules:
88+
- hash_and_equals
89+
''');
90+
8091
final File dartFile = fs.file(fs.path.join(directory.path, 'lib', 'main.dart'));
8192
dartFile.parent.createSync();
8293
dartFile.writeAsStringSync('''
8394
void main() {
8495
print('hello world');
8596
${brokenCode ? 'prints("hello world");' : ''}
8697
}
98+
99+
class SomeClassWithoutDartDoc { }
87100
''');
88101
}

0 commit comments

Comments
 (0)