blob: 0fb77cf035f1bc7b1c5490267d288ba33fbc3683 [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 'dart:io';
import 'package:analyzer/dart/analysis/declared_variables.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/constant/value.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/scope.dart';
import 'package:analyzer/dart/element/type_provider.dart';
import 'package:analyzer/dart/element/type_system.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/file_system/file_system.dart' as file_system;
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer/src/dart/ast/token.dart';
import 'package:analyzer/src/dart/constant/compute.dart';
import 'package:analyzer/src/dart/constant/constant_verifier.dart';
import 'package:analyzer/src/dart/constant/evaluation.dart';
import 'package:analyzer/src/dart/constant/potentially_constant.dart';
import 'package:analyzer/src/dart/constant/utilities.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/dart/element/inheritance_manager3.dart';
import 'package:analyzer/src/dart/element/type_system.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/engine.dart'
show
AnalysisErrorInfo,
AnalysisErrorInfoImpl,
AnalysisOptions,
AnalysisOptionsImpl;
import 'package:analyzer/src/generated/resolver.dart' show ScopedVisitor;
import 'package:analyzer/src/generated/source.dart' show LineInfo;
import 'package:analyzer/src/generated/source_io.dart';
import 'package:analyzer/src/lint/analysis.dart';
import 'package:analyzer/src/lint/config.dart';
import 'package:analyzer/src/lint/io.dart';
import 'package:analyzer/src/lint/linter_visitor.dart' show NodeLintRegistry;
import 'package:analyzer/src/lint/project.dart';
import 'package:analyzer/src/lint/pub.dart';
import 'package:analyzer/src/lint/registry.dart';
import 'package:analyzer/src/services/lint.dart' show Linter;
import 'package:analyzer/src/workspace/workspace.dart';
import 'package:glob/glob.dart';
import 'package:path/path.dart' as p;
export 'package:analyzer/src/lint/linter_visitor.dart' show NodeLintRegistry;
typedef Printer = void Function(String msg);
/// Describes a String in valid camel case format.
@deprecated // Never intended for public use.
class CamelCaseString {
static final _camelCaseMatcher = RegExp(r'[A-Z][a-z]*');
static final _camelCaseTester = RegExp(r'^([_$]*)([A-Z?$]+[a-z0-9]*)+$');
final String value;
CamelCaseString(this.value) {
if (!isCamelCase(value)) {
throw ArgumentError('$value is not CamelCase');
}
}
String get humanized => _humanize(value);
@override
String toString() => value;
static bool isCamelCase(String name) => _camelCaseTester.hasMatch(name);
static String _humanize(String camelCase) =>
_camelCaseMatcher.allMatches(camelCase).map((m) => m.group(0)).join(' ');
}
/// Dart source linter.
class DartLinter implements AnalysisErrorListener {
final errors = <AnalysisError>[];
final LinterOptions options;
final Reporter reporter;
/// The total number of sources that were analyzed. Only valid after
/// [lintFiles] has been called.
late int numSourcesAnalyzed;
/// Creates a new linter.
DartLinter(this.options, {this.reporter = const PrintingReporter()});
Future<Iterable<AnalysisErrorInfo>> lintFiles(List<File> files) async {
List<AnalysisErrorInfo> errors = [];
final lintDriver = LintDriver(options);
errors.addAll(await lintDriver.analyze(files.where((f) => isDartFile(f))));
numSourcesAnalyzed = lintDriver.numSourcesAnalyzed;
files.where((f) => isPubspecFile(f)).forEach((p) {
numSourcesAnalyzed++;
return errors.addAll(_lintPubspecFile(p));
});
return errors;
}
Iterable<AnalysisErrorInfo> lintPubspecSource(
{required String contents, String? sourcePath}) {
var results = <AnalysisErrorInfo>[];
var sourceUrl = sourcePath == null ? null : p.toUri(sourcePath);
var spec = Pubspec.parse(contents, sourceUrl: sourceUrl);
for (Linter lint in options.enabledLints) {
if (lint is LintRule) {
LintRule rule = lint;
var visitor = rule.getPubspecVisitor();
if (visitor != null) {
// Analyzer sets reporters; if this file is not being analyzed,
// we need to set one ourselves. (Needless to say, when pubspec
// processing gets pushed down, this hack can go away.)
if (sourceUrl != null) {
var source = createSource(sourceUrl);
rule.reporter = ErrorReporter(
this,
source,
isNonNullableByDefault: false,
);
}
try {
spec.accept(visitor);
} on Exception catch (e) {
reporter.exception(LinterException(e.toString()));
}
if (rule._locationInfo.isNotEmpty) {
results.addAll(rule._locationInfo);
rule._locationInfo.clear();
}
}
}
}
return results;
}
@override
void onError(AnalysisError error) => errors.add(error);
Iterable<AnalysisErrorInfo> _lintPubspecFile(File sourceFile) =>
lintPubspecSource(
contents: sourceFile.readAsStringSync(),
sourcePath: options.resourceProvider.pathContext
.normalize(sourceFile.absolute.path));
}
class FileGlobFilter extends LintFilter {
Iterable<Glob> includes;
Iterable<Glob> excludes;
FileGlobFilter(Iterable<String> includeGlobs, Iterable<String> excludeGlobs)
: includes = includeGlobs.map((glob) => Glob(glob)),
excludes = excludeGlobs.map((glob) => Glob(glob));
@override
bool filter(AnalysisError lint) {
// TODO specify order
return excludes.any((glob) => glob.matches(lint.source.fullName)) &&
!includes.any((glob) => glob.matches(lint.source.fullName));
}
}
class Group implements Comparable<Group> {
/// Defined rule groups.
static const Group errors =
Group._('errors', description: 'Possible coding errors.');
static const Group pub = Group._('pub',
description: 'Pub-related rules.',
link: Hyperlink('See the <strong>Pubspec Format</strong>',
'https://dart.dev/tools/pub/pubspec'));
static const Group style = Group._('style',
description:
'Matters of style, largely derived from the official Dart Style Guide.',
link: Hyperlink('See the <strong>Style Guide</strong>',
'https://dart.dev/guides/language/effective-dart/style'));
/// List of builtin groups in presentation order.
static const Iterable<Group> builtin = [errors, style, pub];
final String name;
final bool custom;
final String description;
final Hyperlink? link;
factory Group(String name, {String description = '', Hyperlink? link}) {
var n = name.toLowerCase();
return builtin.firstWhere((g) => g.name == n,
orElse: () =>
Group._(name, custom: true, description: description, link: link));
}
const Group._(this.name,
{this.custom = false, required this.description, this.link});
@override
int compareTo(Group other) => name.compareTo(other.name);
}
class Hyperlink {
final String label;
final String href;
final bool bold;
const Hyperlink(this.label, this.href, {this.bold = false});
String get html => '<a href="$href">${_emph(label)}</a>';
String _emph(String msg) => bold ? '<strong>$msg</strong>' : msg;
}
/// The result of attempting to evaluate an expression.
class LinterConstantEvaluationResult {
/// The value of the expression, or `null` if has [errors].
final DartObject? value;
/// The errors reported during the evaluation.
final List<AnalysisError> errors;
LinterConstantEvaluationResult(this.value, this.errors);
}
/// Provides access to information needed by lint rules that is not available
/// from AST nodes or the element model.
abstract class LinterContext {
List<LinterContextUnit> get allUnits;
AnalysisOptions get analysisOptions;
LinterContextUnit get currentUnit;
DeclaredVariables get declaredVariables;
InheritanceManager3 get inheritanceManager;
WorkspacePackage? get package;
TypeProvider get typeProvider;
TypeSystem get typeSystem;
/// Return `true` if it would be valid for the given [expression] to have
/// a keyword of `const`.
///
/// The [expression] is expected to be a node within one of the compilation
/// units in [allUnits].
///
/// Note that this method can cause constant evaluation to occur, which can be
/// computationally expensive.
bool canBeConst(Expression expression);
/// Return `true` if it would be valid for the given constructor declaration
/// [node] to have a keyword of `const`.
///
/// The [node] is expected to be a node within one of the compilation
/// units in [allUnits].
///
/// Note that this method can cause constant evaluation to occur, which can be
/// computationally expensive.
bool canBeConstConstructor(ConstructorDeclaration node);
/// Return the result of evaluating the given expression.
LinterConstantEvaluationResult evaluateConstant(Expression node);
/// Return `true` if the given [unit] is in a test directory.
bool inTestDir(CompilationUnit unit);
/// Return `true` if the [feature] is enabled in the library being linted.
bool isEnabled(Feature feature);
/// Resolve the name `id` or `id=` (if [setter] is `true`) an the location
/// of the [node], according to the "16.35 Lexical Lookup" of the language
/// specification.
LinterNameInScopeResolutionResult resolveNameInScope(
String id, bool setter, AstNode node);
}
/// Implementation of [LinterContext]
class LinterContextImpl implements LinterContext {
static final _testDirectories = [
'${p.separator}test${p.separator}',
'${p.separator}integration_test${p.separator}',
'${p.separator}test_driver${p.separator}',
'${p.separator}testing${p.separator}',
];
@override
final List<LinterContextUnit> allUnits;
@override
final AnalysisOptions analysisOptions;
@override
final LinterContextUnit currentUnit;
@override
final DeclaredVariables declaredVariables;
@override
final WorkspacePackage? package;
@override
final TypeProvider typeProvider;
@override
final TypeSystemImpl typeSystem;
@override
final InheritanceManager3 inheritanceManager;
LinterContextImpl(
this.allUnits,
this.currentUnit,
this.declaredVariables,
this.typeProvider,
this.typeSystem,
this.inheritanceManager,
this.analysisOptions,
this.package,
);
@override
bool canBeConst(Expression expression) {
if (expression is InstanceCreationExpressionImpl) {
return _canBeConstInstanceCreation(expression);
} else if (expression is TypedLiteralImpl) {
return _canBeConstTypedLiteral(expression);
} else {
return false;
}
}
@override
bool canBeConstConstructor(covariant ConstructorDeclarationImpl node) {
var element = node.declaredElement!;
ClassElement classElement = element.enclosingElement;
if (classElement.hasNonFinalField) return false;
var oldKeyword = node.constKeyword;
try {
temporaryConstConstructorElements[element] = true;
node.constKeyword = KeywordToken(Keyword.CONST, node.offset);
return !_hasConstantVerifierError(node);
} finally {
temporaryConstConstructorElements[element] = null;
node.constKeyword = oldKeyword;
}
}
@override
LinterConstantEvaluationResult evaluateConstant(Expression node) {
var unitElement = currentUnit.unit.declaredElement!;
var source = unitElement.source;
var libraryElement = unitElement.library as LibraryElementImpl;
var errorListener = RecordingErrorListener();
var errorReporter = ErrorReporter(
errorListener,
source,
isNonNullableByDefault: libraryElement.isNonNullableByDefault,
);
var visitor = ConstantVisitor(
ConstantEvaluationEngine(
declaredVariables, isEnabled(Feature.triple_shift)),
libraryElement,
errorReporter,
);
var value = node.accept(visitor);
return LinterConstantEvaluationResult(value, errorListener.errors);
}
@override
bool inTestDir(CompilationUnit unit) {
var path = unit.declaredElement?.source.fullName;
return path != null && _testDirectories.any(path.contains);
}
@override
bool isEnabled(Feature feature) {
var unitElement = currentUnit.unit.declaredElement!;
return unitElement.library.featureSet.isEnabled(feature);
}
@override
LinterNameInScopeResolutionResult resolveNameInScope(
String id, bool setter, AstNode node) {
Scope? scope;
for (AstNode? context = node; context != null; context = context.parent) {
scope = ScopedVisitor.getNodeNameScope(context);
if (scope != null) {
break;
}
}
if (scope != null) {
var lookupResult = scope.lookup(id);
var idElement = lookupResult.getter;
var idEqElement = lookupResult.setter;
var requestedElement = setter ? idEqElement : idElement;
var differentElement = setter ? idElement : idEqElement;
if (requestedElement != null) {
return LinterNameInScopeResolutionResult._requestedName(
requestedElement,
);
}
if (differentElement != null) {
return LinterNameInScopeResolutionResult._differentName(
differentElement,
);
}
}
return const LinterNameInScopeResolutionResult._none();
}
bool _canBeConstInstanceCreation(InstanceCreationExpressionImpl node) {
//
// Verify that the invoked constructor is a const constructor.
//
var element = node.constructorName.staticElement;
if (element == null || !element.isConst) {
return false;
}
// Ensure that dependencies (e.g. default parameter values) are computed.
var implElement = element.declaration as ConstructorElementImpl;
implElement.computeConstantDependencies();
//
// Verify that the evaluation of the constructor would not produce an
// exception.
//
var oldKeyword = node.keyword;
try {
node.keyword = KeywordToken(Keyword.CONST, node.offset);
return !_hasConstantVerifierError(node);
} finally {
node.keyword = oldKeyword;
}
}
bool _canBeConstTypedLiteral(TypedLiteralImpl node) {
var oldKeyword = node.constKeyword;
try {
node.constKeyword = KeywordToken(Keyword.CONST, node.offset);
return !_hasConstantVerifierError(node);
} finally {
node.constKeyword = oldKeyword;
}
}
/// Return `true` if [ConstantVerifier] reports an error for the [node].
bool _hasConstantVerifierError(AstNode node) {
var unitElement = currentUnit.unit.declaredElement!;
var libraryElement = unitElement.library as LibraryElementImpl;
var dependenciesFinder = ConstantExpressionsDependenciesFinder();
node.accept(dependenciesFinder);
computeConstants(
typeProvider,
typeSystem,
declaredVariables,
dependenciesFinder.dependencies.toList(),
(analysisOptions as AnalysisOptionsImpl).experimentStatus,
);
var listener = _ConstantAnalysisErrorListener();
var errorReporter = ErrorReporter(
listener,
unitElement.source,
isNonNullableByDefault: libraryElement.isNonNullableByDefault,
);
node.accept(
ConstantVerifier(
errorReporter,
libraryElement,
declaredVariables,
),
);
return listener.hasConstError;
}
}
class LinterContextUnit {
final String content;
final CompilationUnit unit;
LinterContextUnit(this.content, this.unit);
}
/// Thrown when an error occurs in linting.
class LinterException implements Exception {
/// A message describing the error.
final String? message;
/// Creates a new LinterException with an optional error [message].
const LinterException([this.message]);
@override
String toString() =>
message == null ? "LinterException" : "LinterException: $message";
}
/// The result of resolving of a basename `id` in a scope.
class LinterNameInScopeResolutionResult {
/// The element with the requested basename, `null` is [isNone].
final Element? element;
/// The state of the result.
final _LinterNameInScopeResolutionResultState _state;
const LinterNameInScopeResolutionResult._differentName(this.element)
: _state = _LinterNameInScopeResolutionResultState.differentName;
const LinterNameInScopeResolutionResult._none()
: element = null,
_state = _LinterNameInScopeResolutionResultState.none;
const LinterNameInScopeResolutionResult._requestedName(this.element)
: _state = _LinterNameInScopeResolutionResultState.requestedName;
bool get isDifferentName =>
_state == _LinterNameInScopeResolutionResultState.differentName;
bool get isNone => _state == _LinterNameInScopeResolutionResultState.none;
bool get isRequestedName =>
_state == _LinterNameInScopeResolutionResultState.requestedName;
@override
String toString() {
return '(state: $_state, element: $element)';
}
}
/// Linter options.
class LinterOptions extends DriverOptions {
Iterable<LintRule> enabledLints;
String? analysisOptions;
LintFilter? filter;
late file_system.ResourceProvider resourceProvider;
// todo (pq): consider migrating to named params (but note Linter dep).
LinterOptions([Iterable<LintRule>? enabledLints, this.analysisOptions])
: enabledLints = enabledLints ?? Registry.ruleRegistry;
void configure(LintConfig config) {
enabledLints = Registry.ruleRegistry.where((LintRule rule) =>
!config.ruleConfigs.any((rc) => rc.disables(rule.name)));
filter = FileGlobFilter(config.fileIncludes, config.fileExcludes);
}
}
/// Filtered lints are omitted from linter output.
abstract class LintFilter {
bool filter(AnalysisError lint);
}
/// Describes a lint rule.
abstract class LintRule extends Linter implements Comparable<LintRule> {
/// Description (in markdown format) suitable for display in a detailed lint
/// description.
final String details;
/// Short description suitable for display in console output.
final String description;
/// Lint group (for example, 'style').
final Group group;
/// Lint maturity (stable|experimental).
final Maturity maturity;
/// Lint name.
@override
final String name;
/// Until pubspec analysis is pushed into the analyzer proper, we need to
/// do some extra book-keeping to keep track of details that will help us
/// constitute AnalysisErrorInfos.
final List<AnalysisErrorInfo> _locationInfo = <AnalysisErrorInfo>[];
LintRule({
required this.name,
required this.group,
required this.description,
required this.details,
this.maturity = Maturity.stable,
});
/// A list of incompatible rule ids.
List<String> get incompatibleRules => const [];
@override
LintCode get lintCode => _LintCode(name, description);
@override
int compareTo(LintRule other) {
var g = group.compareTo(other.group);
if (g != 0) {
return g;
}
return name.compareTo(other.name);
}
/// Return a visitor to be passed to provide access to Dart project context
/// and to perform project-level analyses.
ProjectVisitor? getProjectVisitor() => null;
/// Return a visitor to be passed to pubspecs to perform lint
/// analysis.
/// Lint errors are reported via this [Linter]'s error [reporter].
PubspecVisitor? getPubspecVisitor() => null;
@override
AstVisitor? getVisitor() => null;
void reportLint(AstNode? node,
{List<Object> arguments = const [],
ErrorCode? errorCode,
bool ignoreSyntheticNodes = true}) {
if (node != null && (!node.isSynthetic || !ignoreSyntheticNodes)) {
reporter.reportErrorForNode(errorCode ?? lintCode, node, arguments);
}
}
void reportLintForOffset(int offset, int length,
{List<Object> arguments = const [], ErrorCode? errorCode}) {
reporter.reportErrorForOffset(
errorCode ?? lintCode, offset, length, arguments);
}
void reportLintForToken(Token? token,
{List<Object> arguments = const [],
ErrorCode? errorCode,
bool ignoreSyntheticTokens = true}) {
if (token != null && (!token.isSynthetic || !ignoreSyntheticTokens)) {
reporter.reportErrorForToken(errorCode ?? lintCode, token, arguments);
}
}
void reportPubLint(PSNode node) {
var source = node.source;
// Cache error and location info for creating AnalysisErrorInfos
AnalysisError error = AnalysisError(
source, node.span.start.offset, node.span.length, lintCode);
LineInfo lineInfo = LineInfo.fromContent(source.contents.data);
_locationInfo.add(AnalysisErrorInfoImpl([error], lineInfo));
// Then do the reporting
reporter.reportError(error);
}
}
class Maturity implements Comparable<Maturity> {
static const Maturity stable = Maturity._('stable', ordinal: 0);
static const Maturity experimental = Maturity._('experimental', ordinal: 1);
static const Maturity deprecated = Maturity._('deprecated', ordinal: 2);
final String name;
final int ordinal;
factory Maturity(String name, {required int ordinal}) {
switch (name.toLowerCase()) {
case 'stable':
return stable;
case 'experimental':
return experimental;
case 'deprecated':
return deprecated;
default:
return Maturity._(name, ordinal: ordinal);
}
}
const Maturity._(this.name, {required this.ordinal});
@override
int compareTo(Maturity other) => ordinal - other.ordinal;
}
/// [LintRule]s that implement this interface want to process only some types
/// of AST nodes, and will register their processors in the registry.
abstract class NodeLintRule {
/// This method is invoked to let the [LintRule] register 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(NodeLintRegistry registry, LinterContext context);
}
/// [LintRule]s that implement this interface want to process only some types
/// of AST nodes, and will register their processors in the registry.
///
/// This class exists solely to allow a smoother transition from analyzer
/// version 0.33.*. It will be removed in a future analyzer release, so please
/// use [NodeLintRule] instead.
@deprecated
abstract class NodeLintRuleWithContext extends NodeLintRule {}
class PrintingReporter implements Reporter {
final Printer _print;
const PrintingReporter([this._print = print]);
@override
void exception(LinterException exception) {
_print('EXCEPTION: $exception');
}
@override
void warn(String message) {
_print('WARN: $message');
}
}
abstract class Reporter {
void exception(LinterException exception);
void warn(String message);
}
/// Linter implementation.
class SourceLinter implements DartLinter, AnalysisErrorListener {
@override
final errors = <AnalysisError>[];
@override
final LinterOptions options;
@override
final Reporter reporter;
@override
late int numSourcesAnalyzed;
SourceLinter(this.options, {this.reporter = const PrintingReporter()});
@override
Future<Iterable<AnalysisErrorInfo>> lintFiles(List<File> files) async {
List<AnalysisErrorInfo> errors = [];
final lintDriver = LintDriver(options);
errors.addAll(await lintDriver.analyze(files.where((f) => isDartFile(f))));
numSourcesAnalyzed = lintDriver.numSourcesAnalyzed;
files.where((f) => isPubspecFile(f)).forEach((p) {
numSourcesAnalyzed++;
return errors.addAll(_lintPubspecFile(p));
});
return errors;
}
@override
Iterable<AnalysisErrorInfo> lintPubspecSource(
{required String contents, String? sourcePath}) {
var results = <AnalysisErrorInfo>[];
var sourceUrl = sourcePath == null ? null : p.toUri(sourcePath);
var spec = Pubspec.parse(contents, sourceUrl: sourceUrl);
for (Linter lint in options.enabledLints) {
if (lint is LintRule) {
LintRule rule = lint;
var visitor = rule.getPubspecVisitor();
if (visitor != null) {
// Analyzer sets reporters; if this file is not being analyzed,
// we need to set one ourselves. (Needless to say, when pubspec
// processing gets pushed down, this hack can go away.)
if (sourceUrl != null) {
var source = createSource(sourceUrl);
rule.reporter = ErrorReporter(
this,
source,
isNonNullableByDefault: false,
);
}
try {
spec.accept(visitor);
} on Exception catch (e) {
reporter.exception(LinterException(e.toString()));
}
var locationInfo = rule._locationInfo;
if (!identical(null, locationInfo) && locationInfo.isNotEmpty) {
results.addAll(rule._locationInfo);
rule._locationInfo.clear();
}
}
}
}
return results;
}
@override
void onError(AnalysisError error) => errors.add(error);
@override
Iterable<AnalysisErrorInfo> _lintPubspecFile(File sourceFile) =>
lintPubspecSource(
contents: sourceFile.readAsStringSync(), sourcePath: sourceFile.path);
}
/// An error listener that only records whether any constant related errors have
/// been reported.
class _ConstantAnalysisErrorListener extends AnalysisErrorListener {
/// A flag indicating whether any constant related errors have been reported
/// to this listener.
bool hasConstError = false;
@override
void onError(AnalysisError error) {
ErrorCode errorCode = error.errorCode;
if (errorCode is CompileTimeErrorCode) {
switch (errorCode) {
case CompileTimeErrorCode
.CONST_CONSTRUCTOR_WITH_FIELD_INITIALIZED_BY_NON_CONST:
case CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL:
case CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_INT:
case CompileTimeErrorCode.CONST_EVAL_TYPE_BOOL_NUM_STRING:
case CompileTimeErrorCode.CONST_EVAL_TYPE_INT:
case CompileTimeErrorCode.CONST_EVAL_TYPE_NUM:
case CompileTimeErrorCode.CONST_EVAL_THROWS_EXCEPTION:
case CompileTimeErrorCode.CONST_EVAL_THROWS_IDBZE:
case CompileTimeErrorCode
.CONST_MAP_KEY_EXPRESSION_TYPE_IMPLEMENTS_EQUALS:
case CompileTimeErrorCode.CONST_SET_ELEMENT_TYPE_IMPLEMENTS_EQUALS:
case CompileTimeErrorCode.CONST_WITH_NON_CONST:
case CompileTimeErrorCode.CONST_WITH_NON_CONSTANT_ARGUMENT:
case CompileTimeErrorCode.CONST_WITH_TYPE_PARAMETERS:
case CompileTimeErrorCode.INVALID_CONSTANT:
case CompileTimeErrorCode.MISSING_CONST_IN_LIST_LITERAL:
case CompileTimeErrorCode.MISSING_CONST_IN_MAP_LITERAL:
case CompileTimeErrorCode.MISSING_CONST_IN_SET_LITERAL:
case CompileTimeErrorCode.NON_CONSTANT_LIST_ELEMENT:
case CompileTimeErrorCode.NON_CONSTANT_MAP_ELEMENT:
case CompileTimeErrorCode.NON_CONSTANT_MAP_KEY:
case CompileTimeErrorCode.NON_CONSTANT_MAP_VALUE:
case CompileTimeErrorCode.NON_CONSTANT_SET_ELEMENT:
hasConstError = true;
}
}
}
}
class _LintCode extends LintCode {
static final registry = <String, _LintCode>{};
factory _LintCode(String name, String message) {
return registry[name + message] ??= _LintCode._(name, message);
}
_LintCode._(String name, String message) : super(name, message);
}
/// The state of a [LinterNameInScopeResolutionResult].
enum _LinterNameInScopeResolutionResultState {
/// Indicates that no element was found.
none,
/// Indicates that an element with the requested name was found.
requestedName,
/// Indicates that an element with the same basename, but different name
/// was found.
differentName
}