Reverts "[ Analysis ] Added initial implementation of the flutter_analyzer_plugin (#175679)" (#179766)
<!-- start_original_pr_link --> Reverts: flutter/flutter#175679 <!-- end_original_pr_link --> <!-- start_initiating_author --> Initiated by: jtmcdole <!-- end_initiating_author --> <!-- start_revert_reason --> Reason for reverting: Linux linux_unopt is now timing out in postsubmit <!-- end_revert_reason --> <!-- start_original_pr_author --> Original PR Author: bkonyi <!-- end_original_pr_author --> <!-- start_reviewers --> Reviewed By: {srawlins} <!-- end_reviewers --> <!-- start_revert_body --> This change reverts the following previous change: The `flutter_analyzer_plugin` implements rules previously enforced by the `dev/bots/analyze.dart` check run on the CI, allowing for earlier detection of custom lint violations before a change is uploaded for review. Currently, the plugin implements the following rules: - avoid_future_catch_error - no_double_clamp - no_stopwatches - protect_public_state_subtypes - render_box_intrinsics Towards https://github.com/flutter/flutter/issues/175276 <!-- end_revert_body --> Co-authored-by: auto-submit[bot] <flutter-engprod-team@google.com>
This commit is contained in:
@@ -13,16 +13,6 @@
|
||||
# This file contains the analysis options used for code in the flutter/flutter
|
||||
# repository.
|
||||
|
||||
plugins:
|
||||
flutter_analyzer_plugin:
|
||||
path: dev/flutter_analyzer_plugin
|
||||
diagnostics:
|
||||
avoid_future_catch_error: false
|
||||
no_double_clamp: false
|
||||
no_stopwatches: false
|
||||
protect_public_state_subtypes: false
|
||||
render_box_intrinsics: false
|
||||
|
||||
analyzer:
|
||||
language:
|
||||
strict-casts: true
|
||||
@@ -34,7 +24,6 @@ analyzer:
|
||||
# something (https://github.com/flutter/flutter/issues/143312)
|
||||
deprecated_member_use: ignore
|
||||
deprecated_member_use_from_same_package: ignore
|
||||
plugins_in_inner_options: ignore
|
||||
exclude:
|
||||
- "bin/cache/**"
|
||||
# Ignore protoc generated files
|
||||
|
||||
@@ -298,7 +298,6 @@ Future<void> frameworkTestsRunner() async {
|
||||
path.join(flutterRoot, 'dev', 'integration_tests', 'android_semantics_testing'),
|
||||
fatalWarnings: false,
|
||||
);
|
||||
await runDartTest(path.join(flutterRoot, 'dev', 'flutter_analyzer_plugin'));
|
||||
await runFlutterTest(path.join(flutterRoot, 'dev', 'integration_tests', 'ui'));
|
||||
await runFlutterTest(path.join(flutterRoot, 'dev', 'manual_tests'));
|
||||
await runFlutterTest(path.join(flutterRoot, 'dev', 'tools'));
|
||||
|
||||
@@ -2,15 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
abstract class RenderBox {
|
||||
void computeDryBaseline() {}
|
||||
void computeDryLayout() {}
|
||||
void computeDistanceToActualBaseline() {}
|
||||
void computeMaxIntrinsicHeight() {}
|
||||
void computeMinIntrinsicHeight() {}
|
||||
void computeMaxIntrinsicWidth() {}
|
||||
void computeMinIntrinsicWidth() {}
|
||||
}
|
||||
import '../../foo/fake_render_box.dart';
|
||||
|
||||
mixin ARenderBoxMixin on RenderBox {
|
||||
@override
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
abstract class RenderBox {
|
||||
void computeDryBaseline() {}
|
||||
void computeDryLayout() {}
|
||||
void computeDistanceToActualBaseline() {}
|
||||
void computeMaxIntrinsicHeight() {}
|
||||
void computeMinIntrinsicHeight() {}
|
||||
void computeMaxIntrinsicWidth() {}
|
||||
void computeMinIntrinsicWidth() {}
|
||||
}
|
||||
3
dev/flutter_analyzer_plugin/.gitignore
vendored
3
dev/flutter_analyzer_plugin/.gitignore
vendored
@@ -1,3 +0,0 @@
|
||||
# https://dart.dev/guides/libraries/private-files
|
||||
# Created by `dart pub`
|
||||
.dart_tool/
|
||||
@@ -1,7 +0,0 @@
|
||||
# Flutter Analyzer Plugin
|
||||
|
||||
This plugin provides custom lint rules specific to development within flutter/flutter.
|
||||
|
||||
This plugin is a WIP as cases covered by `dev/bots/analyze.dart` are ported to this plugin,
|
||||
with the eventual goal of implementing as many of the checks as possible to reduce the number
|
||||
of analysis failures only discovered by CI checks.
|
||||
@@ -1,5 +0,0 @@
|
||||
include: ../analysis_options.yaml
|
||||
|
||||
linter:
|
||||
rules:
|
||||
unawaited_futures: true
|
||||
@@ -1,28 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. 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:analysis_server_plugin/plugin.dart';
|
||||
import 'package:analysis_server_plugin/registry.dart';
|
||||
import 'src/rules/avoid_future_catch_error.dart';
|
||||
import 'src/rules/no_double_clamp.dart';
|
||||
import 'src/rules/no_stopwatches.dart';
|
||||
import 'src/rules/protect_public_state_subtypes.dart';
|
||||
import 'src/rules/render_box_intrinsics.dart';
|
||||
|
||||
final FlutterAnalyzerPlugin plugin = FlutterAnalyzerPlugin();
|
||||
|
||||
class FlutterAnalyzerPlugin extends Plugin {
|
||||
@override
|
||||
void register(PluginRegistry registry) {
|
||||
registry
|
||||
..registerWarningRule(AvoidFutureCatchError())
|
||||
..registerWarningRule(NoDoubleClamp())
|
||||
..registerWarningRule(NoStopwatches())
|
||||
..registerWarningRule(ProtectPublicStateSubtypes())
|
||||
..registerWarningRule(RenderBoxIntrinsicCalculationRule());
|
||||
}
|
||||
|
||||
@override
|
||||
String get name => 'flutter/flutter analyzer plugin';
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. 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/analysis_rule.dart';
|
||||
import 'package:analyzer/analysis_rule/rule_context.dart';
|
||||
import 'package:analyzer/analysis_rule/rule_visitor_registry.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/dart/ast/visitor.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:analyzer/error/error.dart';
|
||||
|
||||
class AvoidFutureCatchError extends AnalysisRule {
|
||||
AvoidFutureCatchError()
|
||||
: super(
|
||||
name: code.name,
|
||||
description: 'Future.catchError and Future.onError are not type safe.',
|
||||
);
|
||||
|
||||
static const LintCode code = LintCode(
|
||||
'avoid_future_catch_error',
|
||||
'Avoid using Future.catchError',
|
||||
correctionMessage: 'Use Future.then instead (https://github.com/dart-lang/sdk/issues/51248).',
|
||||
severity: DiagnosticSeverity.ERROR,
|
||||
);
|
||||
|
||||
@override
|
||||
LintCode get diagnosticCode => code;
|
||||
|
||||
@override
|
||||
void registerNodeProcessors(RuleVisitorRegistry registry, RuleContext context) {
|
||||
final visitor = _Visitor(this, context);
|
||||
registry.addMethodInvocation(this, visitor);
|
||||
}
|
||||
}
|
||||
|
||||
class _Visitor extends SimpleAstVisitor<void> {
|
||||
_Visitor(this.rule, this.context);
|
||||
|
||||
final AnalysisRule rule;
|
||||
final RuleContext context;
|
||||
|
||||
@override
|
||||
void visitMethodInvocation(MethodInvocation node) {
|
||||
if (node case MethodInvocation(
|
||||
methodName: SimpleIdentifier(name: 'onError' || 'catchError'),
|
||||
realTarget: Expression(staticType: DartType(isDartAsyncFuture: true)),
|
||||
)) {
|
||||
rule.reportAtNode(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. 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/analysis_rule.dart';
|
||||
import 'package:analyzer/analysis_rule/rule_context.dart';
|
||||
import 'package:analyzer/analysis_rule/rule_visitor_registry.dart';
|
||||
import 'package:analyzer/dart/ast/ast.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/error/error.dart';
|
||||
|
||||
/// Verify that we use clampDouble instead of double.clamp for performance
|
||||
/// reasons.
|
||||
///
|
||||
/// See also:
|
||||
/// * https://github.com/flutter/flutter/pull/103559
|
||||
/// * https://github.com/flutter/flutter/issues/103917
|
||||
class NoDoubleClamp extends AnalysisRule {
|
||||
NoDoubleClamp()
|
||||
: super(
|
||||
name: code.name,
|
||||
description:
|
||||
'Verify that we use clampDouble instead of double.clamp for performance reasons.',
|
||||
);
|
||||
|
||||
static const LintCode code = LintCode(
|
||||
'no_double_clamp',
|
||||
'Avoid double.clamp for performance reasons.',
|
||||
correctionMessage: 'Use clampDouble instead.',
|
||||
severity: DiagnosticSeverity.ERROR,
|
||||
);
|
||||
|
||||
@override
|
||||
DiagnosticCode get diagnosticCode => code;
|
||||
|
||||
@override
|
||||
void registerNodeProcessors(RuleVisitorRegistry registry, RuleContext context) {
|
||||
final visitor = _Visitor(this, context);
|
||||
registry.addSimpleIdentifier(this, visitor);
|
||||
}
|
||||
}
|
||||
|
||||
class _Visitor extends SimpleAstVisitor<void> {
|
||||
_Visitor(this.rule, this.context);
|
||||
|
||||
final AnalysisRule rule;
|
||||
final RuleContext context;
|
||||
|
||||
@override
|
||||
void visitSimpleIdentifier(SimpleIdentifier node) {
|
||||
if (node.name != 'clamp' || node.element is! MethodElement) {
|
||||
return;
|
||||
}
|
||||
final bool isAllowed = switch (node.parent) {
|
||||
// PropertyAccess matches num.clamp in tear-off form. Always prefer
|
||||
// doubleClamp over tear-offs: even when all 3 operands are int literals,
|
||||
// the return type doesn't get promoted to int:
|
||||
// final x = 1.clamp(0, 2); // The inferred return type is int, where as:
|
||||
// final f = 1.clamp;
|
||||
// final y = f(0, 2) // The inferred return type is num.
|
||||
PropertyAccess(
|
||||
target: Expression(
|
||||
staticType: DartType(isDartCoreDouble: true) ||
|
||||
DartType(isDartCoreNum: true) ||
|
||||
DartType(isDartCoreInt: true),
|
||||
),
|
||||
) =>
|
||||
false,
|
||||
|
||||
// Expressions like `final int x = 1.clamp(0, 2);` should be allowed.
|
||||
MethodInvocation(
|
||||
target: Expression(staticType: DartType(isDartCoreInt: true)),
|
||||
argumentList: ArgumentList(
|
||||
arguments: [
|
||||
Expression(staticType: DartType(isDartCoreInt: true)),
|
||||
Expression(staticType: DartType(isDartCoreInt: true)),
|
||||
],
|
||||
),
|
||||
) =>
|
||||
true,
|
||||
|
||||
// Otherwise, disallow num.clamp() invocations.
|
||||
MethodInvocation(
|
||||
target: Expression(
|
||||
staticType: DartType(isDartCoreDouble: true) ||
|
||||
DartType(isDartCoreNum: true) ||
|
||||
DartType(isDartCoreInt: true),
|
||||
),
|
||||
) =>
|
||||
false,
|
||||
|
||||
_ => true,
|
||||
};
|
||||
if (!isAllowed) {
|
||||
rule.reportAtNode(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. 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/analysis_rule.dart';
|
||||
import 'package:analyzer/analysis_rule/rule_context.dart';
|
||||
import 'package:analyzer/analysis_rule/rule_visitor_registry.dart';
|
||||
import 'package:analyzer/dart/ast/ast.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/error/error.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
// The comment pattern representing the "flutter_ignore" inline directive that
|
||||
// indicates the line should be exempt from the stopwatch check.
|
||||
final Pattern _ignoreStopwatch = RegExp(r'// flutter_ignore: .*stopwatch .*\(see analyze\.dart\)');
|
||||
|
||||
/// Use of Stopwatches can introduce test flakes as the logical time of a
|
||||
/// stopwatch can fall out of sync with the mocked time of FakeAsync in testing.
|
||||
/// The Clock object provides a safe stopwatch instead, which is paired with
|
||||
/// FakeAsync as part of the test binding.
|
||||
class NoStopwatches extends AnalysisRule {
|
||||
NoStopwatches() : super(name: code.name, description: ruleDescription);
|
||||
|
||||
static const String ruleDescription =
|
||||
'Use of Stopwatches can introduce test flakes as the logical time of a stopwatch can fall '
|
||||
'out of sync with the mocked time of FakeAsync in testing.';
|
||||
|
||||
static const LintCode code = LintCode(
|
||||
'no_stopwatches',
|
||||
ruleDescription,
|
||||
correctionMessage: 'Use clock.stopwatch() from package:clock instead.',
|
||||
severity: DiagnosticSeverity.ERROR,
|
||||
);
|
||||
|
||||
@override
|
||||
DiagnosticCode get diagnosticCode => code;
|
||||
|
||||
@override
|
||||
void registerNodeProcessors(RuleVisitorRegistry registry, RuleContext context) {
|
||||
final visitor = _Visitor(this, context);
|
||||
registry
|
||||
..addConstructorName(this, visitor)
|
||||
..addSimpleIdentifier(this, visitor);
|
||||
}
|
||||
}
|
||||
|
||||
// This visitor finds invocation sites of Stopwatch (and subclasses) constructors
|
||||
// and references to "external" functions that return a Stopwatch (and subclasses),
|
||||
// including constructors.
|
||||
class _Visitor extends SimpleAstVisitor<void> {
|
||||
_Visitor(this.rule, this.context);
|
||||
|
||||
final AnalysisRule rule;
|
||||
final RuleContext context;
|
||||
|
||||
final Map<ClassElement, bool> _isStopwatchClassElementCache = <ClassElement, bool>{};
|
||||
|
||||
bool _checkIfImplementsStopwatchRecursively(ClassElement classElement) {
|
||||
if (classElement.library.isDartCore) {
|
||||
return classElement.name == 'Stopwatch';
|
||||
}
|
||||
return classElement.allSupertypes.any((InterfaceType interface) {
|
||||
final InterfaceElement interfaceElement = interface.element;
|
||||
return interfaceElement is ClassElement && _implementsStopwatch(interfaceElement);
|
||||
});
|
||||
}
|
||||
|
||||
// The cached version, call this method instead of _checkIfImplementsStopwatchRecursively.
|
||||
bool _implementsStopwatch(ClassElement classElement) {
|
||||
return classElement.library.isDartCore
|
||||
? classElement.name == 'Stopwatch'
|
||||
: _isStopwatchClassElementCache.putIfAbsent(
|
||||
classElement,
|
||||
() => _checkIfImplementsStopwatchRecursively(classElement),
|
||||
);
|
||||
}
|
||||
|
||||
bool _isInternal(LibraryElement libraryElement) {
|
||||
return path.isWithin(
|
||||
libraryElement.session.analysisContext.contextRoot.root.path,
|
||||
libraryElement.firstFragment.source.fullName,
|
||||
);
|
||||
}
|
||||
|
||||
bool _hasTrailingFlutterIgnore(AstNode node) {
|
||||
return context.currentUnit!.content
|
||||
.substring(
|
||||
node.offset + node.length,
|
||||
context.currentUnit!.unit.lineInfo.getOffsetOfLineAfter(node.offset + node.length),
|
||||
)
|
||||
.contains(_ignoreStopwatch);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitConstructorName(ConstructorName node) {
|
||||
final ConstructorElement element = node.element!;
|
||||
final bool isAllowed = switch (element.returnType) {
|
||||
InterfaceType(element: final ClassElement classElement) =>
|
||||
!_implementsStopwatch(classElement),
|
||||
InterfaceType(element: InterfaceElement()) => true,
|
||||
};
|
||||
if (isAllowed || _hasTrailingFlutterIgnore(node)) {
|
||||
return;
|
||||
}
|
||||
rule.reportAtNode(node);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitSimpleIdentifier(SimpleIdentifier node) {
|
||||
final bool isAllowed = switch (node.element) {
|
||||
ExecutableElement(
|
||||
returnType: DartType(element: final ClassElement classElement),
|
||||
library: final LibraryElement libraryElement,
|
||||
)
|
||||
// Don't double report constructors and factories.
|
||||
when node.element is! ConstructorElement =>
|
||||
_isInternal(libraryElement) || !_implementsStopwatch(classElement),
|
||||
Element() || null => true,
|
||||
};
|
||||
if (isAllowed || _hasTrailingFlutterIgnore(node)) {
|
||||
return;
|
||||
}
|
||||
rule.reportAtNode(node);
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// TODO(nate-thegrate): remove this file if @protected changes, or add a test if it doesn't.
|
||||
// https://github.com/dart-lang/sdk/issues/57094
|
||||
|
||||
import 'package:analyzer/analysis_rule/analysis_rule.dart';
|
||||
import 'package:analyzer/analysis_rule/rule_context.dart';
|
||||
import 'package:analyzer/analysis_rule/rule_visitor_registry.dart';
|
||||
import 'package:analyzer/dart/ast/ast.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/error/error.dart';
|
||||
|
||||
class ProtectPublicStateSubtypes extends AnalysisRule {
|
||||
ProtectPublicStateSubtypes()
|
||||
: super(
|
||||
name: code.name,
|
||||
description:
|
||||
'Public State subtypes should add @protected when overriding methods '
|
||||
'to avoid exposing internal logic to developers.',
|
||||
);
|
||||
|
||||
static const LintCode code = LintCode(
|
||||
'protect_public_state_subtypes',
|
||||
'Public State subtypes should add @protected when overriding methods '
|
||||
'to avoid exposing internal logic to developers.',
|
||||
severity: DiagnosticSeverity.ERROR,
|
||||
);
|
||||
|
||||
@override
|
||||
DiagnosticCode get diagnosticCode => code;
|
||||
|
||||
@override
|
||||
void registerNodeProcessors(RuleVisitorRegistry registry, RuleContext context) {
|
||||
final visitor = _Visitor(this, context);
|
||||
registry.addClassDeclaration(this, visitor);
|
||||
}
|
||||
}
|
||||
|
||||
class _Visitor extends RecursiveAstVisitor<void> {
|
||||
_Visitor(this.rule, this.context);
|
||||
|
||||
final AnalysisRule rule;
|
||||
final RuleContext context;
|
||||
|
||||
final List<MethodDeclaration> unprotectedMethods = <MethodDeclaration>[];
|
||||
|
||||
/// Holds the `State` class [DartType].
|
||||
static DartType? stateType;
|
||||
|
||||
static bool isPublicStateSubtype(InterfaceElement element) {
|
||||
if (!element.isPublic) {
|
||||
return false;
|
||||
}
|
||||
if (stateType != null) {
|
||||
return element.allSupertypes.contains(stateType);
|
||||
}
|
||||
for (final InterfaceType superType in element.allSupertypes) {
|
||||
if (superType.element.name == 'State') {
|
||||
stateType = superType;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
void visitClassDeclaration(ClassDeclaration node) {
|
||||
if (isPublicStateSubtype(node.declaredFragment!.element)) {
|
||||
node.visitChildren(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether overridden `State` methods have the `@protected` annotation,
|
||||
/// and reports the method if not.
|
||||
@override
|
||||
void visitMethodDeclaration(MethodDeclaration node) {
|
||||
switch (node.name.lexeme) {
|
||||
case 'initState':
|
||||
case 'setState':
|
||||
case 'didUpdateWidget':
|
||||
case 'didChangeDependencies':
|
||||
case 'reassemble':
|
||||
case 'deactivate':
|
||||
case 'activate':
|
||||
case 'dispose':
|
||||
case 'build':
|
||||
case 'debugFillProperties':
|
||||
if (!node.declaredFragment!.element.metadata.hasProtected) {
|
||||
rule.reportAtNode(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. 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/analysis_rule.dart';
|
||||
import 'package:analyzer/analysis_rule/rule_context.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/dart/ast/visitor.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/dart/element/type.dart';
|
||||
import 'package:analyzer/error/error.dart';
|
||||
|
||||
/// Verify that no RenderBox subclasses call compute* instead of get* for
|
||||
/// computing the intrinsic dimensions. The [candidates] variable contains the
|
||||
/// full list of RenderBox intrinsic method invocations checked by this rule.
|
||||
const Map<String, String> candidates = <String, String>{
|
||||
'computeDryBaseline': 'getDryBaseline',
|
||||
'computeDryLayout': 'getDryLayout',
|
||||
'computeDistanceToActualBaseline': 'getDistanceToBaseline, or getDistanceToActualBaseline',
|
||||
'computeMaxIntrinsicHeight': 'getMaxIntrinsicHeight',
|
||||
'computeMinIntrinsicHeight': 'getMinIntrinsicHeight',
|
||||
'computeMaxIntrinsicWidth': 'getMaxIntrinsicWidth',
|
||||
'computeMinIntrinsicWidth': 'getMinIntrinsicWidth',
|
||||
};
|
||||
|
||||
class RenderBoxIntrinsicCalculationRule extends AnalysisRule {
|
||||
RenderBoxIntrinsicCalculationRule()
|
||||
: super(
|
||||
name: code.name,
|
||||
description: 'get* methods should be used to obtain the intrinsics of a RenderBox.',
|
||||
);
|
||||
|
||||
static const LintCode code = LintCode(
|
||||
'render_box_intrinsics',
|
||||
'Typically the get* methods should be used to obtain the intrinsics of a RenderBox.',
|
||||
correctionMessage: 'Consider calling {0} instead.',
|
||||
severity: DiagnosticSeverity.ERROR,
|
||||
);
|
||||
|
||||
@override
|
||||
LintCode get diagnosticCode => code;
|
||||
|
||||
@override
|
||||
void registerNodeProcessors(RuleVisitorRegistry registry, RuleContext context) {
|
||||
final visitor = _Visitor(this, context);
|
||||
registry.addSimpleIdentifier(this, visitor);
|
||||
}
|
||||
}
|
||||
|
||||
class _Visitor extends SimpleAstVisitor<void> {
|
||||
_Visitor(this.rule, this.context);
|
||||
|
||||
final AnalysisRule rule;
|
||||
final RuleContext context;
|
||||
|
||||
static final Map<InterfaceElement, bool> _isRenderBoxClassElementCache =
|
||||
<InterfaceElement, bool>{};
|
||||
// The cached version, call this method instead of _checkIfImplementsRenderBox.
|
||||
static bool _implementsRenderBox(InterfaceElement interfaceElement) {
|
||||
// Framework naming convention: a RenderObject subclass names have "Render" in its name.
|
||||
if (!interfaceElement.name!.contains('Render')) {
|
||||
return false;
|
||||
}
|
||||
return interfaceElement.name == 'RenderBox' ||
|
||||
_isRenderBoxClassElementCache.putIfAbsent(
|
||||
interfaceElement,
|
||||
() => _checkIfImplementsRenderBox(interfaceElement),
|
||||
);
|
||||
}
|
||||
|
||||
static bool _checkIfImplementsRenderBox(InterfaceElement element) {
|
||||
return element.allSupertypes.any(
|
||||
(InterfaceType interface) => _implementsRenderBox(interface.element),
|
||||
);
|
||||
}
|
||||
|
||||
static bool _checkIfRenderBoxParent(AstNode? node) {
|
||||
if (node == null) {
|
||||
return false;
|
||||
}
|
||||
if (node case ClassDeclaration(:final Token name)) {
|
||||
// Ignore the RenderBox class implementation: that's the only place the
|
||||
// compute* methods are supposed to be called.
|
||||
return name.lexeme == 'RenderBox';
|
||||
}
|
||||
return _checkIfRenderBoxParent(node.parent);
|
||||
}
|
||||
|
||||
static bool _checkForCommentContext(AstNode? node) {
|
||||
if (node == null) {
|
||||
return false;
|
||||
}
|
||||
if (node is CommentReference) {
|
||||
return true;
|
||||
}
|
||||
return _checkForCommentContext(node.parent);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitSimpleIdentifier(SimpleIdentifier node) {
|
||||
if (node.parent is CommentReference) {
|
||||
return;
|
||||
}
|
||||
final String? correctMethodName = candidates[node.name];
|
||||
if (correctMethodName == null) {
|
||||
return;
|
||||
}
|
||||
if (_checkIfRenderBoxParent(node.parent) || _checkForCommentContext(node.parent)) {
|
||||
return;
|
||||
}
|
||||
final bool isCallingSuperImplementation = switch (node.parent) {
|
||||
PropertyAccess(target: SuperExpression()) ||
|
||||
MethodInvocation(target: SuperExpression()) => true,
|
||||
_ => false,
|
||||
};
|
||||
if (isCallingSuperImplementation) {
|
||||
return;
|
||||
}
|
||||
final Element? declaredInClassElement = node.element?.enclosingElement;
|
||||
if (declaredInClassElement is InterfaceElement &&
|
||||
_implementsRenderBox(declaredInClassElement)) {
|
||||
rule.reportAtNode(node, arguments: <Object>[correctMethodName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
name: flutter_analyzer_plugin
|
||||
description: Custom analysis rules for flutter/flutter
|
||||
version: 0.0.1
|
||||
publish_to: none
|
||||
|
||||
resolution: workspace
|
||||
|
||||
environment:
|
||||
sdk: ^3.7.0
|
||||
|
||||
dependencies:
|
||||
analysis_server_plugin: any
|
||||
analyzer: any
|
||||
path: any
|
||||
|
||||
dev_dependencies:
|
||||
analyzer_testing: any
|
||||
test_reflective_loader: any
|
||||
|
||||
# PUBSPEC CHECKSUM: jdpqac
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. 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/src/lint/registry.dart';
|
||||
import 'package:analyzer_testing/analysis_rule/analysis_rule.dart';
|
||||
import 'package:analyzer_testing/src/analysis_rule/pub_package_resolution.dart';
|
||||
import 'package:flutter_analyzer_plugin/src/rules/avoid_future_catch_error.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
@reflectiveTest
|
||||
class AvoidFutureCatchErrorTest extends AnalysisRuleTest {
|
||||
@override
|
||||
void setUp() {
|
||||
Registry.ruleRegistry.registerWarningRule(AvoidFutureCatchError());
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@override
|
||||
String get analysisRule => AvoidFutureCatchError.code.name;
|
||||
|
||||
static const String source = '''
|
||||
import 'dart:async';
|
||||
|
||||
// This extension isn't picked up from dart:async, so we just fake it.
|
||||
extension MyFutureExtension<T> on Future<T> {
|
||||
Future<T> onError<E extends Object>(
|
||||
FutureOr<T> handleError(E error, StackTrace stackTrace), {
|
||||
bool test(E error)?,
|
||||
}) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
Future<void>.value().catchError((e, st) => null); // ERROR
|
||||
Future<void>.value().onError((e, st) => null); // ERROR
|
||||
Future<void>.value().then((_) => null, onError: (e, st) => null); // OK
|
||||
}
|
||||
''';
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
Future<void> test_avoid_future_catch_error() async {
|
||||
await assertDiagnostics(source, <ExpectedDiagnostic>[lint(313, 48), lint(374, 45)]);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(AvoidFutureCatchErrorTest);
|
||||
});
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. 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/src/lint/registry.dart';
|
||||
import 'package:analyzer_testing/analysis_rule/analysis_rule.dart';
|
||||
import 'package:analyzer_testing/src/analysis_rule/pub_package_resolution.dart';
|
||||
import 'package:flutter_analyzer_plugin/src/rules/no_double_clamp.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
@reflectiveTest
|
||||
class NoDoubleClampTest extends AnalysisRuleTest {
|
||||
@override
|
||||
void setUp() {
|
||||
Registry.ruleRegistry.registerWarningRule(NoDoubleClamp());
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@override
|
||||
String get analysisRule => NoDoubleClamp.code.name;
|
||||
|
||||
static const String source = '''
|
||||
class ClassWithAClampMethod {
|
||||
ClassWithAClampMethod clamp(double min, double max) => this;
|
||||
}
|
||||
|
||||
void testNoDoubleClamp(int input) {
|
||||
final ClassWithAClampMethod nonDoubleClamp = ClassWithAClampMethod();
|
||||
// ignore: unnecessary_nullable_for_final_variable_declarations
|
||||
final ClassWithAClampMethod? nonDoubleClamp2 = nonDoubleClamp;
|
||||
// ignore: unnecessary_nullable_for_final_variable_declarations
|
||||
final int? nullableInt = input;
|
||||
final double? nullableDouble = nullableInt?.toDouble();
|
||||
|
||||
nonDoubleClamp.clamp(0, 2);
|
||||
input.clamp(0, 2);
|
||||
input.clamp(0.0, 2); // ERROR: input.clamp(0.0, 2)
|
||||
input.toDouble().clamp(0, 2); // ERROR: input.toDouble().clamp(0, 2)
|
||||
|
||||
nonDoubleClamp2?.clamp(0, 2);
|
||||
nullableInt?.clamp(0, 2);
|
||||
nullableInt?.clamp(0, 2.0); // ERROR: nullableInt?.clamp(0, 2.0)
|
||||
nullableDouble?.clamp(0, 2); // ERROR: nullableDouble?.clamp(0, 2)
|
||||
|
||||
// ignore: unused_local_variable
|
||||
final ClassWithAClampMethod Function(double, double)? tearOff1 = nonDoubleClamp2?.clamp;
|
||||
// ignore: unused_local_variable
|
||||
final num Function(num, num)? tearOff2 = nullableInt?.clamp; // ERROR: nullableInt?.clamp
|
||||
// ignore: unused_local_variable
|
||||
final num Function(num, num)? tearOff3 = nullableDouble?.clamp; // ERROR: nullableDouble?.clamp
|
||||
}
|
||||
''';
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
Future<void> test_no_double_clamp() async {
|
||||
await assertDiagnostics(source, <ExpectedDiagnostic>[
|
||||
lint(553, 5),
|
||||
lint(617, 5),
|
||||
lint(745, 5),
|
||||
lint(815, 5),
|
||||
lint(1084, 5),
|
||||
lint(1214, 5),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(NoDoubleClampTest);
|
||||
});
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. 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/src/lint/registry.dart';
|
||||
import 'package:analyzer/utilities/package_config_file_builder.dart';
|
||||
import 'package:analyzer_testing/analysis_rule/analysis_rule.dart';
|
||||
import 'package:analyzer_testing/src/analysis_rule/pub_package_resolution.dart';
|
||||
import 'package:flutter_analyzer_plugin/src/rules/no_stopwatches.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import 'package_mixins/external_stopwatches_mixin.dart';
|
||||
|
||||
@reflectiveTest
|
||||
class NoStopwatchesTest extends AnalysisRuleTest with ExternalStopwatchesPackage {
|
||||
@override
|
||||
void setUp() {
|
||||
Registry.ruleRegistry.registerWarningRule(NoStopwatches());
|
||||
super.setUp();
|
||||
|
||||
writeTestPackageConfig(PackageConfigFileBuilder()..addExternalStopwatchesPackage(this));
|
||||
}
|
||||
|
||||
@override
|
||||
String get analysisRule => NoStopwatches.code.name;
|
||||
|
||||
static const String source = '''
|
||||
// Copyright 2014 The Flutter Authors. 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:external_stopwatches/external_stopwatches.dart' as externallib;
|
||||
|
||||
typedef ExternalStopwatchConstructor = externallib.MyStopwatch Function();
|
||||
|
||||
class StopwatchAtHome extends Stopwatch {
|
||||
StopwatchAtHome();
|
||||
StopwatchAtHome.create() : this();
|
||||
|
||||
Stopwatch get stopwatch => this;
|
||||
}
|
||||
|
||||
void testNoStopwatches(Stopwatch stopwatch) {
|
||||
// OK for now, but we probably want to catch public APIs that take a Stopwatch?
|
||||
stopwatch.runtimeType;
|
||||
// Bad: introducing Stopwatch from dart:core.
|
||||
final Stopwatch localVariable = Stopwatch(); // ERROR: Stopwatch()
|
||||
// Bad: introducing Stopwatch from dart:core.
|
||||
Stopwatch().runtimeType; // ERROR: Stopwatch()
|
||||
|
||||
(localVariable..runtimeType) // OK: not directly introducing Stopwatch.
|
||||
.runtimeType;
|
||||
|
||||
// Bad: introducing a Stopwatch subclass.
|
||||
StopwatchAtHome().runtimeType; // ERROR: StopwatchAtHome()
|
||||
|
||||
// OK: not directly introducing Stopwatch.
|
||||
Stopwatch anotherStopwatch = stopwatch;
|
||||
// Bad: introducing a Stopwatch constructor.
|
||||
StopwatchAtHome Function() constructor = StopwatchAtHome.new; // ERROR: StopwatchAtHome.new
|
||||
assert(() {
|
||||
anotherStopwatch = constructor()..runtimeType;
|
||||
// Bad: introducing a Stopwatch constructor.
|
||||
constructor = StopwatchAtHome.create; // ERROR: StopwatchAtHome.create
|
||||
anotherStopwatch = constructor()..runtimeType;
|
||||
return true;
|
||||
}());
|
||||
anotherStopwatch.runtimeType;
|
||||
|
||||
// Bad: introducing an external Stopwatch constructor.
|
||||
externallib.MyStopwatch.create(); // ERROR: externallib.MyStopwatch.create()
|
||||
ExternalStopwatchConstructor? externalConstructor;
|
||||
|
||||
assert(() {
|
||||
// Bad: introducing an external Stopwatch constructor.
|
||||
externalConstructor = externallib.MyStopwatch.new; // ERROR: externallib.MyStopwatch.new
|
||||
return true;
|
||||
}());
|
||||
externalConstructor?.call();
|
||||
|
||||
// Bad: introducing an external Stopwatch.
|
||||
externallib.stopwatch.runtimeType; // ERROR: externallib.stopwatch
|
||||
// Bad: calling an external function that returns a Stopwatch.
|
||||
externallib.createMyStopwatch().runtimeType; // ERROR: externallib.createMyStopwatch()
|
||||
// Bad: calling an external function that returns a Stopwatch.
|
||||
externallib.createStopwatch().runtimeType; // ERROR: externallib.createStopwatch()
|
||||
// Bad: introducing the tear-off form of an external function that returns a Stopwatch.
|
||||
externalConstructor = externallib.createMyStopwatch; // ERROR: externallib.createMyStopwatch
|
||||
|
||||
// OK: existing instance.
|
||||
constructor.call().stopwatch;
|
||||
}
|
||||
|
||||
void testStopwatchIgnore(Stopwatch stopwatch) {
|
||||
Stopwatch().runtimeType; // flutter_ignore: stopwatch (see analyze.dart)
|
||||
Stopwatch().runtimeType; // flutter_ignore: some_other_ignores, stopwatch (see analyze.dart)
|
||||
}
|
||||
''';
|
||||
|
||||
// ignore: unreachable_from_main, non_constant_identifier_names
|
||||
Future<void> fail_test_no_stopwatches() async {
|
||||
await assertDiagnostics(source, <ExpectedDiagnostic>[
|
||||
lint(696, 9),
|
||||
lint(781, 9),
|
||||
lint(970, 15),
|
||||
lint(1207, 19),
|
||||
lint(1390, 22),
|
||||
lint(1615, 30),
|
||||
lint(1845, 27),
|
||||
lint(2028, 9),
|
||||
lint(2162, 17),
|
||||
lint(2316, 15),
|
||||
lint(2513, 17),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(NoStopwatchesTest);
|
||||
});
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. 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/utilities/package_config_file_builder.dart';
|
||||
import 'package:analyzer_testing/analysis_rule/analysis_rule.dart';
|
||||
|
||||
extension ExternalStopwatchesExtension on PackageConfigFileBuilder {
|
||||
PackageConfigFileBuilder addExternalStopwatchesPackage(AnalysisRuleTest test) {
|
||||
add(
|
||||
name: ExternalStopwatchesPackage._externalStopwatchesPackageName,
|
||||
rootPath: test.convertPath(ExternalStopwatchesPackage._externalStopwatchesPackageRoot),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/// Mixin application that allows for `package:meta` imports in tests.
|
||||
mixin ExternalStopwatchesPackage on AnalysisRuleTest {
|
||||
static const String _externalStopwatchesPackageName = 'external_stopwatches';
|
||||
static const String _externalStopwatchesPackageRoot =
|
||||
'/packages/$_externalStopwatchesPackageName';
|
||||
|
||||
@override
|
||||
void setUp() {
|
||||
super.setUp();
|
||||
newFile('$_externalStopwatchesPackageRoot/lib/external_stopwatches.dart', '''
|
||||
// External Library that creates Stopwatches. This file will not be analyzed but
|
||||
// its symbols will be imported by tests.
|
||||
|
||||
class MyStopwatch implements Stopwatch {
|
||||
MyStopwatch();
|
||||
MyStopwatch.create() : this();
|
||||
|
||||
@override
|
||||
Duration get elapsed => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
int get elapsedMicroseconds => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
int get elapsedMilliseconds => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
int get elapsedTicks => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
int get frequency => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
bool get isRunning => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
void reset() {}
|
||||
|
||||
@override
|
||||
void start() {}
|
||||
|
||||
@override
|
||||
void stop() {}
|
||||
}
|
||||
|
||||
final MyStopwatch stopwatch = MyStopwatch.create();
|
||||
|
||||
MyStopwatch createMyStopwatch() => MyStopwatch();
|
||||
Stopwatch createStopwatch() => Stopwatch();
|
||||
|
||||
''');
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. 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/utilities/package_config_file_builder.dart';
|
||||
import 'package:analyzer_testing/analysis_rule/analysis_rule.dart';
|
||||
|
||||
extension MetaPackageConfigExtnesion on PackageConfigFileBuilder {
|
||||
PackageConfigFileBuilder addMetaPackage(AnalysisRuleTest test) {
|
||||
add(
|
||||
name: MetaPackage._metaPackageName,
|
||||
rootPath: test.convertPath(MetaPackage._metaPackageRoot),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/// Mixin application that allows for `package:meta` imports in tests.
|
||||
mixin MetaPackage on AnalysisRuleTest {
|
||||
static const String _metaPackageName = 'meta';
|
||||
static const String _metaPackageRoot = '/packages/$_metaPackageName';
|
||||
|
||||
@override
|
||||
void setUp() {
|
||||
super.setUp();
|
||||
newFile('$_metaPackageRoot/lib/meta.dart', '''
|
||||
library meta;
|
||||
|
||||
const protected = Object();
|
||||
const mustCallSuper = Object();
|
||||
const factory = Object();
|
||||
const optionalTypeArgs = Object();
|
||||
''');
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. 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/utilities/package_config_file_builder.dart';
|
||||
import 'package:analyzer_testing/analysis_rule/analysis_rule.dart';
|
||||
|
||||
extension FlutterWidgetsPackageConfigExtnesion on PackageConfigFileBuilder {
|
||||
PackageConfigFileBuilder addFlutterWidgetsPackage(AnalysisRuleTest test) {
|
||||
add(
|
||||
name: FlutterWidgetsPackage._flutterPackageName,
|
||||
rootPath: test.convertPath(FlutterWidgetsPackage._flutterPackageRoot),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/// Mixin application that allows for `package:flutter/widgets.dart` imports in tests.
|
||||
mixin FlutterWidgetsPackage on AnalysisRuleTest {
|
||||
static const String _flutterPackageName = 'flutter';
|
||||
static const String _flutterPackageRoot = '/packages/$_flutterPackageName';
|
||||
|
||||
@override
|
||||
void setUp() {
|
||||
super.setUp();
|
||||
newFile('$_flutterPackageRoot/lib/widgets.dart', '''
|
||||
library widgets;
|
||||
|
||||
abstract class StatefulWidget {
|
||||
const StatefulWidget();
|
||||
|
||||
@protected
|
||||
@factory
|
||||
State createState();
|
||||
}
|
||||
|
||||
mixin Diagnosticable {
|
||||
@protected
|
||||
@mustCallSuper
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {}
|
||||
}
|
||||
|
||||
class DiagnosticPropertiesBuilder {}
|
||||
class BuildContext {}
|
||||
class Widget {}
|
||||
|
||||
typedef VoidCallback = void Function();
|
||||
|
||||
@optionalTypeArgs
|
||||
abstract class State<T extends StatefulWidget> with Diagnosticable {
|
||||
@protected
|
||||
@mustCallSuper
|
||||
void initState() {}
|
||||
|
||||
@mustCallSuper
|
||||
@protected
|
||||
void didUpdateWidget(covariant T oldWidget) {}
|
||||
|
||||
@protected
|
||||
@mustCallSuper
|
||||
void reassemble() {}
|
||||
|
||||
@protected
|
||||
void setState(VoidCallback fn) {}
|
||||
|
||||
@protected
|
||||
@mustCallSuper
|
||||
void deactivate() {}
|
||||
|
||||
@protected
|
||||
@mustCallSuper
|
||||
void activate() {}
|
||||
|
||||
@protected
|
||||
@mustCallSuper
|
||||
void dispose() {}
|
||||
|
||||
@protected
|
||||
Widget build(BuildContext context);
|
||||
|
||||
@protected
|
||||
@mustCallSuper
|
||||
void didChangeDependencies() {}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
}
|
||||
|
||||
// If @protected State methods are added or removed, the analysis rule should be
|
||||
// updated accordingly (dev/bots/custom_rules/protect_public_state_subtypes.dart)
|
||||
}
|
||||
''');
|
||||
}
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. 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/src/lint/registry.dart';
|
||||
import 'package:analyzer/utilities/package_config_file_builder.dart';
|
||||
import 'package:analyzer_testing/analysis_rule/analysis_rule.dart';
|
||||
import 'package:analyzer_testing/src/analysis_rule/pub_package_resolution.dart';
|
||||
import 'package:flutter_analyzer_plugin/src/rules/protect_public_state_subtypes.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import 'package_mixins/meta_mixin.dart';
|
||||
import 'package_mixins/widgets_mixin.dart';
|
||||
|
||||
@reflectiveTest
|
||||
class ProtectPublicStateSubtypesTest extends AnalysisRuleTest
|
||||
with MetaPackage, FlutterWidgetsPackage {
|
||||
@override
|
||||
void setUp() {
|
||||
Registry.ruleRegistry.registerWarningRule(ProtectPublicStateSubtypes());
|
||||
super.setUp();
|
||||
|
||||
writeTestPackageConfig(
|
||||
PackageConfigFileBuilder()
|
||||
..addFlutterWidgetsPackage(this)
|
||||
..addMetaPackage(this),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String get analysisRule => ProtectPublicStateSubtypes.code.name;
|
||||
|
||||
static const String source = '''
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
class MyWidget extends StatefulWidget {
|
||||
|
||||
@override
|
||||
State createState() => MyWidgetStateBad();
|
||||
}
|
||||
|
||||
class MyWidgetStateBad extends State<MyWidget>{
|
||||
@override
|
||||
void initState() { // ERROR
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant MyWidget oldWidget) { // ERROR
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
@override
|
||||
void reassemble() { // ERROR
|
||||
super.reassemble();
|
||||
}
|
||||
|
||||
@override
|
||||
void setState(VoidCallback fn) {} // ERROR
|
||||
|
||||
@override
|
||||
void deactivate() { // ERROR
|
||||
super.deactivate();
|
||||
}
|
||||
|
||||
@override
|
||||
void activate() { // ERROR
|
||||
super.activate();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() { // ERROR
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Widget();
|
||||
|
||||
@override
|
||||
void didChangeDependencies() { // ERROR
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) { // ERROR
|
||||
super.debugFillProperties(properties);
|
||||
}
|
||||
}
|
||||
|
||||
class MyWidgetStateValid extends State<MyWidget>{
|
||||
@override
|
||||
@protected
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
@protected
|
||||
void didUpdateWidget(covariant MyWidget oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
@override
|
||||
@protected
|
||||
void reassemble() {
|
||||
super.reassemble();
|
||||
}
|
||||
|
||||
@override
|
||||
@protected
|
||||
void setState(VoidCallback fn) {}
|
||||
|
||||
@override
|
||||
@protected
|
||||
void deactivate() {
|
||||
super.deactivate();
|
||||
}
|
||||
|
||||
@override
|
||||
@protected
|
||||
void activate() {
|
||||
super.activate();
|
||||
}
|
||||
|
||||
@override
|
||||
@protected
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
@protected
|
||||
Widget build(BuildContext context) => Widget();
|
||||
|
||||
@override
|
||||
@protected
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
|
||||
@override
|
||||
@protected
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
Future<void> test_protect_public_state_subtypes() async {
|
||||
await assertDiagnostics(source, <ExpectedDiagnostic>[
|
||||
lint(224, 66),
|
||||
lint(294, 115),
|
||||
lint(413, 68),
|
||||
lint(485, 45),
|
||||
lint(543, 68),
|
||||
lint(615, 64),
|
||||
lint(683, 62),
|
||||
lint(749, 59),
|
||||
lint(812, 90),
|
||||
lint(906, 134),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(ProtectPublicStateSubtypesTest);
|
||||
});
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. 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/src/lint/registry.dart';
|
||||
import 'package:analyzer_testing/analysis_rule/analysis_rule.dart';
|
||||
import 'package:analyzer_testing/src/analysis_rule/pub_package_resolution.dart';
|
||||
import 'package:flutter_analyzer_plugin/src/rules/render_box_intrinsics.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
@reflectiveTest
|
||||
class RenderBoxIntrinsicCalculationRuleTest extends AnalysisRuleTest {
|
||||
@override
|
||||
void setUp() {
|
||||
Registry.ruleRegistry.registerWarningRule(RenderBoxIntrinsicCalculationRule());
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@override
|
||||
String get analysisRule => RenderBoxIntrinsicCalculationRule.code.name;
|
||||
|
||||
static const String source = '''
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
abstract class RenderBox {
|
||||
void computeDryBaseline() {}
|
||||
void computeDryLayout() {}
|
||||
void computeDistanceToActualBaseline() {}
|
||||
void computeMaxIntrinsicHeight() {}
|
||||
void computeMinIntrinsicHeight() {}
|
||||
void computeMaxIntrinsicWidth() {}
|
||||
void computeMinIntrinsicWidth() {}
|
||||
}
|
||||
|
||||
mixin ARenderBoxMixin on RenderBox {
|
||||
@override
|
||||
void computeMaxIntrinsicWidth() {}
|
||||
|
||||
@override
|
||||
void computeMinIntrinsicWidth() => computeMaxIntrinsicWidth(); // ERROR: computeMaxIntrinsicWidth(). Consider calling getMaxIntrinsicWidth instead.
|
||||
|
||||
@override
|
||||
void computeMinIntrinsicHeight() {
|
||||
final void Function() f =
|
||||
computeMaxIntrinsicWidth; // ERROR: f = computeMaxIntrinsicWidth. Consider calling getMaxIntrinsicWidth instead.
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
extension ARenderBoxExtension on RenderBox {
|
||||
void test() {
|
||||
computeDryBaseline(); // ERROR: computeDryBaseline(). Consider calling getDryBaseline instead.
|
||||
computeDryLayout(); // ERROR: computeDryLayout(). Consider calling getDryLayout instead.
|
||||
}
|
||||
}
|
||||
|
||||
class RenderBoxSubclass1 extends RenderBox {
|
||||
@override
|
||||
void computeDryLayout() {
|
||||
computeDistanceToActualBaseline(); // ERROR: computeDistanceToActualBaseline(). Consider calling getDistanceToBaseline, or getDistanceToActualBaseline instead.
|
||||
}
|
||||
|
||||
@override
|
||||
void computeDistanceToActualBaseline() {
|
||||
computeMaxIntrinsicHeight(); // ERROR: computeMaxIntrinsicHeight(). Consider calling getMaxIntrinsicHeight instead.
|
||||
}
|
||||
|
||||
/// [RenderBox.computeDryLayout]: // OK
|
||||
double? getDryBaseline() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class RenderBoxSubclass2 extends RenderBox with ARenderBoxMixin {
|
||||
@override
|
||||
void computeMaxIntrinsicWidth() {
|
||||
super.computeMinIntrinsicHeight(); // OK
|
||||
super.computeMaxIntrinsicWidth(); // OK
|
||||
final void Function() f = super.computeDryBaseline; // OK
|
||||
f();
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
||||
// ignore: non_constant_identifier_names
|
||||
Future<void> test_render_box_intrinsics() async {
|
||||
await assertDiagnostics(source, <ExpectedDiagnostic>[
|
||||
lint(585, 24),
|
||||
lint(786, 24),
|
||||
lint(980, 18),
|
||||
lint(1079, 16),
|
||||
lint(1264, 31),
|
||||
lint(1488, 25),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(RenderBoxIntrinsicCalculationRuleTest);
|
||||
});
|
||||
}
|
||||
@@ -1,14 +1,5 @@
|
||||
include: ../analysis_options.yaml
|
||||
|
||||
plugins:
|
||||
flutter_analyzer_plugin:
|
||||
path: ../../dev/flutter_analyzer_plugin
|
||||
diagnostics:
|
||||
no_double_clamp: true
|
||||
no_stopwatches: true
|
||||
protect_public_state_subtypes: true
|
||||
render_box_intrinsics: true
|
||||
|
||||
linter:
|
||||
rules:
|
||||
# diagnostic_describe_all_properties: true # blocked on https://github.com/dart-lang/sdk/issues/47418
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
include: ../analysis_options.yaml
|
||||
|
||||
plugins:
|
||||
flutter_analyzer_plugin:
|
||||
path: ../dev/flutter_analyzer_plugin
|
||||
diagnostics:
|
||||
no_stopwatches: true
|
||||
|
||||
linter:
|
||||
rules:
|
||||
# Tests try to throw and catch things in exciting ways all the time, so
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
include: ../analysis_options.yaml
|
||||
|
||||
plugins:
|
||||
flutter_analyzer_plugin:
|
||||
path: ../../dev/flutter_analyzer_plugin
|
||||
diagnostics:
|
||||
avoid_future_catch_error: true
|
||||
|
||||
linter:
|
||||
rules:
|
||||
avoid_catches_without_on_clauses: true
|
||||
|
||||
32
pubspec.lock
32
pubspec.lock
@@ -25,14 +25,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.7"
|
||||
analysis_server_plugin:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analysis_server_plugin
|
||||
sha256: eec9e58ac77595118203d6b87ce8ef559eba413f95beea78aea42828b86cf610
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.1"
|
||||
analyzer:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -41,22 +33,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.2.0"
|
||||
analyzer_plugin:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer_plugin
|
||||
sha256: "7c56a95eebc84db60b6dfa5a545e09df95f2ffc4453a3ef2cec59a08c7d70e56"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.13.8"
|
||||
analyzer_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer_testing
|
||||
sha256: f0c4eb2ffcbe8dfb56e8537de2cff9cad432d1d8644566427148819cf057a35a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.3"
|
||||
animations:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -871,14 +847,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.14"
|
||||
test_reflective_loader:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_reflective_loader
|
||||
sha256: b94229c9ae2a9ebe77d7abfee30641842e7e427b8bd851709de517970b905096
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
typed_data:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@@ -5,7 +5,6 @@ environment:
|
||||
|
||||
workspace:
|
||||
- dev/a11y_assessments
|
||||
- dev/flutter_analyzer_plugin
|
||||
- dev/automated_tests
|
||||
- dev/benchmarks/complex_layout
|
||||
- dev/benchmarks/imitation_game_flutter
|
||||
|
||||
Reference in New Issue
Block a user