blob: 094cfe696cb5b8279a2577ce0f2e1ad7c4b4fb1e [file] [log] [blame]
// Copyright (c) 2015, 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: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';
/// 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,
) {}
void _reportAtNode(
AstNode? node, {
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
required DiagnosticCode diagnosticCode,
}) {
if (node != null && !node.isSynthetic) {
_reporter.atNode(
node,
diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
}
}
void _reportAtOffset(
int offset,
int length, {
required DiagnosticCode diagnosticCode,
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
}) {
_reporter.atOffset(
offset: offset,
length: length,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
}
void _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);
}
void _reportAtToken(
Token token, {
required DiagnosticCode diagnosticCode,
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
}) {
if (!token.isSynthetic) {
_reporter.atToken(
token,
diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
}
}
}
/// 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].
void 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].
void 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].
void 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].
void 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].
void 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].
void 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].
void reportAtPubNode(
PubspecNode node, {
required DiagnosticCode diagnosticCode,
List<Object> arguments = const [],
List<DiagnosticMessage> contextMessages = const [],
}) {
// Cache error and location info for creating `AnalysisErrorInfo`s.
var error = Diagnostic.tmp(
source: (node as PubspecNodeImpl).source,
offset: node.span.start.offset,
length: node.span.length,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
_reporter.reportError(error);
}
/// Reports [diagnosticCode] at [token], with message [arguments] and
/// [contextMessages].
void reportAtToken(
Token token, {
required DiagnosticCode diagnosticCode,
List<Object> arguments = const [],
List<DiagnosticMessage>? contextMessages,
}) => _reportAtToken(
token,
diagnosticCode: diagnosticCode,
arguments: arguments,
contextMessages: contextMessages,
);
}