Skip to content

Commit d89d105

Browse files
committed
Merge pull request #102 from travissanderson-wf/fatal_hints
CP-1140 Initial implementation of failing on analyzer hints
2 parents 5d2b6cc + ab28659 commit d89d105

File tree

5 files changed

+74
-12
lines changed

5 files changed

+74
-12
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,12 @@ All configuration options for the `analyze` task are found on the
237237
<td><code>true</code></td>
238238
<td>Show hint results.</td>
239239
</tr>
240+
<tr>
241+
<td><code>fatalHints</code></td>
242+
<td><code>bool</code></td>
243+
<td><code>false</code></td>
244+
<td>Fail on hints (requests hints to be true).</td>
245+
</tr>
240246
</tbody>
241247
</table>
242248

lib/src/tasks/analyze/api.dart

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ import 'package:dart_dev/src/tasks/task.dart';
2525
AnalyzeTask analyze(
2626
{List<String> entryPoints: defaultEntryPoints,
2727
bool fatalWarnings: defaultFatalWarnings,
28-
bool hints: defaultHints}) {
28+
bool hints: defaultHints,
29+
bool fatalHints: defaultFatalHints}) {
2930
var executable = 'dartanalyzer';
3031
var args = [];
3132
if (fatalWarnings) {
@@ -36,14 +37,30 @@ AnalyzeTask analyze(
3637
}
3738
args.addAll(_findFilesFromEntryPoints(entryPoints));
3839

40+
var postProcessCompleter = new Completer();
3941
TaskProcess process = new TaskProcess(executable, args);
40-
AnalyzeTask task =
41-
new AnalyzeTask('$executable ${args.join(' ')}', process.done);
42+
AnalyzeTask task = new AnalyzeTask(
43+
'$executable ${args.join(' ')}', postProcessCompleter.future);
4244

43-
process.stdout.listen(task._analyzerOutput.add);
44-
process.stderr.listen(task._analyzerOutput.addError);
45-
process.exitCode.then((code) {
46-
task.successful = code <= 0;
45+
var matchingLineFuture = task.analyzerOutput
46+
.firstWhere((line) => line.contains('[hint] '), defaultValue: () => null);
47+
48+
var stdoutDone = process.stdout.listen(task._analyzerOutput.add).asFuture();
49+
var stderrDone = process.stderr.listen(task._analyzerOutput.add).asFuture();
50+
Future.wait([stdoutDone, stderrDone])
51+
.then((_) => task._analyzerOutput.close());
52+
53+
matchingLineFuture.then((matchingLine) {
54+
process.exitCode.then((exitCode) async {
55+
if (exitCode > 0) {
56+
task.successful = false;
57+
} else if (fatalHints) {
58+
task.successful = matchingLine == null;
59+
} else {
60+
task.successful = true;
61+
}
62+
postProcessCompleter.complete();
63+
});
4764
});
4865

4966
return task;
@@ -72,7 +89,7 @@ class AnalyzeTask extends Task {
7289
final String analyzerCommand;
7390
final Future done;
7491

75-
StreamController<String> _analyzerOutput = new StreamController();
92+
StreamController<String> _analyzerOutput = new StreamController.broadcast();
7693
Stream<String> get analyzerOutput => _analyzerOutput.stream;
7794
AnalyzeTask(String this.analyzerCommand, Future this.done);
7895
}

lib/src/tasks/analyze/cli.dart

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ class AnalyzeCli extends TaskCli {
3232
negatable: true,
3333
help: 'Treat non-type warnings as fatal.')
3434
..addFlag('hints',
35-
defaultsTo: defaultHints, negatable: true, help: 'Show hint results.');
35+
defaultsTo: defaultHints, negatable: true, help: 'Show hint results.')
36+
..addFlag('fatal-hints',
37+
defaultsTo: defaultFatalHints,
38+
negatable: true,
39+
help: 'Treat hints as fatal.');
3640

3741
final String command = 'analyze';
3842

@@ -41,10 +45,22 @@ class AnalyzeCli extends TaskCli {
4145
bool fatalWarnings = TaskCli.valueOf(
4246
'fatal-warnings', parsedArgs, config.analyze.fatalWarnings);
4347
bool hints = TaskCli.valueOf('hints', parsedArgs, config.analyze.hints);
48+
bool fatalHints =
49+
TaskCli.valueOf('fatal-hints', parsedArgs, config.analyze.fatalHints);
50+
51+
if (!hints && fatalHints) {
52+
return new CliResult.fail('You must enable hints to fail on hints.');
53+
}
4454

4555
AnalyzeTask task = analyze(
46-
entryPoints: entryPoints, fatalWarnings: fatalWarnings, hints: hints);
47-
reporter.logGroup(task.analyzerCommand, outputStream: task.analyzerOutput);
56+
entryPoints: entryPoints,
57+
fatalWarnings: fatalWarnings,
58+
hints: hints,
59+
fatalHints: fatalHints);
60+
var title = task.analyzerCommand;
61+
if (fatalHints) title += ' (treating hints as fatal)';
62+
63+
reporter.logGroup(title, outputStream: task.analyzerOutput);
4864
await task.done;
4965
return task.successful
5066
? new CliResult.success('Analysis completed.')

lib/src/tasks/analyze/config.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,11 @@ import 'package:dart_dev/src/tasks/config.dart';
1919
const List<String> defaultEntryPoints = const ['lib/'];
2020
const bool defaultFatalWarnings = true;
2121
const bool defaultHints = true;
22+
const bool defaultFatalHints = false;
2223

2324
class AnalyzeConfig extends TaskConfig {
2425
List<String> entryPoints = defaultEntryPoints.toList();
2526
bool fatalWarnings = defaultFatalWarnings;
2627
bool hints = defaultHints;
28+
bool fatalHints = defaultFatalHints;
2729
}

test/integration/analyze_test.dart

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,16 @@ const String projectWithHints = 'test/fixtures/analyze/hints';
2626
const String projectWithNoIssues = 'test/fixtures/analyze/no_issues';
2727

2828
Future<Analysis> analyzeProject(String projectPath,
29-
{bool fatalWarnings: true, bool hints: true}) async {
29+
{bool fatalWarnings: true,
30+
bool hints: true,
31+
bool fatalHints: false}) async {
3032
await Process.run('pub', ['get'], workingDirectory: projectPath);
3133

3234
var args = ['run', 'dart_dev', 'analyze'];
3335
args.add(fatalWarnings ? '--fatal-warnings' : '--no-fatal-warnings');
3436
args.add(hints ? '--hints' : '--no-hints');
37+
args.add(fatalHints ? '--fatal-hints' : ' --no-fatal-hints');
38+
3539
TaskProcess process =
3640
new TaskProcess('pub', args, workingDirectory: projectPath);
3741

@@ -92,16 +96,33 @@ void main() {
9296
test('should report no issues found', () async {
9397
Analysis analysis = await analyzeProject(projectWithNoIssues);
9498
expect(analysis.noIssues, isTrue);
99+
expect(analysis.exitCode, equals(0));
95100
});
96101

97102
test('should report hints if configured to do so', () async {
98103
Analysis analysis = await analyzeProject(projectWithHints);
99104
expect(analysis.numHints, equals(2));
105+
expect(analysis.exitCode, equals(0));
100106
});
101107

102108
test('should not report hints if configured to ignore them', () async {
103109
Analysis analysis = await analyzeProject(projectWithHints, hints: false);
104110
expect(analysis.numHints, equals(0));
111+
expect(analysis.exitCode, equals(0));
112+
});
113+
114+
test('should report hints as fatal if configured to do so', () async {
115+
Analysis analysis =
116+
await analyzeProject(projectWithHints, fatalHints: true);
117+
expect(analysis.numHints, equals(2));
118+
expect(analysis.exitCode, greaterThan(0));
119+
});
120+
121+
test('should not report hints as fatal if none existed', () async {
122+
Analysis analysis =
123+
await analyzeProject(projectWithNoIssues, fatalHints: true);
124+
expect(analysis.numHints, equals(0));
125+
expect(analysis.exitCode, equals(0));
105126
});
106127

107128
test('should report warnings as fatal if configured to do so', () async {

0 commit comments

Comments
 (0)