Skip to content

Commit 58ea216

Browse files
Merge pull request #15 from Workiva/clean-up-diagnostics
Clean up diagnostics code organization
2 parents 162132e + 1538341 commit 58ea216

22 files changed

+256
-249
lines changed

over_react_analyzer_plugin/lib/src/assist/add_props.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import 'package:analyzer_plugin/protocol/protocol_generated.dart';
22
import 'package:analyzer_plugin/utilities/assist/assist.dart';
33
import 'package:over_react_analyzer_plugin/src/assist/contributor_base.dart';
44
import 'package:over_react_analyzer_plugin/src/component_usage.dart';
5-
import 'package:over_react_analyzer_plugin/src/diagnostic/component_usage.dart';
5+
import 'package:over_react_analyzer_plugin/src/diagnostic_contributor.dart';
66
import 'package:over_react_analyzer_plugin/src/fluent_interface_util.dart';
77

88
class AddPropsAssistContributor extends AssistContributorBase {

over_react_analyzer_plugin/lib/src/async_plugin_apis/diagnostic.dart

Lines changed: 104 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,19 @@
3333
import 'dart:async';
3434

3535
import 'package:analyzer/dart/analysis/results.dart';
36-
import 'package:analyzer/error/error.dart';
36+
import 'package:analyzer_plugin/channel/channel.dart';
3737
import 'package:analyzer_plugin/plugin/plugin.dart';
38+
import 'package:analyzer_plugin/protocol/protocol.dart';
3839
import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
40+
import 'package:analyzer_plugin/protocol/protocol_common.dart';
3941
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
4042
import 'package:analyzer_plugin/protocol/protocol_generated.dart';
4143
// ignore: implementation_imports
4244
import 'package:analyzer_plugin/src/utilities/fixes/fixes.dart';
4345
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
44-
import 'package:over_react_analyzer_plugin/src/diagnostic/component_usage.dart';
46+
import 'package:meta/meta.dart';
47+
import 'package:over_react_analyzer_plugin/src/diagnostic_contributor.dart';
48+
import 'package:over_react_analyzer_plugin/src/error_filtering.dart';
4549

4650
mixin DiagnosticMixin on ServerPlugin {
4751
List<DiagnosticContributor> getDiagnosticContributors(String path);
@@ -56,7 +60,7 @@ mixin DiagnosticMixin on ServerPlugin {
5660
// If there is something to analyze, do so and notify the analyzer.
5761
// Note that notifying with an empty set of errors is important as
5862
// this clears errors if they were fixed.
59-
final generator = DiagnosticGenerator(getDiagnosticContributors(analysisResult.path));
63+
final generator = _DiagnosticGenerator(getDiagnosticContributors(analysisResult.path));
6064
final result = await generator.generateErrors(analysisResult);
6165
channel.sendNotification(plugin.AnalysisErrorsParams(analysisResult.path, result.result).toNotification());
6266
result.sendNotifications(channel);
@@ -72,7 +76,7 @@ mixin DiagnosticMixin on ServerPlugin {
7276
// We want request errors to propagate if they throw
7377
final request = await _getFixesRequest(parameters);
7478
try {
75-
final generator = DiagnosticGenerator(getDiagnosticContributors(parameters.file));
79+
final generator = _DiagnosticGenerator(getDiagnosticContributors(parameters.file));
7680
final result = await generator.generateFixesResponse(request);
7781
result.sendNotifications(channel);
7882
return result.result;
@@ -103,3 +107,99 @@ mixin DiagnosticMixin on ServerPlugin {
103107
// return result.errors;
104108
// }
105109
}
110+
111+
/// A class that generates errors and fixes for a set of [contributors] for
112+
/// a given result unit or fixes request.
113+
@sealed
114+
class _DiagnosticGenerator {
115+
/// Initialize a newly created errors generator to use the given
116+
/// [contributors].
117+
_DiagnosticGenerator(this.contributors);
118+
119+
/// The contributors to be used to generate the errors.
120+
final List<DiagnosticContributor> contributors;
121+
122+
/// Creates a 'analysis.errors' response for the the file specified
123+
/// by the given [unitResult]. If any of the contributors throws an exception,
124+
/// also create a non-fatal 'plugin.error' notification.
125+
Future<_GeneratorResult<List<AnalysisError>>> generateErrors(ResolvedUnitResult unitResult) async {
126+
return _generateErrors(unitResult, DiagnosticCollectorImpl(shouldComputeFixes: false));
127+
}
128+
129+
/// Creates an 'edit.getFixes' response for the location in the file specified
130+
/// by the given [request]. If any of the contributors throws an exception,
131+
/// also create a non-fatal 'plugin.error' notification.
132+
Future<_GeneratorResult<EditGetFixesResult>> generateFixesResponse(DartFixesRequest request) async {
133+
// Recompute the errors and then emit the matching fixes
134+
135+
final collector = DiagnosticCollectorImpl(shouldComputeFixes: true);
136+
final errorsResult = await _generateErrors(request.result, collector);
137+
final notifications = [...errorsResult.notifications];
138+
139+
// Return any fixes that contain the given offset.
140+
// TODO use request.errorsToFix instead?
141+
final fixes = <AnalysisErrorFixes>[];
142+
for (var i = 0; i < collector.errors.length; i++) {
143+
final error = collector.errors[i];
144+
final errorStart = error.location.offset;
145+
final errorEnd = errorStart + error.location.length;
146+
147+
// `<=` because we do want the end to be inclusive (you should get
148+
// the fix when your cursor is on the tail end of the error).
149+
if (request.offset >= errorStart && request.offset <= errorEnd) {
150+
fixes.add(AnalysisErrorFixes(
151+
error,
152+
fixes: [collector.fixes[i]],
153+
));
154+
}
155+
}
156+
157+
return _GeneratorResult(EditGetFixesResult(fixes), notifications);
158+
}
159+
160+
Future<_GeneratorResult<List<AnalysisError>>> _generateErrors(
161+
ResolvedUnitResult unitResult, DiagnosticCollectorImpl collector) async {
162+
final notifications = <Notification>[];
163+
for (final contributor in contributors) {
164+
try {
165+
await contributor.computeErrors(unitResult, collector);
166+
} catch (exception, stackTrace) {
167+
notifications.add(PluginErrorParams(false, exception.toString(), stackTrace.toString()).toNotification());
168+
}
169+
}
170+
171+
// The analyzer normally filters out errors with "ignore" comments,
172+
// but it doesn't do it for plugin errors, so we need to do that here.
173+
final lineInfo = unitResult.unit.lineInfo;
174+
final filteredErrors =
175+
filterIgnores(collector.errors, lineInfo, () => IgnoreInfo.calculateIgnores(unitResult.content, lineInfo));
176+
177+
return _GeneratorResult(filteredErrors, notifications);
178+
}
179+
}
180+
181+
/// The result produced by a generator.
182+
///
183+
/// Adapted from `GeneratorResult` in analyzer_plugin, but with less restrictive typing.
184+
@sealed
185+
class _GeneratorResult<T> {
186+
/// The result to be sent to the server, or `null` if there is no response, as
187+
/// when the generator is generating a notification.
188+
final T result;
189+
190+
/// The notifications that should be sent to the server. The list will be empty
191+
/// if there are no notifications.
192+
final List<Notification> notifications;
193+
194+
/// Initialize a newly created generator result with the given [result] and
195+
/// [notifications].
196+
_GeneratorResult(this.result, this.notifications);
197+
198+
/// Use the given communications [channel] to send the notifications to the
199+
/// server.
200+
void sendNotifications(PluginCommunicationChannel channel) {
201+
for (final notification in notifications) {
202+
channel.sendNotification(notification);
203+
}
204+
}
205+
}

over_react_analyzer_plugin/lib/src/component_usage.dart

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,3 +167,55 @@ FluentComponentUsage identifyUsage(AstNode node) {
167167
}
168168
return null;
169169
}
170+
171+
class ComponentUsageVisitor extends RecursiveAstVisitor<void> {
172+
ComponentUsageVisitor(this.onComponent);
173+
174+
final void Function(FluentComponentUsage) onComponent;
175+
176+
@override
177+
void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
178+
visitInvocationExpression(node);
179+
}
180+
181+
@override
182+
void visitMethodInvocation(MethodInvocation node) {
183+
visitInvocationExpression(node);
184+
}
185+
186+
void visitInvocationExpression(InvocationExpression node) {
187+
var usage = getComponentUsage(node);
188+
if (usage != null) {
189+
onComponent(usage);
190+
}
191+
192+
node.visitChildren(this);
193+
}
194+
}
195+
196+
//
197+
//class ComponentUsageElementVisitor extends RecursiveElementVisitor<void> {
198+
// final _OnComponent onComponent;
199+
//
200+
// ComponentUsageElementVisitor(this.onComponent);
201+
//
202+
// @override
203+
// void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
204+
// return visitInvocationExpression(node);
205+
// }
206+
//
207+
// @override
208+
// void visitMethodInvocation(MethodInvocation node) {
209+
// return visitInvocationExpression(node);
210+
// }
211+
//
212+
// void visitInvocationExpression(InvocationExpression node) {
213+
// var usage = getComponentUsage(node);
214+
// if (usage != null) {
215+
// onComponent(usage);
216+
// }
217+
//
218+
// node.visitChildren(this);
219+
// return null;
220+
// }
221+
//}

over_react_analyzer_plugin/lib/src/diagnostic/analyzer_debug_helper.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import 'package:analyzer/dart/analysis/results.dart';
22
import 'package:analyzer_plugin/protocol/protocol_common.dart';
3-
import 'package:over_react_analyzer_plugin/src/diagnostic/component_usage.dart';
3+
import 'package:over_react_analyzer_plugin/src/diagnostic_contributor.dart';
44

55
/// Usage:
66
/// AnalyzerDebugHelper debug = new AnalyzerDebugHelper(result, collector);
@@ -9,7 +9,7 @@ class AnalyzerDebugHelper {
99

1010
ResolvedUnitResult result;
1111
DiagnosticCollector collector;
12-
static const code = ErrorCode(
12+
static const code = DiagnosticCode(
1313
'over_react_debug_analyzer_plugin_helper',
1414
"{0}",
1515
AnalysisErrorSeverity.INFO,

over_react_analyzer_plugin/lib/src/diagnostic/arrow_function_prop.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import 'package:analyzer/dart/ast/ast.dart';
22
import 'package:analyzer_plugin/protocol/protocol_common.dart';
33
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
4-
import 'package:over_react_analyzer_plugin/src/diagnostic/component_usage.dart';
4+
import 'package:over_react_analyzer_plugin/src/diagnostic_contributor.dart';
55
import 'package:over_react_analyzer_plugin/src/fluent_interface_util.dart';
66

77
class ArrowFunctionPropCascadeDiagnostic extends ComponentUsageDiagnosticContributor {
8-
static final code = ErrorCode(
8+
static final code = DiagnosticCode(
99
'over_react_cascaded_arrow_functions',
1010
'Unparenthesized arrow function values prevent subsequent cascades',
1111
AnalysisErrorSeverity.WARNING,

over_react_analyzer_plugin/lib/src/diagnostic/bool_prop_name_readability.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import 'package:analyzer/dart/ast/ast.dart';
22
import 'package:analyzer/dart/ast/visitor.dart';
3-
import 'package:over_react_analyzer_plugin/src/diagnostic/component_usage.dart';
3+
import 'package:over_react_analyzer_plugin/src/diagnostic_contributor.dart';
44

55
class BoolPropNameReadabilityDiagnostic extends DiagnosticContributor {
6-
static const code = ErrorCode(
6+
static const code = DiagnosticCode(
77
'over_react_bool_prop_name_readability',
88
"'{0}.{1}' isn't an easily readable Boolean prop name. Try using a prefix like: {2}",
99
AnalysisErrorSeverity.INFO,

over_react_analyzer_plugin/lib/src/diagnostic/dom_prop_types.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import 'package:over_react_analyzer_plugin/src/diagnostic/component_usage.dart';
1+
import 'package:over_react_analyzer_plugin/src/diagnostic_contributor.dart';
22
import 'package:over_react_analyzer_plugin/src/fluent_interface_util.dart';
33

44
/// A diagnostic that warns when an HTML attribute set on an OverReact `Dom` component builder is invalid
55
/// based on the `<attribute>: [<allowed_html_elems>]` schema found within [allowedHtmlElementsForAttribute].
66
class InvalidDomAttributeDiagnostic extends ComponentUsageDiagnosticContributor {
7-
static const code = ErrorCode(
7+
static const code = DiagnosticCode(
88
'over_react_invalid_dom_attribute',
99
"'{0}' isn't a valid HTML attribute prop for '{1}'. It may only be used on: {2}",
1010
AnalysisErrorSeverity.WARNING,

over_react_analyzer_plugin/lib/src/diagnostic/duplicate_prop_cascade.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import 'package:analyzer/dart/ast/ast.dart';
22
import 'package:analyzer_plugin/utilities/pair.dart';
3-
import 'package:over_react_analyzer_plugin/src/diagnostic/component_usage.dart';
3+
import 'package:over_react_analyzer_plugin/src/diagnostic_contributor.dart';
44
import 'package:over_react_analyzer_plugin/src/fluent_interface_util.dart';
55

66
class DuplicatePropCascadeDiagnostic extends ComponentUsageDiagnosticContributor {
7-
static final code = ErrorCode(
7+
static final code = DiagnosticCode(
88
'over_react_duplicate_prop_cascade',
99
"Prop '{0}' is set more than once ({1} of {2}). This is most likely a typo.",
1010
AnalysisErrorSeverity.WARNING,

over_react_analyzer_plugin/lib/src/diagnostic/hashcode_as_key.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
import 'package:over_react_analyzer_plugin/src/diagnostic/component_usage.dart';
1+
import 'package:over_react_analyzer_plugin/src/diagnostic_contributor.dart';
22
import 'package:over_react_analyzer_plugin/src/fluent_interface_util.dart';
33

44
class HashCodeAsKeyDiagnostic extends ComponentUsageDiagnosticContributor {
5-
static final code = ErrorCode(
5+
static final code = DiagnosticCode(
66
'over_react_missing_casecade_parens',
77
"React keys should not be derived from 'hashCode' since it is not unique",
88
AnalysisErrorSeverity.WARNING,

over_react_analyzer_plugin/lib/src/diagnostic/invalid_child.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ import 'package:analyzer/dart/element/type.dart';
55
import 'package:analyzer/src/generated/type_system.dart' show TypeSystem;
66
import 'package:analyzer_plugin/protocol/protocol_common.dart';
77
import 'package:meta/meta.dart';
8-
import 'package:over_react_analyzer_plugin/src/diagnostic/component_usage.dart';
8+
import 'package:over_react_analyzer_plugin/src/diagnostic_contributor.dart';
99
import 'package:over_react_analyzer_plugin/src/fluent_interface_util.dart';
1010

1111
class InvalidChildDiagnostic extends ComponentUsageDiagnosticContributor {
12-
static final code = ErrorCode(
12+
static final code = DiagnosticCode(
1313
'over_react_invalid_child',
1414
"Invalid child type: '{0}'. Must be a ReactElement, Fragment, string, number, boolean, null, or an Iterable of those types.{1}",
1515
AnalysisErrorSeverity.WARNING,

0 commit comments

Comments
 (0)