[messages] Move AbstractAnalysisRule class hierarchy into src.

Moves the following classes from
`package:analyzer/analysis_rule/analysis_rule.dart` into
`package:analyzer/src/analysis_rule/analysis_rule.dart`:

- `AbstractAnalysisRule`
- `AnalysisRule`
- `MultiAnalysisRule`
- `RemovedAnalysisRule`

These classes are part of the analyzer public API, so the file
`package:analyzer/analysis_rule/analysis_rule.dart` remains, exporting
the declarations so that they can still be used by clients.

This paves the way for a follow-up CL that will use extensions to add
functionality that we *don't* want to include in the analyzer public
API yet.

Change-Id: I6a6a696431f00f69b4b80e0d98b1b8fbb33f020f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/481164
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
Paul Berry
2026-02-17 14:35:19 -08:00
committed by Commit Queue
parent 5196ec61ed
commit dfd26d463c
3 changed files with 349 additions and 326 deletions
@@ -2,327 +2,9 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer/analysis_rule/pubspec.dart';
import 'package:analyzer/analysis_rule/rule_context.dart';
import 'package:analyzer/analysis_rule/rule_state.dart';
import 'package:analyzer/analysis_rule/rule_visitor_registry.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/diagnostic/diagnostic.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/lint/pub.dart';
import 'package:pub_semver/pub_semver.dart';
/// Describes a static analysis rule, either a lint rule (which must be enabled
/// via analysis options) or a warning rule (which is enabled by default).
sealed class AbstractAnalysisRule {
/// Used to report lints and warnings.
///
/// NOTE: this is set by the framework before any node processors start
/// visiting nodes.
late DiagnosticReporter _reporter;
/// Short description suitable for display in console output and IDEs.
///
/// This text may be used when providing information about a rule listed in
/// analysis options file, or to suggest enabling a rule.
final String description;
/// The rule name.
final String name;
/// The state of this analysis rule.
///
/// See [RuleState] for a list of the possible states and when they should be
/// applied to a given rule.
final RuleState state;
AbstractAnalysisRule({
required this.name,
required this.description,
this.state = const RuleState.stable(),
});
/// Indicates whether this analysis rule can work with just the parsed
/// information or if it requires a resolved unit.
bool get canUseParsedResult => false;
/// The diagnostic codes associated with this analysis rule.
List<DiagnosticCode> get diagnosticCodes;
/// A list of incompatible rule names.
///
/// Two rules are incompatible if there are situations where it is difficult
/// or impossible to comply with both. The analyzer will warn if two
/// incompatible rules are both enabled in analysis options.
List<String> get incompatibleRules => const [];
/// A visitor that visits a [Pubspec] to perform analysis.
///
/// Diagnostics are reported via this [AbstractAnalysisRule]'s error
/// [reporter].
PubspecVisitor? get pubspecVisitor => null;
/// Sets the [DiagnosticReporter] for the [CompilationUnit] currently being
/// visited.
set reporter(DiagnosticReporter value) => _reporter = value;
/// Registers node processors in the given [registry].
///
/// The node processors may use the provided [context] to access information
/// that is not available from the AST nodes or their associated elements.
void registerNodeProcessors(
RuleVisitorRegistry registry,
RuleContext context,
) {}
Diagnostic? _reportAtNode(
AstNode? node, {
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
required DiagnosticCode diagnosticCode,
}) {
if (node != null && !node.isSynthetic) {
return _reporter.atNode(
node,
diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
}
return null;
}
Diagnostic _reportAtOffset(
int offset,
int length, {
required DiagnosticCode diagnosticCode,
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
}) {
return _reporter.atOffset(
offset: offset,
length: length,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
}
Diagnostic _reportAtPubNode(
PubspecNodeImpl node, {
List<Object> arguments = const [],
List<DiagnosticMessage> contextMessages = const [],
required DiagnosticCode diagnosticCode,
}) {
// Cache diagnostic and location info for creating `AnalysisErrorInfo`s.
var diagnostic = Diagnostic.tmp(
source: node.source,
offset: node.span.start.offset,
length: node.span.length,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
_reporter.reportError(diagnostic);
return diagnostic;
}
Diagnostic? _reportAtToken(
Token token, {
required DiagnosticCode diagnosticCode,
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
}) {
if (!token.isSynthetic) {
return _reporter.atToken(
token,
diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
}
return null;
}
}
/// Describes an [AbstractAnalysisRule] which reports exactly one type of
/// diagnostic (one [DiagnosticCode]).
abstract class AnalysisRule extends AbstractAnalysisRule {
AnalysisRule({required super.name, required super.description, super.state});
/// The code to report for a violation.
DiagnosticCode get diagnosticCode;
@override
List<DiagnosticCode> get diagnosticCodes => [diagnosticCode];
/// Reports a diagnostic at [node] with message [arguments] and
/// [contextMessages].
///
/// {@template analyzer.lib.analysis_rule.analysis_rule.arguments}
/// The [arguments] are interpolated into the [DiagnosticCode.problemMessage]
/// and [DiagnosticCode.correctionMessage] text. If present, the first
/// argument (at position 0) replaces each instance of `{0}`, the second
/// argument (at position 1) replaces each instance of `{1}`, etc.
/// {@endtemplate}
Diagnostic? reportAtNode(
AstNode? node, {
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
}) => _reportAtNode(
node,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
/// Reports a diagnostic at [offset], with [length], with message [arguments]
/// and [contextMessages].
///
/// {@macro analyzer.lib.analysis_rule.analysis_rule.arguments}
Diagnostic reportAtOffset(
int offset,
int length, {
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
}) => _reportAtOffset(
offset,
length,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
/// Reports a diagnostic at Pubspec [node], with message [arguments] and
/// [contextMessages].
///
/// {@macro analyzer.lib.analysis_rule.analysis_rule.arguments}
Diagnostic reportAtPubNode(
PubspecNode node, {
List<Object> arguments = const [],
List<DiagnosticMessage> contextMessages = const [],
}) => _reportAtPubNode(
node as PubspecNodeImpl,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
/// Reports a diagnostic at [token], with message [arguments] and
/// [contextMessages].
///
/// {@macro analyzer.lib.analysis_rule.analysis_rule.arguments}
Diagnostic? reportAtToken(
Token token, {
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
}) => _reportAtToken(
token,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
}
/// Describes an [AbstractAnalysisRule] which reports diagnostics using multiple
/// [DiagnosticCode]s).
abstract class MultiAnalysisRule extends AbstractAnalysisRule {
MultiAnalysisRule({
required super.name,
required super.description,
super.state,
});
/// Reports [diagnosticCode] at [node] with message [arguments] and
/// [contextMessages].
///
/// {@macro analyzer.lib.analysis_rule.analysis_rule.arguments}
Diagnostic? reportAtNode(
AstNode? node, {
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
required DiagnosticCode diagnosticCode,
}) => _reportAtNode(
node,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
/// Reports [diagnosticCode] at [offset], with [length], with message [arguments]
/// and [contextMessages].
///
/// {@macro analyzer.lib.analysis_rule.analysis_rule.arguments}
Diagnostic reportAtOffset(
int offset,
int length, {
required DiagnosticCode diagnosticCode,
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
}) => _reportAtOffset(
offset,
length,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
/// Reports [diagnosticCode] at Pubspec [node], with message [arguments] and
/// [contextMessages].
///
/// {@macro analyzer.lib.analysis_rule.analysis_rule.arguments}
Diagnostic reportAtPubNode(
PubspecNode node, {
required DiagnosticCode diagnosticCode,
List<Object> arguments = const [],
List<DiagnosticMessage> contextMessages = const [],
}) {
// Cache diagnostic and location info for creating `AnalysisErrorInfo`s.
var diagnostic = Diagnostic.tmp(
source: (node as PubspecNodeImpl).source,
offset: node.span.start.offset,
length: node.span.length,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
_reporter.reportError(diagnostic);
return diagnostic;
}
/// Reports [diagnosticCode] at [token], with message [arguments] and
/// [contextMessages].
///
/// {@macro analyzer.lib.analysis_rule.analysis_rule.arguments}
Diagnostic? reportAtToken(
Token token, {
required DiagnosticCode diagnosticCode,
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
}) => _reportAtToken(
token,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
}
/// Describes an [AbstractAnalysisRule] whose implementation has been removed.
final class RemovedAnalysisRule extends MultiAnalysisRule {
RemovedAnalysisRule({
required super.name,
required super.description,
Version? since,
String? replacedBy,
}) : super(
// Note: the reason `RuleState.removed` is deprecated is to encourage
// clients to use `AbstractAnalysisRule`, so this reference is ok.
// ignore: deprecated_member_use_from_same_package
state: RuleState.removed(since: since, replacedBy: replacedBy),
);
@override
List<DiagnosticCode> get diagnosticCodes => const [];
}
export 'package:analyzer/src/analysis_rule/analysis_rule.dart'
show
AbstractAnalysisRule,
AnalysisRule,
MultiAnalysisRule,
RemovedAnalysisRule;
@@ -0,0 +1,341 @@
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:_fe_analyzer_shared/src/base/analyzer_public_api.dart';
import 'package:analyzer/analysis_rule/pubspec.dart';
import 'package:analyzer/analysis_rule/rule_context.dart';
import 'package:analyzer/analysis_rule/rule_state.dart';
import 'package:analyzer/analysis_rule/rule_visitor_registry.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/diagnostic/diagnostic.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/lint/pub.dart';
import 'package:pub_semver/pub_semver.dart';
/// Describes a static analysis rule, either a lint rule (which must be enabled
/// via analysis options) or a warning rule (which is enabled by default).
@AnalyzerPublicApi(
message: 'Exported by package:analyzer/analysis_rule/analysis_rule.dart',
)
sealed class AbstractAnalysisRule {
/// Used to report lints and warnings.
///
/// NOTE: this is set by the framework before any node processors start
/// visiting nodes.
late DiagnosticReporter _reporter;
/// Short description suitable for display in console output and IDEs.
///
/// This text may be used when providing information about a rule listed in
/// analysis options file, or to suggest enabling a rule.
final String description;
/// The rule name.
final String name;
/// The state of this analysis rule.
///
/// See [RuleState] for a list of the possible states and when they should be
/// applied to a given rule.
final RuleState state;
AbstractAnalysisRule({
required this.name,
required this.description,
this.state = const RuleState.stable(),
});
/// Indicates whether this analysis rule can work with just the parsed
/// information or if it requires a resolved unit.
bool get canUseParsedResult => false;
/// The diagnostic codes associated with this analysis rule.
List<DiagnosticCode> get diagnosticCodes;
/// A list of incompatible rule names.
///
/// Two rules are incompatible if there are situations where it is difficult
/// or impossible to comply with both. The analyzer will warn if two
/// incompatible rules are both enabled in analysis options.
List<String> get incompatibleRules => const [];
/// A visitor that visits a [Pubspec] to perform analysis.
///
/// Diagnostics are reported via this [AbstractAnalysisRule]'s error
/// [reporter].
PubspecVisitor? get pubspecVisitor => null;
/// Sets the [DiagnosticReporter] for the [CompilationUnit] currently being
/// visited.
set reporter(DiagnosticReporter value) => _reporter = value;
/// Registers node processors in the given [registry].
///
/// The node processors may use the provided [context] to access information
/// that is not available from the AST nodes or their associated elements.
void registerNodeProcessors(
RuleVisitorRegistry registry,
RuleContext context,
) {}
Diagnostic? _reportAtNode(
AstNode? node, {
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
required DiagnosticCode diagnosticCode,
}) {
if (node != null && !node.isSynthetic) {
return _reporter.atNode(
node,
diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
}
return null;
}
Diagnostic _reportAtOffset(
int offset,
int length, {
required DiagnosticCode diagnosticCode,
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
}) {
return _reporter.atOffset(
offset: offset,
length: length,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
}
Diagnostic _reportAtPubNode(
PubspecNodeImpl node, {
List<Object> arguments = const [],
List<DiagnosticMessage> contextMessages = const [],
required DiagnosticCode diagnosticCode,
}) {
// Cache diagnostic and location info for creating `AnalysisErrorInfo`s.
var diagnostic = Diagnostic.tmp(
source: node.source,
offset: node.span.start.offset,
length: node.span.length,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
_reporter.reportError(diagnostic);
return diagnostic;
}
Diagnostic? _reportAtToken(
Token token, {
required DiagnosticCode diagnosticCode,
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
}) {
if (!token.isSynthetic) {
return _reporter.atToken(
token,
diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
}
return null;
}
}
/// Describes an [AbstractAnalysisRule] which reports exactly one type of
/// diagnostic (one [DiagnosticCode]).
@AnalyzerPublicApi(
message: 'Exported by package:analyzer/analysis_rule/analysis_rule.dart',
)
abstract class AnalysisRule extends AbstractAnalysisRule {
AnalysisRule({required super.name, required super.description, super.state});
/// The code to report for a violation.
DiagnosticCode get diagnosticCode;
@override
List<DiagnosticCode> get diagnosticCodes => [diagnosticCode];
/// Reports a diagnostic at [node] with message [arguments] and
/// [contextMessages].
///
/// {@template analyzer.lib.analysis_rule.analysis_rule.arguments}
/// The [arguments] are interpolated into the [DiagnosticCode.problemMessage]
/// and [DiagnosticCode.correctionMessage] text. If present, the first
/// argument (at position 0) replaces each instance of `{0}`, the second
/// argument (at position 1) replaces each instance of `{1}`, etc.
/// {@endtemplate}
Diagnostic? reportAtNode(
AstNode? node, {
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
}) => _reportAtNode(
node,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
/// Reports a diagnostic at [offset], with [length], with message [arguments]
/// and [contextMessages].
///
/// {@macro analyzer.lib.analysis_rule.analysis_rule.arguments}
Diagnostic reportAtOffset(
int offset,
int length, {
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
}) => _reportAtOffset(
offset,
length,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
/// Reports a diagnostic at Pubspec [node], with message [arguments] and
/// [contextMessages].
///
/// {@macro analyzer.lib.analysis_rule.analysis_rule.arguments}
Diagnostic reportAtPubNode(
PubspecNode node, {
List<Object> arguments = const [],
List<DiagnosticMessage> contextMessages = const [],
}) => _reportAtPubNode(
node as PubspecNodeImpl,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
/// Reports a diagnostic at [token], with message [arguments] and
/// [contextMessages].
///
/// {@macro analyzer.lib.analysis_rule.analysis_rule.arguments}
Diagnostic? reportAtToken(
Token token, {
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
}) => _reportAtToken(
token,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
}
/// Describes an [AbstractAnalysisRule] which reports diagnostics using multiple
/// [DiagnosticCode]s).
@AnalyzerPublicApi(
message: 'Exported by package:analyzer/analysis_rule/analysis_rule.dart',
)
abstract class MultiAnalysisRule extends AbstractAnalysisRule {
MultiAnalysisRule({
required super.name,
required super.description,
super.state,
});
/// Reports [diagnosticCode] at [node] with message [arguments] and
/// [contextMessages].
///
/// {@macro analyzer.lib.analysis_rule.analysis_rule.arguments}
Diagnostic? reportAtNode(
AstNode? node, {
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
required DiagnosticCode diagnosticCode,
}) => _reportAtNode(
node,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
/// Reports [diagnosticCode] at [offset], with [length], with message [arguments]
/// and [contextMessages].
///
/// {@macro analyzer.lib.analysis_rule.analysis_rule.arguments}
Diagnostic reportAtOffset(
int offset,
int length, {
required DiagnosticCode diagnosticCode,
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
}) => _reportAtOffset(
offset,
length,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
/// Reports [diagnosticCode] at Pubspec [node], with message [arguments] and
/// [contextMessages].
///
/// {@macro analyzer.lib.analysis_rule.analysis_rule.arguments}
Diagnostic reportAtPubNode(
PubspecNode node, {
required DiagnosticCode diagnosticCode,
List<Object> arguments = const [],
List<DiagnosticMessage> contextMessages = const [],
}) {
// Cache diagnostic and location info for creating `AnalysisErrorInfo`s.
var diagnostic = Diagnostic.tmp(
source: (node as PubspecNodeImpl).source,
offset: node.span.start.offset,
length: node.span.length,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
_reporter.reportError(diagnostic);
return diagnostic;
}
/// Reports [diagnosticCode] at [token], with message [arguments] and
/// [contextMessages].
///
/// {@macro analyzer.lib.analysis_rule.analysis_rule.arguments}
Diagnostic? reportAtToken(
Token token, {
required DiagnosticCode diagnosticCode,
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
}) => _reportAtToken(
token,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
}
/// Describes an [AbstractAnalysisRule] whose implementation has been removed.
@AnalyzerPublicApi(
message: 'Exported by package:analyzer/analysis_rule/analysis_rule.dart',
)
final class RemovedAnalysisRule extends MultiAnalysisRule {
RemovedAnalysisRule({
required super.name,
required super.description,
Version? since,
String? replacedBy,
}) : super(
// Note: the reason `RuleState.removed` is deprecated is to encourage
// clients to use `AbstractAnalysisRule`, so this reference is ok.
// ignore: deprecated_member_use_from_same_package
state: RuleState.removed(since: since, replacedBy: replacedBy),
);
@override
List<DiagnosticCode> get diagnosticCodes => const [];
}
+2 -2
View File
@@ -92,13 +92,13 @@ dart:core:
package:_fe_analyzer_shared/src/base/errors.dart:
Diagnostic (referenced)
DiagnosticCode (referenced)
package:analyzer/analysis_rule/analysis_rule.dart:
AbstractAnalysisRule (referenced)
package:analyzer/file_system/file_system.dart:
File (referenced)
Folder (referenced)
Link (referenced)
ResourceProvider (referenced)
package:analyzer/src/analysis_rule/analysis_rule.dart:
AbstractAnalysisRule (referenced)
package:analyzer/utilities/package_config_file_builder.dart:
PackageConfigFileBuilder (referenced)
package:path/src/context.dart: