Skip to content

Commit c1c6225

Browse files
srawlinsCommit Queue
authored andcommitted
DAS plugins: Support configurable severity
This introduces a concept of a "configured severity," based on the design at #57034, such that an analysis rule can be configured in analysis options to be one of: "disable," "enable," "info," "warning," or "error." We actually don't have any validation of what is inside a plugin section in analysis options; that should come next. Closes #59644 Change-Id: I56f1f51ef01adee7e6c042d8df9a974e7e5a11a8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/443003 Reviewed-by: Konstantin Shcheglov <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Samuel Rawlins <[email protected]>
1 parent 0ffd284 commit c1c6225

File tree

4 files changed

+95
-32
lines changed

4 files changed

+95
-32
lines changed

pkg/analysis_server_plugin/lib/src/plugin_server.dart

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@ import 'package:analysis_server_plugin/src/registry.dart';
1616
import 'package:analyzer/analysis_rule/rule_context.dart';
1717
import 'package:analyzer/dart/analysis/analysis_context.dart';
1818
import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
19+
import 'package:analyzer/dart/analysis/analysis_options.dart';
1920
import 'package:analyzer/dart/analysis/results.dart';
2021
import 'package:analyzer/dart/analysis/session.dart';
2122
import 'package:analyzer/dart/ast/ast.dart';
2223
import 'package:analyzer/diagnostic/diagnostic.dart';
24+
import 'package:analyzer/error/error.dart';
2325
import 'package:analyzer/error/listener.dart';
2426
import 'package:analyzer/file_system/file_system.dart';
2527
import 'package:analyzer/file_system/overlay_file_system.dart';
@@ -30,6 +32,7 @@ import 'package:analyzer/src/dart/analysis/byte_store.dart';
3032
import 'package:analyzer/src/dart/analysis/file_content_cache.dart';
3133
import 'package:analyzer/src/dart/element/type_system.dart';
3234
import 'package:analyzer/src/ignore_comments/ignore_info.dart';
35+
import 'package:analyzer/src/lint/config.dart';
3336
import 'package:analyzer/src/lint/linter.dart';
3437
import 'package:analyzer/src/lint/linter_visitor.dart';
3538
import 'package:analyzer/src/lint/registry.dart';
@@ -351,8 +354,11 @@ class PluginServer {
351354
null,
352355
);
353356

354-
// A mapping from each lint and warning code to its corresponding plugin.
355-
var pluginCodeMapping = <String, String>{};
357+
// A mapping from each diagnostic code to its corresponding plugin.
358+
var pluginCodeMapping = <DiagnosticCode, String>{};
359+
360+
// A mapping from each diagnostic code to its configured severity.
361+
var severityMapping = <DiagnosticCode, protocol.AnalysisErrorSeverity?>{};
356362

