Skip to content

Commit

Permalink
feat: allow checking for deprecated rules (#131)
Browse files Browse the repository at this point in the history
* feat: define LinterRule model

* feat: define inspect script

* docs: update all linter rule doc

* feat: allow version argument

* feat: specify "Dart" in log

* docs: document the inspect script

* docs: use "run" over "inspect"

* refactor: barrel file

* dos: update see also

* ci: update Dart version
  • Loading branch information
alestiago authored Jan 2, 2025
1 parent 1da92d9 commit 2a80995
Show file tree
Hide file tree
Showing 10 changed files with 3,785 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tool_linter_rules.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: 🎯 Setup Dart
uses: dart-lang/setup-dart@v1
with:
sdk: 3.5.0
sdk: 3.6.0

- name: 📦 Install Dependencies
run: dart pub get
Expand Down
2 changes: 1 addition & 1 deletion lib/very_good_analysis.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/// Very Good Dart analyzer settings and best practices
/// used internally at [Very Good Ventures](https://verygood.ventures).
library very_good_analysis;
library;
20 changes: 20 additions & 0 deletions tool/linter_rules/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,23 @@ dart lib/exclusion_reason_table.dart $version
This command will update the README table for the rules that are not enabled by default in the specified `$version` of Very Good Analysis. The `$version` is a user specified argument and it should be in the format `x.y.z`. In addition, no longer excluded rules will be removed from the `exclusion_reasons.json` file. The command does not format the output, so it is recommended to format both files with your preferred formatter after running the command.

Rules that are missing a reason in the `exclusion_reasons.json` file will be given the reason `Not specified`.

## Inspection 🔍

If you're looking to update Very Good Analysis you might want to inspect the health of the latest rule set. You can use the script at `bin/inspect.dart` to do exactly that.

It will log information about:

- The number of Dart linter rules fetched.
- The number of rules being declared in the given Very Good Analysis with the given version.
- The number of deprecated rules Dart rules being used in Very Good Analysis with the given version.

### Usage

To run the `inspect` script, run the following command (from `tool/linter_rules`, and don't forget to `dart pub get`):

```sh
dart bin/inspect.dart $version
```

Where version is the existing Very Good Analysis version you would like to inspect, for example `7.0.0`.
70 changes: 70 additions & 0 deletions tool/linter_rules/bin/inspect.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import 'dart:convert';

import 'package:http/http.dart';
import 'package:linter_rules/linter_rules.dart';

/// The [Uri] to fetch all linter rules from.
final _allLinterRulesUri = Uri.parse(
'https://raw.githubusercontent.com/dart-lang/site-www/refs/heads/main/src/_data/linter_rules.json',
);

/// Compares Very Good Analysis with the all available Dart linter rules.
///
/// Should be run from the root of the `linter_rules` package (tool/linter_rules),
/// with the version of the Very Good Analysis to inspect.
///
/// The version argument should be in the format of `x.y.z`. For example,
/// `5.1.0`.
///
/// To use the tool run (from tool/linter_rules):
/// ```sh
/// dart bin/inspect.dart $version
/// ```
///
/// Where `$version` is the version of the Very Good Analysis to log the table
/// for. For example:
///
/// ```sh
/// dart bin/inspect.dart 5.1.0
/// ```
///
/// It will log information about:
/// - The number of Dart linter rules fetched.
/// - The number of rules being declared in the given Very Good Analysis
/// with the given version.
/// - The number of deprecated rules Dart rules being used in Very Good
/// Analysis with the given version.
Future<void> main(
List<String> args, {
void Function(String) log = print,
}) async {
final version = args[0];

final response = await get(_allLinterRulesUri);
final json = jsonDecode(response.body) as List<dynamic>;

final dartRules = json
.map(
(rule) => LinterRule.fromJson(rule as Map<String, dynamic>),
)
.toList();
log('Fetched ${dartRules.length} Dart linter rules');

final vgaRules = await allVeryGoodAnalysisRules(version: version);
log('Fetched ${vgaRules.length} Very Good Analysis rules');
log('');

final deprecatedDartRules = dartRules
.where((rule) => rule.state == LinterRuleState.deprecated)
.map((rule) => rule.name)
.toSet();
final deprecatedVgaRules =
vgaRules.where(deprecatedDartRules.contains).toList();
final deprecationMessage = StringBuffer(
'''Found ${deprecatedVgaRules.length} deprecated Dart rules (out of ${deprecatedDartRules.length} deprecated Dart rules) in Very Good Analysis ($version)${deprecatedVgaRules.isEmpty ? '.' : ':'}''',
);
for (final rule in deprecatedVgaRules) {
deprecationMessage.write('\n - $rule');
}
log(deprecationMessage.toString());
}
1 change: 1 addition & 0 deletions tool/linter_rules/lib/linter_rules.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export 'src/all_linter_rules.dart';
export 'src/all_vga_rules.dart';
export 'src/linter_rules_reasons.dart';
export 'src/markdown_table_generator.dart';
export 'src/models/models.dart';
export 'src/readme.dart';
139 changes: 139 additions & 0 deletions tool/linter_rules/lib/src/models/linter_rule.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import 'package:meta/meta.dart';

/// {@template LinterRuleState}
/// The state of a [LinterRule] describing its present state within the Dart
/// language.
/// {@endtemplate}
enum LinterRuleState {
/// The rule is stable.
stable,

/// The rule has been removed from the Dart language.
removed,

/// The rule is currently experimental within the Dart language.
experimental,

/// The rule is deprecated and should not be used.
deprecated;

/// Converts a [String] value to a [LinterRuleState].
static LinterRuleState _fromJson(String value) {
switch (value) {
case 'stable':
return LinterRuleState.stable;
case 'removed':
return LinterRuleState.removed;
case 'experimental':
return LinterRuleState.experimental;
case 'deprecated':
return LinterRuleState.deprecated;
default:
throw ArgumentError('Invalid value for LinterRuleState: $value');
}
}
}

/// {@template FixStatus}
/// Wether there is an automated fix available for the rule.
/// {@endtemplate}
enum FixStatus {
/// The rule has an automated fix available.
hasFix,

/// The rule has no automated fix available.
noFix,

/// The rule can have an automated fix available, but is yet to be
/// implemented.
needsFix,

/// The automated fix for the rule needs evaluation.
needsEvaluation;

/// Converts a [String] value to a [FixStatus].
static FixStatus _fromJson(String value) {
switch (value) {
case 'hasFix':
return FixStatus.hasFix;
case 'noFix':
return FixStatus.noFix;
case 'needsFix':
return FixStatus.needsFix;
case 'needsEvaluation':
return FixStatus.needsEvaluation;
default:
throw ArgumentError('Invalid value for FixStatus: $value');
}
}
}

/// {@template LinterRule}
/// A linter rule within the Dart language.
///
/// See also:
///
/// * [Available linter rules](https://raw.githubusercontent.com/dart-lang/sdk/main/pkg/linter/tool/machine/rules.json)
/// * [All linter rules](https://github.com/dart-lang/site-www/blob/main/src/_data/linter_rules.json)
/// {@endtemplate}
@immutable
class LinterRule {
/// {@macro LinterRule}
const LinterRule({
required this.name,
required this.description,
required this.details,
required this.categories,
required this.state,
required this.incompatible,
required this.sets,
required this.fixStatus,
required this.sinceDartSdk,
});

/// Converts JSON to a [LinterRule].
factory LinterRule.fromJson(Map<String, dynamic> json) {
return LinterRule(
name: json['name'] as String,
description: json['description'] as String,
details: json['details'] as String,
categories: (json['categories'] as List).cast<String>(),
state: LinterRuleState._fromJson(json['state'] as String),
incompatible: (json['incompatible'] as List).cast<String>(),
sets: (json['sets'] as List).cast<String>(),
fixStatus: FixStatus._fromJson(json['fixStatus'] as String),
sinceDartSdk: json['sinceDartSdk'] as String,
);
}

/// The unique name of the linter rule.
final String name;

/// A brief description of the linter rule explaining what it does.
final String description;

/// A detailed explanation of the linter rule.
final String details;

/// Categories that the rule falls into.
///
/// Examples include `style`, `errorProne`, `flutter`.
final List<String> categories;

/// {@macro LinterRuleState}
final LinterRuleState state;

/// Rules that are incompatible with this rule.
final List<String> incompatible;

/// The sets this rule belongs to.
///
/// Examples include `core`, `recommended`, `flutter`.
final List<String> sets;

/// {@macro FixStatus}
final FixStatus fixStatus;

/// The Dart SDK version when the rule was introduced.
final String sinceDartSdk;
}
1 change: 1 addition & 0 deletions tool/linter_rules/lib/src/models/models.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export 'linter_rule.dart';
2 changes: 2 additions & 0 deletions tool/linter_rules/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ environment:

dependencies:
http: ^1.2.1
meta: ^1.16.0
path: ^1.9.0
yaml: ^3.1.2

dev_dependencies:
test: ^1.25.14
very_good_analysis:
path: ../../
Loading

0 comments on commit 2a80995

Please sign in to comment.