Irbisa · Шпаргалка
Линты, анализатор и ревью кода
analysis_options, наборы линтов, dart fix, dart format, custom_lint, аннотации, ревью PR
Что такое
analysis_options.yamlи что в нём можно настроить?include: package:flutter_lints/flutter.yaml analyzer: exclude: - "**/*.g.dart" - "**/*.freezed.dart"…Чем отличаются
dart analyze,dart fixиdart format?dart analyze # report only; infos do not faildart analyze --fatal-infos # what CI should runflutter analyze # infos are already fatal here dart fix --dry-run # preview automated fixes, grouped by ruledart fix --apply --code=prefer_const_constructors…Если включён
flutter_lints, чтоflutter analyzeсообщит об этом виджете и как исправить каждую проблему?class Greeting extends StatelessWidget { Greeting({this.name}); final String? name; @override Widget build(BuildContext context) { print('build $name'); return Column(children: [ Container(height: 16, child: const Text('Hi')), Container(child: Text(name ?? 'guest')), const Icon(Icons.star, color: Color(0xFF00)), ]); }}Чем отличаются
lints,flutter_lintsиvery_good_analysisи как выбрать один из них?# Flutter app default, generated by flutter createinclude: package:flutter_lints/flutter.yaml # Pure Dart package: include: package:lints/recommended.yaml# Strict third-party: include: package:very_good_analysis/analysis_options.yaml…Как изменить строгость линта и какие правила стоит повышать до
error?include: package:flutter_lints/flutter.yaml analyzer: errors: use_build_context_synchronously: error # crash after an await unawaited_futures: error # error with no catch to reach…Какие линты во Flutter ловят настоящие баги, а не стиль, и что предотвращает каждый из них?
// Always false, and it compilesfinal ids = <int>[1, 2, 3];const raw = '2';final found = ids.contains(raw); // collection_methods_unrelated_typefinal same = raw == 2; // unrelated_type_equality_checksfinal fixed = ids.contains(int.parse(raw)); // right…Найдите баг: при ответе 404 экран профиля остаётся на индикаторе загрузки, а в консоли появляется необработанная ошибка.
enum ApiFailure { notFound, server } Future<User> fetchUser(http.Client client, Uri url) async { final res = await client.get(url); if (res.statusCode == 404) throw ApiFailure.notFound; if (res.statusCode != 200) throw ApiFailure.server; return User.fromJson(jsonDecode(res.body) as Map<String, dynamic>);} Future<void> loadProfile(Uri url) async { try { profile.value = ProfileLoaded(await fetchUser(client, url)); } on Exception catch (e) { profile.value = ProfileError(e); }}Чем отличаются
// ignore:,// ignore_for_file:иanalyzer: exclude:и что из этого допустимо в PR?// wrong in hand-written code: every lint in this file, including future ones// ignore_for_file: type=lint // right: one rule, one line, and a reason the reviewer can check// ignore: avoid_dynamic_calls -- payload shape is validated in decode()String title(dynamic json) => json['title'] as String;…Что делают
@override,@immutable,@protected,@visibleForTestingи@mustCallSuperи что на самом деле их проверяет?import 'package:flutter/foundation.dart'; // re-exports package:meta annotations @immutableclass Filter { const Filter(this.tags); final List<String> tags; // passes must_be_immutable, yet tags.add() works…Найдите баг: DevTools показывает в памяти на один
ChatViewModelбольше каждый раз, когда пользователь открывает и закрывает чат.abstract class ViewModel { final _subs = <StreamSubscription<Object?>>[]; void watch<T>(Stream<T> s, void Function(T) f) => _subs.add(s.listen(f)); Future<void> dispose() => Future.wait([for (final s in _subs) s.cancel()]);} class ChatViewModel extends ViewModel { ChatViewModel(ChatRepository repo) { watch(repo.messages(), messages.add); } final messages = <Message>[]; Timer? _typing; @override Future<void> dispose() async => _typing?.cancel();}Как переименовать публичный метод в пакете, от которого зависят другие приложения, не сломав им сборку?
class UserApi { // The new name holds the only implementation. User parseUser(String body) => User.fromJson(jsonDecode(body) as Map<String, dynamic>); @Deprecated('Use parseUser instead. Will be removed in 3.0.0.') User decodeUser(String body) => parseUser(body); // forwards, never copies…Как поддерживать единое форматирование в команде и что изменилось в
dart formatс Dart 3.7?# analysis_options.yaml, read by the IDE, the CLI and CI:# formatter:# page_width: 100# trailing_commas: preserve # Dart 3.8+: keep commas as written # CI gate: exit 1 if any file would change…Что должно быть в pre-commit хуке, а что в CI, и почему хук не может быть главной проверкой качества?
# lefthook.yml: committed; `lefthook install` wires .git/hooks after a clonepre-commit: commands: format: glob: "*.dart" run: dart format {staged_files}…dart analyzeпадает в CI на коде, который IDE показывает чистым. Анализатор тот же. Что отличается?Как найти неиспользуемый код, зависимости и ассеты во Flutter-проекте и чего не найдёт ни один инструмент?
Этот
Stateпроходит анализатор и свой виджет-тест. Что вы отметите на код-ревью?class _OrderPageState extends State<OrderPage> { final _note = TextEditingController(); Order? _order; @override void initState() { super.initState(); _load(); } Future<void> _load() async { final order = await OrderRepository().fetch(widget.orderId); setState(() => _order = order); } @override Widget build(BuildContext context) => Column(children: [Text(_order?.title ?? 'Loading'), TextField(controller: _note)]);}Половину PR написал ИИ-ассистент. Что вы проверите перед апрувом, помимо того, что ловят
flutter analyzeи тесты?// All three pass `flutter analyze` with flutter_lints; none should be approved. HttpClient trustingClient() => HttpClient() ..badCertificateCallback = (cert, host, port) => true; // accepts any certificate Future<void> onSave(OrderRepository repo, Order order) async {…Как вы проводите код-ревью во Flutter-команде: что ищете помимо анализатора и как формулируете комментарии?
Как превратить повторяющийся комментарий на ревью в собственное правило линтера и как заставить его работать в CI?
// Rules package, built on custom_lint_builder.// CI must run it separately: dart run custom_lintPluginBase createPlugin() => _HouseLints(); class _HouseLints extends PluginBase { @override…Ревьюер хочет, чтобы CI падал, когда файл длиннее 300 строк или цикломатическая сложность функции больше 10. Вы согласитесь?
Строгий набор правил в приложении на 200 000 строк выдаёт 4 000 замечаний. Как внедрить его без одного неревьюабельного диффа?
# 1. The mechanical share, in its own commit, with no manual editsdart fix --dry-rundart fix --apply # or --code=<rule> to keep it reviewablegit commit -am "chore: dart fix --apply (machine-generated)" # 2. Rank what is left; in --format=machine field 3 is the diagnostic code…