357363
for (var configuration in analysisOptions.pluginConfigurations) {
358364
if (!configuration.isEnabled) continue;
@@ -362,14 +368,13 @@ class PluginServer {
362368
for (var rule in rules) {
363369
rule.reporter = diagnosticReporter;
364370
// TODO(srawlins): Enable timing similar to what the linter package's
365-
// `benchhmark.dart` script does.
371+
// `benchmark.dart` script does.
366372
rule.registerNodeProcessors(nodeRegistry, context);
367373
}
368374
for (var code in rules.expand((r) => r.diagnosticCodes)) {
369-
var existingPlugin = pluginCodeMapping[code.name];
370-
if (existingPlugin == null) {
371-
pluginCodeMapping[code.name] = configuration.name;
372-
}
375+
pluginCodeMapping.putIfAbsent(code, () => configuration.name);
376+
severityMapping.putIfAbsent(
377+
code, () => _configuredSeverity(configuration, code));
373378
}
374379
}
375380

@@ -379,7 +384,7 @@ class PluginServer {
379384

380385
var ignoreInfo = IgnoreInfo.forDart(unitResult.unit, unitResult.content);
381386
var diagnostics = listener.diagnostics.where((e) {
382-
var pluginName = pluginCodeMapping[e.diagnosticCode.name];
387+
var pluginName = pluginCodeMapping[e.diagnosticCode];
383388
if (pluginName == null) {
384389
// If [e] is somehow not mapped, something is wrong; but don't mark it
385390
// as ignored.
@@ -395,7 +400,8 @@ class PluginServer {
395400
(
396401
diagnostic: diagnostic,
397402
protocolError: protocol.AnalysisError(
398-
_severityOf(diagnostic),
403+
severityMapping[diagnostic.diagnosticCode] ??
404+
protocol.AnalysisErrorSeverity.INFO,
399405
protocol.AnalysisErrorType.STATIC_WARNING,
400406
_locationFor(currentUnit.unit, path, diagnostic),
401407
diagnostic.message,
@@ -413,17 +419,27 @@ class PluginServer {
413419
return diagnosticsAndProtocolErrors.map((e) => e.protocolError).toList();
414420
}
415421

416-
/// Converts the severity of [diagnostic] into a
417-
/// [protocol.AnalysisErrorSeverity].
418-
protocol.AnalysisErrorSeverity _severityOf(Diagnostic diagnostic) {
419-
try {
420-
return protocol.AnalysisErrorSeverity.values
421-
.byName(diagnostic.severity.name.toUpperCase());
422-
} catch (_) {
423-
assert(false, 'Invalid severity: ${diagnostic.severity}');
424-
// Return the default severity of `LintCode`.
425-
return protocol.AnalysisErrorSeverity.INFO;
422+
/// Converts the severity of [code] into a [protocol.AnalysisErrorSeverity].
423+
protocol.AnalysisErrorSeverity? _configuredSeverity(
424+
PluginConfiguration configuration, DiagnosticCode code) {
425+
var configuredSeverity =
426+
configuration.diagnosticConfigs[code.name]?.severity;
427+
if (configuredSeverity != null &&
428+
configuredSeverity != ConfiguredSeverity.enable) {
429+
var severityName = configuredSeverity.name.toUpperCase();
430+
var severity =
431+
protocol.AnalysisErrorSeverity.values.asNameMap()[severityName];
432+
assert(severity != null,
433+
'Invalid configured severity: ${configuredSeverity.name}');
434+
return severity;
426435
}
436+
437+
// Fall back to the declared severity of [code].
438+
var severityName = code.severity.name.toUpperCase();
439+
var severity =
440+
protocol.AnalysisErrorSeverity.values.asNameMap()[code.severity.name];
441+
assert(severity != null, 'Invalid severity: $severityName');
442+
return severity;
427443
}
428444

429445
/// Invokes [fn] first for priority analysis contexts, then for the rest.

pkg/analysis_server_plugin/test/src/plugin_server_test.dart

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ bool b = false;
146146
}
147147

148148
Future<void> test_lintCodesCanHaveCustomSeverity() async {
149-
writeAnalysisOptionsWithPlugin({'no_doubles_warning': true});
149+
writeAnalysisOptionsWithPlugin({'no_doubles_warning': 'enable'});
150150
newFile(filePath, 'double x = 3.14;');
151151
await channel
152152
.sendRequest(protocol.AnalysisSetContextRootsParams([contextRoot]));
@@ -160,6 +160,21 @@ bool b = false;
160160
);
161161
}
162162

163+
Future<void> test_lintCodesCanHaveConfigurableSeverity() async {
164+
writeAnalysisOptionsWithPlugin({'no_doubles_warning': 'error'});
165+
newFile(filePath, 'double x = 3.14;');
166+
await channel
167+
.sendRequest(protocol.AnalysisSetContextRootsParams([contextRoot]));
168+
var paramsQueue = _analysisErrorsParams;
169+
var params = await paramsQueue.next;
170+
expect(params.errors, hasLength(1));
171+
_expectAnalysisError(
172+
params.errors.single,
173+
message: 'No doubles message',
174+
severity: protocol.AnalysisErrorSeverity.ERROR,
175+
);
176+
}
177+
163178
Future<void> test_lintRulesAreDisabledByDefault() async {
164179
writeAnalysisOptionsWithPlugin();
165180
newFile(filePath, 'double x = 3.14;');
@@ -171,7 +186,7 @@ bool b = false;
171186
}
172187

173188
Future<void> test_lintRulesCanBeEnabled() async {
174-
writeAnalysisOptionsWithPlugin({'no_doubles': true});
189+
writeAnalysisOptionsWithPlugin({'no_doubles': 'enable'});
175190
newFile(filePath, 'double x = 3.14;');
176191
await channel
177192
.sendRequest(protocol.AnalysisSetContextRootsParams([contextRoot]));
@@ -276,7 +291,7 @@ bool b = false;
276291
}
277292

278293
Future<void> test_warningRulesCanBeDisabled() async {
279-
writeAnalysisOptionsWithPlugin({'no_bools': false});
294+
writeAnalysisOptionsWithPlugin({'no_bools': 'disable'});
280295
newFile(filePath, 'bool b = false;');
281296
await channel
282297
.sendRequest(protocol.AnalysisSetContextRootsParams([contextRoot]));
@@ -286,16 +301,16 @@ bool b = false;
286301
}
287302

288303
void writeAnalysisOptionsWithPlugin(
289-
[Map<String, bool> diagnosticConfiguration = const {}]) {
304+
[Map<String, String> diagnosticConfiguration = const {}]) {
290305
var buffer = StringBuffer('''
291306
plugins:
292307
no_literals:
293308
path: some/path
294309
diagnostics:
295310
''');
296-
for (var MapEntry(key: diagnosticName, value: isEnabled)
311+
for (var MapEntry(key: diagnosticName, value: enablement)
297312
in diagnosticConfiguration.entries) {
298-
buffer.writeln(' $diagnosticName: $isEnabled');
313+
buffer.writeln(' $diagnosticName: $enablement');
299314
}
300315
newAnalysisOptionsYamlFile(packagePath, buffer.toString());
301316
}

pkg/analyzer/lib/src/lint/config.dart

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ Map<String, RuleConfig> parseDiagnosticsSection(YamlNode value) {
1919
return {
2020
for (var ruleNode in value.nodes)
2121
if (ruleNode case YamlScalar(value: String ruleName))
22-
ruleName: RuleConfig._(name: ruleName, isEnabled: true),
22+
ruleName: RuleConfig._(
23+
name: ruleName,
24+
severity: ConfiguredSeverity.enable,
25+
),
2326
};
2427
}
2528

@@ -81,7 +84,14 @@ RuleConfig? _parseRuleConfig(
8184
// For example: `{unnecessary_getters: false}`.
8285
if (configKey case YamlScalar(value: String ruleName)) {
8386
if (configNode case YamlScalar(value: bool isEnabled)) {
84-
return RuleConfig._(name: ruleName, isEnabled: isEnabled, group: group);
87+
var severity =
88+
isEnabled ? ConfiguredSeverity.enable : ConfiguredSeverity.disable;
89+
return RuleConfig._(name: ruleName, group: group, severity: severity);
90+
} else if (configNode case YamlScalar(value: String severityString)) {
91+
var severity =
92+
ConfiguredSeverity.values.asNameMap()[severityString] ??
93+
ConfiguredSeverity.enable;
94+
return RuleConfig._(name: ruleName, group: group, severity: severity);
8595
}
8696
}
8797

@@ -97,18 +107,39 @@ RuleConfig? _parseRuleConfig(
97107
/// via the name of the lint rule that reports the diagnostic.)
98108
typedef DiagnosticConfig = RuleConfig;
99109

110+
/// The possible values for an analysis rule's configured severity.
111+
enum ConfiguredSeverity {
112+
/// A severity indicating the rule is simply disabled.
113+
disable,
114+
115+
/// A severity indicating the rule is enabled with its default severity.
116+
enable,
117+
118+
/// A severity indicating the rule is enabled with the 'info' severity.
119+
info,
120+
121+
/// A severity indicating the rule is enabled with the 'warning' severity.
122+
warning,
123+
124+
/// A severity indicating the rule is enabled with the 'error' severity.
125+
error,
126+
}
127+
100128
/// The configuration of a single analysis rule within an analysis options file.
101129
class RuleConfig {
102-
/// Whether this rule is enabled or disabled in this configuration.
103-
final bool isEnabled;
104-
105130
/// The name of the group under which this configuration is found.
106131
final String? group;
107132

108133
/// The name of the rule.
109134
final String name;
110135

111-
RuleConfig._({required this.name, required this.isEnabled, this.group});
136+
/// The rule's severity in this configuration.
137+
final ConfiguredSeverity severity;
138+
139+
RuleConfig._({required this.name, this.group, required this.severity});
140+
141+
/// Whether this rule is enabled or disabled in this configuration.
142+
bool get isEnabled => severity != ConfiguredSeverity.disable;
112143

113144
/// Returns whether [ruleName] is disabled in this configuration.
114145
bool disables(String ruleName) => ruleName == name && !isEnabled;

pkg/analyzer/lib/src/lint/registry.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ class Registry with IterableMixin<AbstractAnalysisRule> {
1313
/// The default registry to be used by clients.
1414
static final Registry ruleRegistry = Registry();
1515

16-
/// A table mapping rule names to rules.
16+
/// A table mapping lint rule names to rules.
1717
final Map<String, AbstractAnalysisRule> _lintRules = {};
1818

19+
/// A table mapping warning rule names to rules.
1920
final Map<String, AbstractAnalysisRule> _warningRules = {};
2021

2122
/// A table mapping unique names to lint codes.

0 commit comments

Comments
 (0)