From a155e2b521088d38f7c2fb537c6c367204f1d5f0 Mon Sep 17 00:00:00 2001 From: Brian Wilkerson Date: Fri, 8 Dec 2023 15:50:09 +0000 Subject: [PATCH] Remove the avoid_unstable_final_fields lint Change-Id: I6835df32fc6c0d9a5f17b3b5344353ec061147d0 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/340386 Reviewed-by: Phil Quitslund Commit-Queue: Brian Wilkerson --- .../services/correction/error_fix_status.yaml | 2 - pkg/linter/lib/src/rules.dart | 2 - .../rules/avoid_unstable_final_fields.dart | 751 +-------- pkg/linter/test/rules/all.dart | 2 - .../avoid_unstable_final_fields_test.dart | 1497 ----------------- 5 files changed, 3 insertions(+), 2251 deletions(-) delete mode 100644 pkg/linter/test/rules/avoid_unstable_final_fields_test.dart diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml index 9d2ad382ace..7232323643e 100644 --- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml +++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml @@ -1986,8 +1986,6 @@ LintCode.avoid_types_on_closure_parameters: status: hasFix LintCode.avoid_unnecessary_containers: status: hasFix -LintCode.avoid_unstable_final_fields: - status: noFix LintCode.avoid_unused_constructor_parameters: status: hasFix LintCode.avoid_void_async: diff --git a/pkg/linter/lib/src/rules.dart b/pkg/linter/lib/src/rules.dart index 301d0fc3c19..0b77a151476 100644 --- a/pkg/linter/lib/src/rules.dart +++ b/pkg/linter/lib/src/rules.dart @@ -49,7 +49,6 @@ import 'rules/avoid_type_to_string.dart'; import 'rules/avoid_types_as_parameter_names.dart'; import 'rules/avoid_types_on_closure_parameters.dart'; import 'rules/avoid_unnecessary_containers.dart'; -import 'rules/avoid_unstable_final_fields.dart'; import 'rules/avoid_unused_constructor_parameters.dart'; import 'rules/avoid_void_async.dart'; import 'rules/avoid_web_libraries_in_flutter.dart'; @@ -283,7 +282,6 @@ void registerLintRules() { ..register(AvoidTypesAsParameterNames()) ..register(AvoidTypesOnClosureParameters()) ..register(AvoidUnnecessaryContainers()) - ..register(AvoidUnstableFinalFields()) ..register(AvoidUnusedConstructorParameters()) ..register(AvoidVoidAsync()) ..register(AvoidWebLibrariesInFlutter()) diff --git a/pkg/linter/lib/src/rules/avoid_unstable_final_fields.dart b/pkg/linter/lib/src/rules/avoid_unstable_final_fields.dart index 28d54dc21e1..6c9a8481411 100644 --- a/pkg/linter/lib/src/rules/avoid_unstable_final_fields.dart +++ b/pkg/linter/lib/src/rules/avoid_unstable_final_fields.dart @@ -2,118 +2,15 @@ // 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/dart/ast/ast.dart'; -import 'package:analyzer/dart/ast/token.dart'; -import 'package:analyzer/dart/ast/visitor.dart'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:analyzer/diagnostic/diagnostic.dart'; -// ignore:implementation_imports -import 'package:analyzer/src/diagnostic/diagnostic.dart'; - import '../analyzer.dart'; const _desc = r'Avoid overriding a final field to return ' 'different values if called multiple times.'; const _details = r''' -**AVOID** overriding or implementing a final field as a getter which could -return different values if it is invoked multiple times on the same receiver. -This could occur because the getter is an implicitly induced getter of a -non-final field, or it could be an explicitly declared getter with a body -that isn't known to return the same value each time it is called. - -The underlying motivation for this rule is that if it is followed then a final -field is an immutable property of an object. This is important for correctness -because it is then safe to assume that the value does not change during the -execution of an algorithm. In contrast, it may be necessary to re-check any -other getter repeatedly if it is not known to have this property. Similarly, -it is safe to cache the immutable property in a local variable and promote it, -but for any other property it is necessary to check repeatedly that the -underlying property hasn't changed since it was promoted. - -**BAD:** -```dart -class A { - final int i; - A(this.i); -} - -var j = 0; - -class B1 extends A { - int get i => ++j + super.i; // LINT. - B1(super.i); -} - -class B2 implements A { - int i; // LINT. - B2(this.i); -} -``` - -**GOOD:** -```dart -class C { - final int i; - C(this.i); -} - -class D1 implements C { - late final int i = someExpression; // OK. -} - -class D2 extends C { - int get i => super.i + 1; // OK. - D2(super.i); -} - -class D3 implements C { - final int i; // OK. - D3(this.i); -} -``` - +This rule has been removed. '''; -bool _isLocallyStable(Element element) { - if (element is PropertyAccessorElement && - element.isGetter && - element.isSynthetic && - element.correspondingSetter == null) { - // This is a final, non-local variable, and they are stable. - var metadata = element.variable.metadata; - if (metadata.isNotEmpty) { - for (var elementAnnotation in metadata) { - var metadataElement = elementAnnotation.element; - if (metadataElement is ConstructorElement) { - var metadataOwner = metadataElement.declaration.enclosingElement; - if (metadataOwner is ClassElement && metadataOwner.isDartCoreObject) { - // A declaration with `@Object()` is not considered stable. - return false; - } - } - } - } - return true; - } else if (element is FunctionElement) { - // A tear-off of a top-level function or static method is stable, - // local functions and function literals are not. - return element.isStatic; - } else if (element is EnumElement || - element is MixinElement || - element is ClassElement) { - // A reified type of a class/mixin/enum is stable. - return true; - } else if (element is MethodElement) { - // An instance method tear-off is never stable, - // but a static method tear-off is stable. - return element.isStatic; - } - // TODO(eernst): Any cases still missing? - return false; -} - class AvoidUnstableFinalFields extends LintRule { AvoidUnstableFinalFields() : super( @@ -121,651 +18,9 @@ class AvoidUnstableFinalFields extends LintRule { description: _desc, details: _details, group: Group.errors, - state: State.deprecated()); + state: State.removed()); @override void registerNodeProcessors( - NodeLintRegistry registry, LinterContext context) { - var visitor = _Visitor(this, context); - registry.addFieldDeclaration(this, visitor); - registry.addMethodDeclaration(this, visitor); - } -} - -abstract class _AbstractVisitor extends UnifyingAstVisitor { - final LintRule rule; - final LinterContext context; - - // Will be true initially when a getter body is traversed. Will be made - // false if the getter body turns out to be unstable. Is checked after the - // traversal of the body, to emit a lint if it is false at that time. - bool isStable = true; - - // Each [PropertyAccessorElement] which is causing the lint to be reported is - // added to this list, which is then used to create context messages. - Set causes = {}; - - // Initialized in `visitMethodDeclaration` if a lint might be emitted. - // It is then guaranteed that `declaration.isGetter` is true. - late final MethodDeclaration declaration; - - _AbstractVisitor(this.rule, this.context); - - void doReportLint(Token? name) { - rule.reportLintForToken(name, contextMessages: _computeContextMessages()); - } - - // The following visitor methods will only be executed in the situation - // where `declaration` is a getter which must be stable, and the - // traversal is visiting the body of said getter. Hence, a lint must - // be emitted whenever the given body is not known to be appropriate - // for a stable getter. - - @override - void visitAsExpression(AsExpression node) { - if (node.expression.staticType?.isBottom ?? true) return; - node.expression.accept(this); - } - - @override - void visitAssignmentExpression(AssignmentExpression node) { - if (node.staticType?.isBottom ?? true) return; - var operator = node.operator; - if (operator.type != TokenType.EQ) { - // TODO(eernst): Could a compound assignment be stable? - isStable = false; - } else { - // A regular assignment is stable iff its right hand side is stable. - node.rightHandSide.accept(this); - } - } - - @override - void visitAwaitExpression(AwaitExpression node) { - // We cannot predict the outcome of awaiting a future. - isStable = false; - } - - @override - void visitBinaryExpression(BinaryExpression node) { - if (node.leftOperand.staticType?.isBottom ?? true) return; - if (node.rightOperand.staticType?.isBottom ?? true) return; - node.leftOperand.accept(this); - node.rightOperand.accept(this); - if (isStable) { - // So far no problems! Only a few cases are working, - // see if we have one of those. - var operatorType = node.operator.type; - var leftType = node.leftOperand.staticType; - if (leftType == null) { - isStable = false; // Presumably a wrong program. Be safe. - return; - } - if (operatorType == TokenType.PLUS) { - if (leftType.isDartCoreInt || - leftType.isDartCoreDouble || - leftType.isDartCoreNum || - leftType.isDartCoreString) { - // These are all stable. - return; - } else { - // A user-defined `+` cannot be assumed to be stable. - isStable = false; - } - } else if (operatorType == TokenType.MINUS || - operatorType == TokenType.STAR || - operatorType == TokenType.SLASH || - operatorType == TokenType.PERCENT || - operatorType == TokenType.LT || - operatorType == TokenType.LT_EQ || - operatorType == TokenType.GT || - operatorType == TokenType.GT_EQ) { - if (leftType.isDartCoreInt || - leftType.isDartCoreDouble || - leftType.isDartCoreNum) { - // These are all stable. - return; - } else { - // User-defined operators in this group - // cannot be assumed to be stable. - isStable = false; - } - } else if (operatorType == TokenType.EQ_EQ || - operatorType == TokenType.BANG_EQ) { - if (leftType.isDartCoreNull || - (node.rightOperand.staticType?.isDartCoreNull ?? false) || - leftType.isDartCoreBool || - leftType.isDartCoreInt || - leftType.isDartCoreString) { - // Primitive equality of two stable expressions is stable, and so is - // equality involving null. Note that we can only detect primitive - // equality for a few types. Any class that inherits `Object.==` has - // primitive equality, but an object with that static type could be - // an instance of a subtype that overrides `==`. - return; - } - // Equality is otherwise not stable, can run arbitrary code. - isStable = false; - } else if (operatorType == TokenType.AMPERSAND_AMPERSAND || - operatorType == TokenType.BAR_BAR) { - // Logical and/or cannot be user-defined, is stable. - return; - } else if (operatorType == TokenType.QUESTION_QUESTION) { - // `e1 ?? e2` is stable when both operands are stable. - return; - } else if (operatorType == TokenType.TILDE_SLASH || - operatorType == TokenType.GT_GT || - operatorType == TokenType.GT_GT_GT || - operatorType == TokenType.LT_LT) { - if (leftType.isDartCoreInt) { - // These primitive arithmetic operations and relations are stable. - return; - } else { - // A user-defined operator can not be assumed to be stable. - isStable = false; - } - } else if (operatorType == TokenType.AMPERSAND || - operatorType == TokenType.BAR || - operatorType == TokenType.CARET) { - if (leftType.isDartCoreInt || leftType.isDartCoreBool) { - // These primitive logical operations are stable. - return; - } else { - // A user-defined operator can not be assumed to be stable. - isStable = false; - } - } else if (operatorType == TokenType.QUESTION_QUESTION) { - // An if-null expression with stable operands is stable. - return; - } - // TODO(eernst): Add support for missing cases, if any. - isStable = false; - } - } - - @override - void visitBlock(Block node) { - // TODO(eernst): Check that only one return statement exists, and it is - // the last statement in the body, and it returns a stable expression. - if (node.statements.isEmpty) { - // This getter returns null, keep it stable. - } else if (node.statements.length == 1) { - var statement = node.statements.first; - if (statement is ReturnStatement) { - statement.accept(this); - } else { - // This getter returns null or throws, keep it stable. - } - } else { - // TODO(eernst): Allow multiple statements, just check returns. - isStable = false; - } - } - - @override - void visitBlockFunctionBody(BlockFunctionBody node) { - visitBlock(node.block); - } - - @override - void visitBooleanLiteral(BooleanLiteral node) { - // Keep it stable. - } - - @override - void visitCascadeExpression(CascadeExpression node) { - // A cascade is stable if its target is stable. - node.target.accept(this); - } - - @override - void visitConditionalExpression(ConditionalExpression node) { - if (node.condition.staticType?.isBottom ?? true) return; - node.condition.accept(this); - node.thenExpression.accept(this); - node.elseExpression.accept(this); - } - - @override - void visitDoubleLiteral(DoubleLiteral node) { - // Keep it stable. - } - - @override - void visitEmptyFunctionBody(EmptyFunctionBody node) { - // Returns null: keep it stable. - } - - @override - void visitExpressionFunctionBody(ExpressionFunctionBody node) { - if (node.expression.staticType?.isBottom ?? true) return; - node.expression.accept(this); - } - - @override - void visitExpressionStatement(ExpressionStatement node) { - node.expression.accept(this); - } - - @override - void visitFunctionExpression(FunctionExpression node) { - // We cannot expect the function literal to be the same function, only if - // we introduce constant expressions that are function literals. - isStable = false; - } - - @override - void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) { - if (node.staticType?.isBottom ?? true) return; - // We cannot expect a function invocation to be stable. - isStable = false; - } - - @override - void visitIndexExpression(IndexExpression node) { - if (node.staticType?.isBottom ?? true) return; - // The type system does not recognize immutable lists or similar entities, - // so we can never hope to detect that this is stable. - isStable = false; - } - - @override - void visitInstanceCreationExpression(InstanceCreationExpression node) { - if (!node.isConst) isStable = false; - } - - @override - void visitIntegerLiteral(IntegerLiteral node) { - // Keep it stable. - } - - @override - void visitInterpolationExpression(InterpolationExpression node) { - node.expression.accept(this); - } - - @override - void visitIsExpression(IsExpression node) { - if (node.expression.staticType?.isBottom ?? true) return; - // Testing `e is T` where `e` is stable depends on `T`. However, there is - // no `` that denotes two different types in the context of the same - // receiver (so class type variables represent the same type each time this - // getter is invoked, and we can't have member type variables in a getter). - // Hence, we just need to check that `e` is stable. - node.expression.accept(this); - } - - @override - void visitListLiteral(ListLiteral node) { - if (!node.isConst) isStable = false; - } - - @override - void visitMethodInvocation(MethodInvocation node) { - if (node.staticType?.isBottom ?? true) return; - // Special case `identical`. - if (node.target == null) { - var nodeLibrary = node.methodName.staticElement?.declaration?.library; - var isFromCore = nodeLibrary?.isDartCore ?? false; - if (isFromCore && node.methodName.name == 'identical') { - var arguments = node.argumentList.arguments; - if (arguments.length == 2) { - // `identical(e1, e2)` is stable iff `e1` and `e2` are stable. - arguments[0].accept(this); - arguments[1].accept(this); - return; - } - } - } - // We could have a notion of pure functions, but for now a - // method invocation is never stable. - isStable = false; - } - - @override - void visitNamedExpression(NamedExpression node) { - if (node.staticType?.isBottom ?? true) return; - node.expression.accept(this); - } - - @override - void visitNode(AstNode node) { - // Default method, only called if the specific visitor method is missing. - assert(false, 'Missing visit method detected!'); - // It is always safe to consider an unknown construct unstable. - isStable = false; - } - - @override - void visitNullLiteral(NullLiteral node) { - // Keep it stable. - } - - @override - void visitParenthesizedExpression(ParenthesizedExpression node) { - if (node.staticType?.isBottom ?? true) return; - node.unParenthesized.accept(this); - } - - @override - void visitPostfixExpression(PostfixExpression node) { - if (node.staticType?.isBottom ?? true) return; - if (node.operator.type == TokenType.BANG) { - // A non-null assertion expression, `e!`: stable if `e` is stable. - node.operand.accept(this); - } else { - // `x.y?.z` is handled in [visitPropertyAccess], this is only about - // ` `, and they are not stable. - isStable = false; - } - } - - @override - void visitPrefixedIdentifier(PrefixedIdentifier node) { - var prefixDeclaration = node.prefix.staticElement?.declaration; - var declaredElement = node.identifier.staticElement?.declaration; - if (prefixDeclaration is PrefixElement) { - // Import prefix followed by simple identifier. - if (!_isStable(declaredElement)) isStable = false; - } else if (prefixDeclaration is InterfaceElement) { - // Static member access. - if (!_isStable(declaredElement)) isStable = false; - } else { - node.prefix.accept(this); - if (isStable) { - if (!_isStable(declaredElement)) isStable = false; - } - } - } - - @override - void visitPrefixExpression(PrefixExpression node) { - var operandType = node.staticType; - if (operandType == null) return; // Program error, play safe. - if (operandType.isBottom) return; // Will throw, is stable. - if (node.operator.type == TokenType.MINUS) { - node.operand.accept(this); - if (!isStable) return; - if (operandType.isDartCoreInt || - operandType.isDartCoreDouble || - operandType.isDartCoreNum) { - // These all have a stable unary minus. - return; - } else { - // A user-defined unary minus cannot be assumed to be stable. - isStable = false; - } - } else { - // TODO(eernst): Could probably support more unary operators. - isStable = false; - } - } - - @override - void visitPropertyAccess(PropertyAccess node) { - node.realTarget.accept(this); - if (isStable) { - var element = node.propertyName.staticElement?.declaration; - if (!_isStable(element)) isStable = false; - } - } - - @override - void visitRecordLiteral(RecordLiteral node) { - // Record literals do not have guaranteed identity. - isStable = false; - } - - @override - void visitRethrowExpression(RethrowExpression node) { - // Throws, cannot be unstable. - } - - @override - void visitReturnStatement(ReturnStatement node) { - node.expression?.accept(this); - } - - @override - void visitSetOrMapLiteral(SetOrMapLiteral node) { - if (!node.isConst) isStable = false; - } - - @override - void visitSimpleIdentifier(SimpleIdentifier node) { - var declaration = node.staticElement?.declaration; - if (!_isStable(declaration)) { - isStable = false; - } - } - - @override - void visitSimpleStringLiteral(SimpleStringLiteral node) { - // No interpolations: Keep it stable. - } - - @override - void visitStringInterpolation(StringInterpolation node) { - var interpolationElements = node.elements; - for (var interpolationElement in interpolationElements) { - if (interpolationElement is InterpolationExpression) { - var expression = interpolationElement.expression; - var expressionType = expression.staticType; - if (expressionType == null) { - isStable = false; - } else if (expressionType.isDartCoreInt || - expressionType.isDartCoreDouble || - expressionType.isDartCoreString || - expressionType.isDartCoreBool || - expressionType.isDartCoreNull) { - // `toString` on these built-in types is stable. - interpolationElement.expression.accept(this); - } else { - // `toString` is otherwise unstable, can run arbitrary code. - isStable = false; - } - } - } - } - - @override - void visitSuperExpression(SuperExpression node) { - // This is simply the keyword `super`: Keep it stable. - } - - @override - void visitSwitchExpression(SwitchExpression node) { - // TODO(eernst): We could include this, if the scrutinee and every branch is stable - isStable = false; - } - - @override - void visitSymbolLiteral(SymbolLiteral node) { - // Keep it stable. - } - - @override - void visitThisExpression(ThisExpression node) { - // Keep it stable. - } - - @override - void visitThrowExpression(ThrowExpression node) { - // Keep it stable. - } - - @override - void visitTypeLiteral(TypeLiteral node) { - // A type literal can contain a type parameter of the enclosing class - // (in a getter it can't be a type parameter of the member). This means - // that it denotes the same type each time it is evaluated on the same - // receiver, but it may still be a different object. So we need to exclude - // type literals that contain type variables. - - bool containsNonConstantType(TypeAnnotation? typeAnnotation) { - if (typeAnnotation == null) return false; - if (typeAnnotation is NamedType) { - var typeArguments = typeAnnotation.typeArguments; - if (typeArguments != null) { - for (var typeArgument in typeArguments.arguments) { - if (containsNonConstantType(typeArgument)) return true; - } - } - var typeAnnotationType = typeAnnotation.type; - if (typeAnnotationType is InterfaceType) { - var element = typeAnnotationType.element.declaration; - if (element is InterfaceElement) return false; - } else if (typeAnnotationType is TypeParameterType) { - // We cannot rely on type parameters or parameterized types - // containing type parameters to be stable. - return true; - } - // TODO(eernst): Handle `typedef` and other missing cases. - return true; - } else if (typeAnnotation is GenericFunctionType) { - // TODO(eernst): For now, just use the safe approximation. - return true; - } else { - // TODO(eernst): Add missing cases. Be safe for now. - return true; - } - } - - if (containsNonConstantType(node.type)) isStable = false; - } - - List _computeContextMessages() { - var contextMessages = []; - for (var cause in causes) { - var length = cause.nameLength; - var offset = cause.nameOffset; - if (offset < 0) offset = cause.variable.nameOffset; - if (offset < 0) offset = 0; - contextMessages.add( - DiagnosticMessageImpl( - filePath: cause.library.source.fullName, - message: 'The declaration that requires this ' - 'declaration to be stable is', - offset: offset, - length: length, - url: null), - ); - } - return contextMessages; - } - - bool _inheritsStability(InterfaceElement interfaceElement, Name name) { - // A member of an extension type is never executed when some different - // declaration (in a class or in an extension type) is the statically - // known declaration. Hence, stability is not inherited. - if (interfaceElement is ExtensionTypeElement) return false; - // A member of a class/mixin/enum can inherit a stability requirement. - var overriddenList = - context.inheritanceManager.getOverridden2(interfaceElement, name); - if (overriddenList == null) return false; - for (var overridden in overriddenList) { - if (_isLocallyStable(overridden)) { - if (overridden is PropertyAccessorElement) causes.add(overridden); - return true; - } - var enclosingElement = overridden.enclosingElement; - if (enclosingElement is InterfaceElement) { - if (_inheritsStability(enclosingElement, name)) { - return true; - } - } - } - return false; - } - - bool _isStable(Element? element) { - if (element == null) return false; // This would be an error in the program. - var enclosingElement = element.enclosingElement; - if (_isLocallyStable(element)) return true; - if (element is PropertyAccessorElement) { - if (element.isStatic) return false; - if (enclosingElement is! InterfaceElement) { - // This should not happen, a top-level variable `isStatic`. - // TODO(eernst): Do something like `throw Unhandled(...)`. - return false; - } - var libraryUri = element.library.source.uri; - var name = Name(libraryUri, element.name); - return _inheritsStability(enclosingElement, name); - } - return false; - } -} - -class _FieldVisitor extends _AbstractVisitor { - _FieldVisitor(super.rule, super.context); - - @override - void visitFieldDeclaration(FieldDeclaration node) { - Uri? libraryUri; - Name? name; - InterfaceElement? interfaceElement; - for (var variable in node.fields.variables) { - var declaredElement = variable.declaredElement; - if (declaredElement is FieldElement) { - // A final instance variable can never violate stability. - if (declaredElement.isFinal) continue; - // A non-final instance variable is always a violation of stability. - // Check if stability is required. - interfaceElement ??= - declaredElement.enclosingElement as InterfaceElement; - libraryUri ??= declaredElement.library.source.uri; - name ??= Name(libraryUri, declaredElement.name); - if (_inheritsStability(interfaceElement, name)) { - doReportLint(variable.name); - } - } - } - } -} - -class _MethodVisitor extends _AbstractVisitor { - _MethodVisitor(super.rule, super.context); - - @override - void visitMethodDeclaration(MethodDeclaration node) { - if (!node.isGetter) return; - declaration = node; - var declaredElement = node.declaredElement; - if (declaredElement != null) { - var enclosingElement = declaredElement.enclosingElement; - if (enclosingElement is InterfaceElement) { - var libraryUri = declaredElement.library.source.uri; - var name = Name(libraryUri, declaredElement.name); - if (!_inheritsStability(enclosingElement, name)) return; - node.body.accept(this); - if (!isStable) doReportLint(node.name); - } else { - // Extensions cannot override anything. - } - } - } -} - -class _Visitor extends SimpleAstVisitor { - final LintRule rule; - final LinterContext context; - - _Visitor(this.rule, this.context); - - @override - void visitFieldDeclaration(FieldDeclaration node) { - if (!node.isStatic) { - var visitor = _FieldVisitor(rule, context); - visitor.visitFieldDeclaration(node); - } - } - - @override - void visitMethodDeclaration(MethodDeclaration node) { - if (!node.isStatic && node.isGetter) { - var visitor = _MethodVisitor(rule, context); - visitor.visitMethodDeclaration(node); - } - } + NodeLintRegistry registry, LinterContext context) {} } diff --git a/pkg/linter/test/rules/all.dart b/pkg/linter/test/rules/all.dart index e31a33b5f9a..3137ca3532b 100644 --- a/pkg/linter/test/rules/all.dart +++ b/pkg/linter/test/rules/all.dart @@ -50,7 +50,6 @@ import 'avoid_types_as_parameter_names_test.dart' import 'avoid_types_on_closure_parameters_test.dart' as avoid_types_on_closure_parameters; import 'avoid_unnecessary_containers_test.dart' as avoid_unnecessary_containers; -import 'avoid_unstable_final_fields_test.dart' as avoid_unstable_final_fields; import 'avoid_unused_constructor_parameters_test.dart' as avoid_unused_constructor_parameters; import 'avoid_void_async_test.dart' as avoid_void_async; @@ -288,7 +287,6 @@ void main() { avoid_types_as_parameter_names.main(); avoid_types_on_closure_parameters.main(); avoid_unnecessary_containers.main(); - avoid_unstable_final_fields.main(); avoid_unused_constructor_parameters.main(); avoid_void_async.main(); avoid_web_libraries_in_flutter.main(); diff --git a/pkg/linter/test/rules/avoid_unstable_final_fields_test.dart b/pkg/linter/test/rules/avoid_unstable_final_fields_test.dart deleted file mode 100644 index 718471b6bf9..00000000000 --- a/pkg/linter/test/rules/avoid_unstable_final_fields_test.dart +++ /dev/null @@ -1,1497 +0,0 @@ -// Copyright (c) 2023, 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:test_reflective_loader/test_reflective_loader.dart'; - -import '../rule_test_support.dart'; - -main() { - defineReflectiveSuite(() { - defineReflectiveTests(AvoidUnstableFinalFieldsTest); - }); -} - -@reflectiveTest -class AvoidUnstableFinalFieldsTest extends LintRuleTest { - @override - String get lintRule => 'avoid_unstable_final_fields'; - - test_00lintGetMutableToplevelVariable() async { - await assertDiagnostics(r''' -class A { - final int i; - A(this.i); -} - -var jTop = 0; - -class B01 extends A { - B01(super.i); - int get i => ++jTop + super.i; //LINT -} -''', [ - _lint(104), - ]); - } - - test_01lintOverrideMutableVariable() async { - await assertDiagnostics(r''' -class A { - final int i; - A(this.i); -} - -class B02 implements A { - int i; //LINT - B02(this.i); -} -''', [ - _lint(72), - ]); - } - - test_02noLintLateInitializedVariable() async { - await assertNoDiagnostics(r''' -class A { - final int i; - A(this.i); -} - -var jTop = 0; - -class B03 implements A { - late final int i = jTop++; //OK -} -'''); - } - - test_03noLintSuper() async { - await assertNoDiagnostics(r''' -class A { - final int i; - A(this.i); -} - -class B04 extends A { - B04(super.i); - int get i => super.i - 1; //OK -} -'''); - } - - test_04noLintStableInstanceGetter() async { - await assertNoDiagnostics(r''' -class A { - final int i; - A(this.i); -} - -class B05 implements A { - final int j; - B05(this.j); - int get i => j; //OK -} -'''); - } - - test_05noLintConstant() async { - await assertNoDiagnostics(r''' -class A { - final int i; - A(this.i); -} - -class B06 implements A { - int get i => 1; //OK -} -'''); - } - - test_06noLintConstantInBody() async { - await assertNoDiagnostics(r''' -class A { - final int i; - A(this.i); -} - -class B07 implements A { - int get i //OK - { - return 1; - } -} -'''); - } - - test_07lintMutableToplevelVariableInBody() async { - await assertDiagnostics(r''' -class A { - final int i; - A(this.i); -} - -var jTop = 0; - -class B08 implements A { - int get i //LINT - { - return jTop; - } -} -''', [ - _lint(91), - ]); - } - - test_08noLintUnaryMinus() async { - await assertNoDiagnostics(r''' -class A { - final int i; - A(this.i); -} - -class B09 extends A { - B09(super.i); - int get i => -super.i; //OK -} -'''); - } - - test_09noLintConditional() async { - await assertNoDiagnostics(r''' -class A { - final int i; - A(this.i); -} - -class B10 implements A { - final bool b; - final int j; - B10(this.b, this.j); - int get i => b ? j : 10; //OK -} -'''); - } - - test_10lintUnstableCondition() async { - await assertDiagnostics(r''' -class A { - final int i; - A(this.i); -} - -class B11 implements A { - bool b; - final int j; - B11(this.b, this.j); - int get i => b ? 2 * j : 10; //LINT -} -''', [ - _lint(124), - ]); - } - - test_11noLintThrow() async { - await assertNoDiagnostics(r''' -class A { - final int i; - A(this.i); -} - -class B12 implements A { - int get i => throw 0; //OK -} -'''); - } - - test_12noLintThrowInBody() async { - await assertNoDiagnostics(r''' -class A { - final int i; - A(this.i); -} - -class B13 implements A { - int get i //OK - { - throw 0; - } -} -'''); - } - - test_13lintMutableLateInstanceVariable() async { - await assertDiagnostics(r''' -class C { - final C? next; - final C? nextNext = null; - final X? v = null; - const C(this.next); -} - -const cNever = C(null); - -class D1 implements C { - late X x; - C? get next => cNever; //OK - C? get nextNext => this.next?.next; //OK - X? get v => x; //LINT -} -''', [ - _lint(272), - ]); - } - - test_14lintInstanceTearoff() async { - await assertDiagnostics(r''' -class E { - final Object? o; - E(this.o); -} - -class F01 implements E { - static late final Function fStatic = () {}; - Function get o => m; //LINT - void m() {} -} -''', [ - _lint(131), - ]); - } - - test_15lintFunctionLiteral() async { - await assertDiagnostics(r''' -class E { - final Object? o; - E(this.o); -} - -class F02 implements E { - Function get o => () {}; //LINT -} -''', [ - _lint(85), - ]); - } - - test_16noLintCascade() async { - await assertNoDiagnostics(r''' -class E { - final Object? o; - E(this.o); -} - -class F03 implements E { - Function get o => print..toString(); //OK -} -'''); - } - - test_17noLintImportPrefix() async { - await assertNoDiagnostics(r''' -import 'dart:math' as math; - -class E { - final Object? o; - E(this.o); -} - -class F04 implements E { - Function get o => math.cos; //OK -} -'''); - } - - test_18noLintStaticTearOff() async { - await assertNoDiagnostics(r''' -class E { - final Object? o; - E(this.o); - static late final Function eStatic = () {}; -} - -class F05 implements E { - Function get o => E.eStatic; //OK -} -'''); - } - - test_19lintListLiteral() async { - await assertDiagnostics(r''' -class E { - final Object? o; - E(this.o); -} - -class F06 implements E { - List get o => []; //LINT -} -''', [ - _lint(86), - ]); - } - - test_20noLintConstantMap() async { - await assertNoDiagnostics(r''' -class E { - final Object? o; - E(this.o); -} - -class F07 implements E { - Set get o => const {}; //OK -} -'''); - } - - test_21lintMapLiteral() async { - await assertDiagnostics(r''' -class E { - final Object? o; - E(this.o); -} - -class F08 implements E { - Object get o => {}; //LINT -} -''', [ - _lint(83), - ]); - } - - test_22noLintConstantSymbol() async { - await assertNoDiagnostics(r''' -class E { - final Object? o; - E(this.o); -} - -class F09 implements E { - Symbol get o => #symbol; //OK -} -'''); - } - - test_23noLintConstantTypeLiteral() async { - await assertNoDiagnostics(r''' -class E { - final Object? o; - E(this.o); -} - -class F10 implements E { - Type get o => int; //OK -} -'''); - } - - test_24lintTypeParameter() async { - await assertDiagnostics(r''' -class E { - final Object? o; - E(this.o); -} - -class F11 implements E { - Type get o => X; //LINT -} -''', [ - _lint(84), - ]); - } - - test_25lintUnstableTypeLiteral() async { - await assertDiagnostics(r''' -class E { - final Object? o; - E(this.o); -} - -class F12 implements E { - Type get o => F12; //LINT -} -''', [ - _lint(84), - ]); - } - - test_26noLintConstantObjectExpression() async { - await assertNoDiagnostics(r''' -class E { - final Object? o; - E(this.o); -} - -class F13 implements E { - const F13(int whatever); - F13 get o => const F13(42); //OK -} -'''); - } - - test_27lintNewInstance() async { - await assertDiagnostics(r''' -class E { - final Object? o; - E(this.o); -} - -var jTop = 0; - -class F14 implements E { - F14(int whatever); - F14 get o => F14(jTop); //LINT -} -''', [ - _lint(116), - ]); - } - - test_28noLintStringLiteral() async { - await assertNoDiagnostics(r''' -class E { - final Object? o; - E(this.o); -} - -class F15 implements E { - String get o => 'Something and ${1 * 1}'; //OK -} -'''); - } - - test_29lintUnstableStringLiteral() async { - await assertDiagnostics(r''' -class E { - final Object? o; - E(this.o); -} - -const cNever = 42; - -var jTop = 0; - -class F16 implements E { - String get o => 'Stuff, $cNever, but not $jTop'; //LINT -} -''', [ - _lint(118), - ]); - } - - test_30noLintTypeTest() async { - await assertNoDiagnostics(r''' -class E { - final Object? o; - E(this.o); -} - -class F17 implements E { - bool get o => (this as dynamic) is E; //OK -} -'''); - } - - test_31lintUnstableTypeTest() async { - await assertDiagnostics(r''' -class E { - final Object? o; - E(this.o); -} - -dynamic jTop = 0; - -class F18 implements E { - bool get o => jTop is int; //LINT -} -''', [ - _lint(100), - ]); - } - - test_32noLintNonnullCheck() async { - await assertNoDiagnostics(r''' -class E { - final Object? o; - E(this.o); -} - -class F19 extends E { - F19(super.o); - Object get o => super.o!; //OK -} -'''); - } - - test_33noLintConstantObjectExpressionNamed() async { - await assertNoDiagnostics(r''' -class E { - final Object? o; - E(this.o); -} - -class F20 implements E { - F20Helper get o => const F20Helper.named(15); //OK -} - -class F20Helper { - const F20Helper.named(int i); -} -'''); - } - - test_34noLintIdentical() async { - await assertNoDiagnostics(r''' -class E { - final Object? o; - E(this.o); -} - -class F21 implements E { - bool get o => identical(const [], const []); //OK -} -'''); - } - - test_35lintUnstableIdentical() async { - await assertDiagnostics(r''' -class E { - final Object? o; - E(this.o); -} - -class F22 implements E { - bool get o => identical([], const []); //LINT -} -''', [ - _lint(81), - ]); - } - - test_36noLintDeclaredUnstableStringLiteral() async { - await assertNoDiagnostics(r''' -class G { - @Object() - final String s; - G(this.s); -} - -var jTop = 0; - -class H1 implements G { - String get s => '${++jTop}'; //OK -} -'''); - } - - test_37lintMutableToplevelVariablePlusSuperInMixin() async { - await assertDiagnostics(r''' -class I { - final int i; - I(this.i); -} - -var jTop = 0; - -mixin J1 on I { - int get i => ++jTop + super.i; //LINT -} -''', [ - _lint(82), - ]); - } - - test_38lintMutableToplevelVariableInMixin() async { - await assertDiagnostics(r''' -class I { - final int i; - I(this.i); -} - -var jTop = 0; - -mixin J2 implements I { - int get i => ++jTop; //LINT -} -''', [ - _lint(90), - ]); - } - - test_39noLintGetterInMixinBinary() async { - await assertNoDiagnostics(r''' -class I { - final int i; - I(this.i); -} - -mixin J3 on I { - int get i => super.i - 1; //OK -} -'''); - } - - test_40noLintGetterInMixinConstant() async { - await assertNoDiagnostics(r''' -class I { - final int i; - I(this.i); -} - -mixin J4 implements I { - int get i => 1; //OK -} -'''); - } - - test_41noLintGetterInMixinUnaryMinus() async { - await assertNoDiagnostics(r''' -class I { - final int i; - I(this.i); -} - -mixin J5 on I { - int get i => -super.i; //OK -} -'''); - } - - test_42noLintGetterInMixinNonnullCheck() async { - await assertNoDiagnostics(r''' -class K { - final Object? o; - K(this.o); -} - -mixin L1 on K { - Object get o => super.o!; //OK -} -'''); - } - - test_43noLintDeclaredUnstableStringLiteralInMixin() async { - await assertNoDiagnostics(r''' -class M { - @Object() - final String s; - M(this.s); -} - -var jTop = 0; - -mixin N1 on M { - String get s => '$jTop'; //OK -} -'''); - } - - test_44noLintDeclaredUnstableStringLiteralUnaryMinusInMixin() async { - await assertNoDiagnostics(r''' -class M { - @Object() - final String s; - M(this.s); -} - -var jTop = 0; - -mixin N2 implements M { - String get s => '${-jTop}'; //OK -} -'''); - } - - test_45noLintInitializedFinalVariable() async { - await assertNoDiagnostics(r''' -abstract class O { - final int i; - O(this.i); -} - -var jTop = 0; - -enum P1 implements O { - a, - b, - c; - - final int i = jTop++; //OK -} -'''); - } - - test_46noLintStableVariableInEnum() async { - await assertNoDiagnostics(r''' -abstract class O { - final int i; - O(this.i); -} - -enum P2 implements O { - a(10), - b(12), - c(14); - - final int j; - const P2(this.j); - int get i => j; //OK -} -'''); - } - - test_47noLintConstantInEnum() async { - await assertNoDiagnostics(r''' -abstract class O { - final int i; - O(this.i); -} - -enum P3 implements O { - a, - b, - c; - - int get i => 1; //OK -} -'''); - } - - test_48noLintConstantInBodyInEnum() async { - await assertNoDiagnostics(r''' -abstract class O { - final int i; - O(this.i); -} - -enum P4 implements O { - a, - b, - c; - - int get i //OK - { - return 1; - } -} -'''); - } - - test_49lintMutableToplevelVariableInEnum() async { - await assertDiagnostics(r''' -abstract class O { - final int i; - O(this.i); -} - -var jTop = 0; - -enum P5 implements O { - a, - b, - c; - - int get i //LINT - { - return jTop; - } -} -''', [ - _lint(114), - ]); - } - - test_50noLintConditionalInEnum() async { - await assertNoDiagnostics(r''' -abstract class O { - final int i; - O(this.i); -} - -enum P6 implements O { - aa(true, 4), - bb(true, 8), - cc(false, 12); - - final bool b; - final int j; - const P6(this.b, this.j); - int get i => b ? j : 10; //OK -} -'''); - } - - test_51lintUnstableConditionInEnum() async { - await assertDiagnostics(r''' -abstract class O { - final int i; - O(this.i); -} - -bool bTop = true; - -enum P7 implements O { - aa(3), - bb(5), - cc(7); - - final int j; - const P7(this.j); - int get i => bTop ? 2 * j : 10; //LINT -} -''', [ - _lint(165), - ]); - } - - test_52noLintThrowInEnum() async { - await assertNoDiagnostics(r''' -abstract class O { - final int i; - O(this.i); -} - -enum P8 implements O { - a, - b, - c; - - int get i => throw 0; //OK -} -'''); - } - - test_53noLintThrowInBodyInEnum() async { - await assertNoDiagnostics(r''' -abstract class O { - final int i; - O(this.i); -} - -enum P9 implements O { - a, - b, - c; - - int get i //OK - { - throw 0; - } -} -'''); - } - - test_54lintInstanceTearoffInEnum() async { - await assertDiagnostics(r''' -abstract class Q { - abstract final Object? o; -} - -enum R01 implements Q { - a, - b, - c; - - static late final Function fStatic = () {}; - Function get o => m; //LINT - void m() {} -} -''', [ - _lint(151), - ]); - } - - test_55lintFunctionLiteralInEnum() async { - await assertDiagnostics(r''' -abstract class Q { - abstract final Object? o; -} - -enum R02 implements Q { - a, - b, - c; - - Function get o => () {}; //LINT -} -''', [ - _lint(105), - ]); - } - - test_56noLintCascadeInEnum() async { - await assertNoDiagnostics(r''' -abstract class Q { - abstract final Object? o; -} - -enum R03 implements Q { - a, - b, - c; - - Function get o => print..toString(); //OK -} -'''); - } - - test_57noLintImportPrefixInEnum() async { - await assertNoDiagnostics(r''' -import 'dart:math' as math; - -abstract class Q { - abstract final Object? o; -} - -enum R04 implements Q { - a, - b, - c; - - Function get o => math.cos; //OK -} -'''); - } - - test_58noLintStaticTearOffInEnum() async { - await assertNoDiagnostics(r''' -abstract class Q { - abstract final Object? o; - static late final Function qStatic = () {}; -} - -enum R05 implements Q { - a, - b, - c; - - Function get o => Q.qStatic; //OK -} -'''); - } - - test_59lintListLiteralInEnum() async { - await assertDiagnostics(r''' -abstract class Q { - abstract final Object? o; -} - -enum R06 implements Q { - a, - b, - c; - - List get o => []; //LINT -} -''', [ - _lint(106), - ]); - } - - test_60noLintConstantSetInEnum() async { - await assertNoDiagnostics(r''' -abstract class Q { - abstract final Object? o; -} - -enum R07 implements Q { - a, - b, - c; - - Set get o => const {}; //OK -} -'''); - } - - test_61lintMapLiteralInEnum() async { - await assertDiagnostics(r''' -abstract class Q { - abstract final Object? o; -} - -enum R08 implements Q { - a, - b, - c; - - Object get o => {}; //LINT -} -''', [ - _lint(103), - ]); - } - - test_62noLintConstantSymbolInEnum() async { - await assertNoDiagnostics(r''' -abstract class Q { - abstract final Object? o; -} - -enum R09 implements Q { - a, - b, - c; - - Symbol get o => #symbol; //OK -} -'''); - } - - test_63noLintConstantTypeLiteralInEnum() async { - await assertNoDiagnostics(r''' -abstract class Q { - abstract final Object? o; -} - -enum R10 implements Q { - a, - b, - c; - - Type get o => int; //OK -} -'''); - } - - test_64lintTypeParameterInEnum() async { - await assertDiagnostics(r''' -abstract class Q { - abstract final Object? o; -} - -enum R11 implements Q { - a, - b, - c; - - Type get o => X; //LINT -} -''', [ - _lint(104), - ]); - } - - test_65lintUnstableTypeLiteralInEnum() async { - await assertDiagnostics(r''' -abstract class Q { - abstract final Object? o; -} - -enum R12 implements Q { - a, - b, - c; - - Type get o => R12; //LINT -} -''', [ - _lint(104), - ]); - } - - test_66noLintConstantObjectExpressionInEnum() async { - await assertNoDiagnostics(r''' -abstract class Q { - abstract final Object? o; -} - -class C { - const C(); -} - -enum R13 implements Q { - a(-1), - b(-2), - c(-3); - - const R13(int whatever); - C get o => const C(); //OK -} -'''); - } - - test_67lintNewInstanceInEnum() async { - await assertDiagnostics(r''' -abstract class Q { - abstract final Object? o; -} - -var jTop = 0; - -enum R14 implements Q { - a(-10), - b(-20), - c(-30); - - const R14(int whatever); - List get o => List.from(const [], growable: false); //LINT -} -''', [ - _lint(166), - ]); - } - - test_68noLintStringLiteralInEnum() async { - await assertNoDiagnostics(r''' -abstract class Q { - abstract final Object? o; -} - -enum R15 implements Q { - a, - b, - c; - - String get o => 'Something and ${1 * 1}'; //OK -} -'''); - } - - test_69lintUnstableStringLiteralInEnum() async { - await assertDiagnostics(r''' -abstract class Q { - abstract final Object? o; -} - -const cNever = 42; - -var jTop = 0; - -enum R16 implements Q { - a, - b, - c; - - String get o => 'Stuff, $cNever, but not $jTop'; //LINT -} -''', [ - _lint(138), - ]); - } - - test_70noLintTypeTestInEnum() async { - await assertNoDiagnostics(r''' -abstract class Q { - abstract final Object? o; -} - -enum R17 implements Q { - a, - b, - c; - - bool get o => (this as dynamic) is Q; //OK -} -'''); - } - - test_71lintUnstableTypeTestInEnum() async { - await assertDiagnostics(r''' -abstract class Q { - abstract final Object? o; -} - -dynamic jTop = 0; - -enum R18 implements Q { - a, - b, - c; - - bool get o => jTop is int; //LINT -} -''', [ - _lint(120), - ]); - } - - test_72noLintEnumInEnum() async { - await assertNoDiagnostics(r''' -abstract class Q { - abstract final Object? o; -} - -enum R20 implements Q { - a, - b, - c; - - R20Helper get o => R20Helper.b; //OK -} - -enum R20Helper { - a.named(999), - b.named(-0.888), - c.named(77.7); - - const R20Helper.named(num n); -} -'''); - } - - test_73noLintIdenticalInEnum() async { - await assertNoDiagnostics(r''' -abstract class Q { - abstract final Object? o; -} - -enum R21 implements Q { - a, - b, - c; - - bool get o => identical(const [], const []); //OK -} -'''); - } - - test_74lintUnstableIdenticalInEnum() async { - await assertDiagnostics(r''' -abstract class Q { - abstract final Object? o; -} - -enum R22 implements Q { - a, - b, - c; - - bool get o => identical([], const []); //LINT -} -''', [ - _lint(101), - ]); - } - - // Make sure the lint in `test_76lintMutableVariableDoubleOverride` - // is actually caused by the second override. - test_75noLintSingleOverride() async { - await assertNoDiagnostics(r''' -class S01 { - const S01(); - final int b = 0; - final String s = ''; -} - -class S02 extends S01 { - @override - int get b => 0; //OK -} -'''); - } - - test_76lintMutableVariableDoubleOverride() async { - await assertDiagnostics(r''' -class S01 { - const S01(); - final int b = 0; - final String s = ''; -} - -class S02 extends S01 { - @override - int get b => 0; //OK -} - -int get i2 => _i2++; -int _i2 = 0; - -class S03 extends S02 { - @override - int get b => i2; //LINT -} -''', [ - _lint(215), - ]); - } - - test_77lintToStringViaStringLiteral() async { - await assertDiagnostics(r''' -class S01 { - const S01(); - final int b = 0; - final String s = ''; -} - -const S04 constS04 = S04(); - -int get i2 => _i2++; -int _i2 = 0; - -class S04 extends S01 { - const S04(); - - @override - String toString() => '$i2'; - - @override - String get s => '$constS04'; //LINT -} -''', [ - _lint(244), - ]); - } - - @FailingTest( - issue: 'https://github.com/dart-lang/linter/issues/4766', - reason: 'Not yet implemented', - ) - test_78lintNosuchMethodForwarder() async { - await assertDiagnostics(r''' -class S01 { - const S01(); - final int b = 0; - final String s = ''; -} - -int get i2 => _i2++; -int _i2 = 0; - -class S05 implements S01 { //LINT - @override - dynamic noSuchMethod(_) => i2; -} -''', [ - _lint(107), - ]); - } - - @FailingTest( - issue: 'https://github.com/dart-lang/linter/issues/4765', - reason: 'Not yet implemented', - ) - test_79lintUnstableTopLevelGetterMixedIn() async { - await assertDiagnostics(r''' -class S01 { - const S01(); - final int b = 0; - final String s = ''; -} - -int get i2 => _i2++; -int _i2 = 0; - -mixin S06 { - int get b => i2; -} - -class S07 extends S01 with S06 {} //LINT -''', [ - _lint(141), - ]); - } - - @FailingTest( - issue: 'https://github.com/dart-lang/linter/issues/4764', - reason: 'Not yet implemented', - ) - test_80lintStabilityFromImplementsToInherited() async { - await assertDiagnostics(r''' -class S01 { - const S01(); - final int b = 0; - final String s = ''; -} - -class S08 extends S08Super implements S01 {} //LINT - -int get i2 => _i2++; -int _i2 = 0; - -class S08Super { - int get b => i2; - String get s => "$b"; -} -''', [ - _lint(72), - ]); - } - - test_81lintUnstableGettersOnOther() async { - await assertDiagnostics(r''' -class S01 { - const S01(); - final int b = 0; - final String s = ''; -} - -class S09 extends S01 { - final S09Helper sh = S09Helper(); - - @override - int get b => sh.z + sh.x; //LINT -} - -int get i2 => _i2++; -int _i2 = 0; - -class S09Helper { - int get x => i2; - int z = 1; -} -''', [ - _lint(155), - ]); - } - - test_82lintUnstableEquality() async { - await assertDiagnostics(r''' -class S01 { - const S01(); - final int b = 0; - final String s = ''; -} - -class S10 extends S01 { - final S10Helper sh = S10Helper(); - - @override - int get b => (sh == sh) ? 0 : 1; //LINT -} - -int get i2 => _i2++; -int _i2 = 0; - -class S10Helper { - @override - bool operator ==(Object other) => i2.isEven; -} -''', [ - _lint(155), - ]); - } - - test_83lintUnstableGetterOnOther() async { - await assertDiagnostics(r''' -class S01 { - const S01(); - final int b = 0; - final String s = ''; -} - -class S11 extends S01 { - final S11Helper kh = S11Helper(); - - @override - int get b => kh.b; //LINT -} - -int get i2 => _i2++; -int _i2 = 0; - -class S11Helper { - int get b => i2; -} -''', [ - _lint(155), - ]); - } - - ExpectedLint _lint(int offset) => lint( - offset, - 1, - messageContains: 'Avoid overriding a final field', - ); -}