// 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 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 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 arguments = const [], List? 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 arguments = const [], List? contextMessages, }) { _reporter.atOffset( offset: offset, length: length, diagnosticCode: diagnosticCode, arguments: arguments, contextMessages: contextMessages, ); } void _reportAtPubNode( PubspecNodeImpl node, { List arguments = const [], List 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 arguments = const [], List? 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 get diagnosticCodes => [diagnosticCode]; /// Reports a diagnostic at [node] with message [arguments] and /// [contextMessages]. void reportAtNode( AstNode? node, { List arguments = const [], List? 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 arguments = const [], List? 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 arguments = const [], List 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 arguments = const [], List? 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 arguments = const [], List? 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 arguments = const [], List? 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 arguments = const [], List 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 arguments = const [], List? contextMessages, }) => _reportAtToken( token, diagnosticCode: diagnosticCode, arguments: arguments, contextMessages: contextMessages, ); }