Skip to content

Commit

Permalink
feat: defined_async_value_getter_type rule (#139)
Browse files Browse the repository at this point in the history
  • Loading branch information
ronnnnn authored Jul 26, 2024
1 parent da682f1 commit 7c6c304
Show file tree
Hide file tree
Showing 10 changed files with 233 additions and 6 deletions.
33 changes: 31 additions & 2 deletions packages/nilts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,9 @@ Some of lint rules support quick fixes on IDE.
### Overview

| Rule name | Overview | Target SDK | Rule type | Maturity level | Quick fix |
|:------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------|:------------------------------:| :-------: |:--------------:|:---------:|
|-------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------|:------------------------------:| :-------: |:--------------:|:---------:|
| [defined\_async\_callback\_type](#defined_async_callback_type) | Checks `Future<void> Function()` definitions. | Any versions nilts supports | Practice | Experimental | ✅️ |
| [defined\_async\_value\_getter\_type](#defined_async_value_getter_type) | Checks `Future<T> Function()` definitions. | Any versions nilts supports | Practice | Experimental | ✅️ |
| [defined\_async\_value\_setter\_type](#defined_async_value_setter_type) | Checks `Future<void> Function(T value)` definitions. | Any versions nilts supports | Practice | Experimental | ✅️ |
| [defined\_value\_changed\_type](#defined_value_changed_type) | Checks `void Function(T value)` definitions. | Any versions nilts supports | Practice | Experimental | ✅️ |
| [defined\_value\_getter\_type](#defined_value_getter_type) | Checks `T Function()` definitions. | Any versions nilts supports | Practice | Experimental | ✅️ |
Expand Down Expand Up @@ -137,6 +138,35 @@ See also:

</details>

#### defined_async_value_getter_type

<details>

- Target SDK : Any versions nilts supports
- Rule type : Practice
- Maturity level : Experimental
- Quick fix :

**Consider** replace `Future<T> Function()` with `AsyncValueGetter` which is defined in Flutter SDK.

**BAD:**

```dart
final Future<int> Function() callback;
```

**GOOD:**

```dart
final AsyncValueGetter<int> callback;
```

See also:

- [AsyncValueGetter typedef - foundation library - Dart API](https://api.flutter.dev/flutter/foundation/AsyncValueGetter.html)

</details>

#### defined_async_value_setter_type

<details>
Expand All @@ -154,7 +184,6 @@ See also:
final Future<void> Function(int value) callback;
```


**GOOD:**

```dart
Expand Down
2 changes: 2 additions & 0 deletions packages/nilts/lib/nilts.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:custom_lint_builder/custom_lint_builder.dart';
import 'package:nilts/src/dart_version.dart';
import 'package:nilts/src/lints/defined_async_callback_type.dart';
import 'package:nilts/src/lints/defined_async_value_getter_type.dart';
import 'package:nilts/src/lints/defined_async_value_setter_type.dart';
import 'package:nilts/src/lints/defined_value_callback_type.dart';
import 'package:nilts/src/lints/defined_value_getter_type.dart';
Expand All @@ -23,6 +24,7 @@ class _NiltsLint extends PluginBase {
@override
List<LintRule> getLintRules(CustomLintConfigs configs) => [
const DefinedAsyncCallbackType(),
const DefinedAsyncValueGetterType(),
const DefinedAsyncValueSetterType(),
const DefinedValueChangedType(),
const DefinedValueGetterType(),
Expand Down
3 changes: 3 additions & 0 deletions packages/nilts/lib/src/change_priority.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ class ChangePriority {
/// The priority for [_ReplaceWithAsyncCallback].
static const int replaceWithAsyncCallback = 100;

/// The priority for [_ReplaceWithAsyncValueGetter].
static const int replaceWithAsyncValueGetter = 100;

/// The priority for [_ReplaceWithAsyncValueSetter].
static const int replaceWithAsyncValueSetter = 100;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ class DefinedAsyncCallbackType extends DartLintRule {
// Do nothing if the return type is not Future<void>.
final returnType = type.returnType;
if (returnType is! InterfaceType) return;
if (!returnType.element.library.isDartAsync) return;
if (returnType.element.name != 'Future') return;
if (!returnType.isDartAsyncFuture) return;
if (returnType.typeArguments.length != 1) return;
if (returnType.typeArguments.first is! VoidType) return;

Expand Down
108 changes: 108 additions & 0 deletions packages/nilts/lib/src/lints/defined_async_value_getter_type.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// ignore_for_file: comment_references

import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';
import 'package:nilts/src/change_priority.dart';

/// A class for `defined_async_value_getter_type` rule.
///
/// This rule checks defining `Future<T> Function()` type.
///
/// - Target SDK : Any versions nilts supports
/// - Rule type : Practice
/// - Maturity level : Experimental
/// - Quick fix : ✅
///
/// **Consider** replace `Future<T> Function()` with [AsyncValueGetter]
/// which is defined in Flutter SDK.
///
/// **BAD:**
/// ```dart
/// final Future<int> Function() callback;
/// ```
///
/// **GOOD:**
/// ```dart
/// final AsyncValueGetter<int> callback;
/// ```
///
/// See also:
///
/// - [AsyncValueGetter typedef - foundation library - Dart API](https://api.flutter.dev/flutter/foundation/AsyncValueGetter.html)
class DefinedAsyncValueGetterType extends DartLintRule {
/// Create a new instance of [DefinedAsyncValueGetterType].
const DefinedAsyncValueGetterType() : super(code: _code);

static const _code = LintCode(
name: 'defined_async_value_getter_type',
problemMessage: '`AsyncValueGetter<T>` type is defined in Flutter SDK.',
url: 'https://github.com/ronnnnn/nilts#defined_async_value_getter_type',
);

@override
void run(
CustomLintResolver resolver,
ErrorReporter reporter,
CustomLintContext context,
) {
context.registry.addTypeAnnotation((node) {
final type = node.type;
// Do nothing if the type is not Function.
if (type is! FunctionType) return;

// Do nothing if Function has parameters.
if (type.parameters.isNotEmpty) return;

// Do nothing if the return type is not Future<T>.
final returnType = type.returnType;
if (returnType is! InterfaceType) return;
if (!returnType.isDartAsyncFuture) return;
if (returnType.typeArguments.length != 1) return;
final typeArgument = returnType.typeArguments.first;
if (typeArgument is VoidType ||
typeArgument is InvalidType ||
typeArgument is NeverType) return;

reporter.reportErrorForNode(_code, node);
});
}

@override
List<Fix> getFixes() => [
_ReplaceWithAsyncValueGetter(),
];
}

class _ReplaceWithAsyncValueGetter extends DartFix {
@override
void run(
CustomLintResolver resolver,
ChangeReporter reporter,
CustomLintContext context,
AnalysisError analysisError,
List<AnalysisError> others,
) {
context.registry.addTypeAnnotation((node) {
if (!node.sourceRange.intersects(analysisError.sourceRange)) return;

reporter
.createChangeBuilder(
message: 'Replace with AsyncValueGetter<T>',
priority: ChangePriority.replaceWithAsyncValueGetter,
)
.addDartFileEdit((builder) {
final returnType = (node.type! as FunctionType).returnType;
final returnTypeArgumentName =
(returnType as InterfaceType).typeArguments.first.element!.name;

final delta = node.question != null ? -1 : 0;
builder.addSimpleReplacement(
node.sourceRange.getMoveEnd(delta),
'AsyncValueGetter<$returnTypeArgumentName>',
);
});
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,7 @@ class DefinedAsyncValueSetterType extends DartLintRule {
// Do nothing if the return type is not Future<void>.
final returnType = type.returnType;
if (returnType is! InterfaceType) return;
if (!returnType.element.library.isDartAsync) return;
if (returnType.element.name != 'Future') return;
if (!returnType.isDartAsyncFuture) return;
if (returnType.typeArguments.length != 1) return;
if (returnType.typeArguments.first is! VoidType) return;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// ignore_for_file: type_init_formals
// ignore_for_file: unused_element
// ignore_for_file: defined_async_value_setter_type
// ignore_for_file: defined_async_value_getter_type

import 'package:flutter/material.dart';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// ignore_for_file: prefer_function_declarations_over_variables
// ignore_for_file: type_init_formals
// ignore_for_file: unused_element
// ignore_for_file: defined_value_getter_type
// ignore_for_file: defined_async_callback_type
// ignore_for_file: defined_async_value_setter_type

import 'dart:async';

import 'package:flutter/material.dart';

class MainButton extends StatelessWidget {
const MainButton(
Future<void> Function() this.onPressed,
// expect_lint: defined_async_value_getter_type
Future<int> Function() this.onReturnPressed,
int Function() this.onFutureReturnPressed, {
Future<void> Function()? this.onNullablePressed,
Future<void> Function(int)? this.onParamPressed,
// expect_lint: defined_async_value_getter_type
Future<int> Function()? this.onNullableReturnPressed,
int Function()? this.onNullableFutureReturnPressed,
super.key,
});

final Future<void> Function() onPressed;
// expect_lint: defined_async_value_getter_type
final Future<int> Function() onReturnPressed;
final int Function() onFutureReturnPressed;
final Future<void> Function()? onNullablePressed;
final Future<void> Function(int)? onParamPressed;
// expect_lint: defined_async_value_getter_type
final Future<int> Function()? onNullableReturnPressed;
final int Function()? onNullableFutureReturnPressed;

void _onPressed(
Future<void> Function() onPressed,
// expect_lint: defined_async_value_getter_type
Future<int> Function() onReturnPressed,
int Function() onFutureReturnPressed, {
Future<void> Function()? onNullablePressed,
Future<void> Function(int)? onParamPressed,
// expect_lint: defined_async_value_getter_type
Future<int> Function()? onNullableReturnPressed,
int Function()? onNullableFutureReturnPressed,
}) {}

@override
Widget build(BuildContext context) {
return FilledButton(
onPressed: () {
_onPressed(
() async {},
() async => 0,
() => 0,
);
onPressed();
},
child: const Text('Hello World!'),
);
}
}

final Future<void> Function() globalFunction = () async {};
// expect_lint: defined_async_value_getter_type
final Future<int> Function() globalReturnFunction = () async => 0;
final int Function() globalFutureReturnFunction = () => 0;
const Future<void> Function()? globalNullableFunction = null;
const Future<void> Function(int)? globalParamFunction = null;
// expect_lint: defined_async_value_getter_type
const Future<int> Function()? globalNullableReturnFunction = null;
const int Function()? globalNullableFutureReturnFunction = null;

void _globalFunction(
Future<void> Function() onPressed,
// expect_lint: defined_async_value_getter_type
Future<int> Function() onReturnPressed,
int Function() onFutureReturnPressed, {
Future<void> Function()? onNullablePressed,
Future<void> Function(int)? onParamPressed,
// expect_lint: defined_async_value_getter_type
Future<int> Function()? onNullableReturnPressed,
int Function()? onNullableFutureReturnPressed,
}) {}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// ignore_for_file: type_init_formals
// ignore_for_file: unused_element
// ignore_for_file: defined_async_callback_type
// ignore_for_file: defined_async_value_getter_type

import 'package:flutter/material.dart';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// ignore_for_file: defined_value_changed_type
// ignore_for_file: defined_value_setter_type
// ignore_for_file: defined_void_callback_type
// ignore_for_file: defined_async_value_getter_type

import 'package:flutter/material.dart';

Expand Down

0 comments on commit 7c6c304

Please sign in to comment.