├── packages ├── pyramid_lint │ ├── LICENSE │ ├── README.md │ ├── analysis_options.yaml │ ├── lib │ │ └── src │ │ │ ├── utils │ │ │ ├── typedef.dart │ │ │ ├── constants.dart │ │ │ ├── dart_type_extension.dart │ │ │ ├── pubspec_extension.dart │ │ │ ├── iterable_extension.dart │ │ │ ├── token_type_extension.dart │ │ │ ├── version_constraint_extension.dart │ │ │ ├── string_extension.dart │ │ │ └── lint_code_extension.dart │ │ │ ├── assists │ │ │ ├── flutter │ │ │ │ ├── wrap_with_stack.dart │ │ │ │ └── wrap_with_layout_builder.dart │ │ │ └── dart │ │ │ │ └── invert_boolean_expression.dart │ │ │ ├── lints │ │ │ └── dart │ │ │ │ ├── always_specify_parameter_names.dart │ │ │ │ ├── avoid_mutable_global_variables.dart │ │ │ │ ├── prefer_async_await.dart │ │ │ │ ├── no_self_comparisons.dart │ │ │ │ ├── avoid_positional_fields_in_records.dart │ │ │ │ └── no_duplicate_imports.dart │ │ │ └── pyramid_lint_rule.dart │ ├── example │ │ └── example.md │ ├── screenshots │ │ ├── proper_super_init_state_demo.gif │ │ ├── wrap_with_layout_builder_demo.gif │ │ └── prefer_dedicated_media_query_method_demo.gif │ └── pubspec.yaml └── pyramid_lint_test │ ├── test │ ├── lints │ │ ├── dart │ │ │ ├── prefer_immediate_return │ │ │ │ ├── prefer_immediate_return.dart │ │ │ │ └── fix │ │ │ │ │ ├── prefer_immediate_return.dart │ │ │ │ │ ├── prefer_immediate_return.diff │ │ │ │ │ └── prefer_immediate_return_test.dart │ │ │ ├── prefer_new_line_before_return │ │ │ │ ├── prefer_new_line_before_return.dart │ │ │ │ └── fix │ │ │ │ │ ├── prefer_new_line_before_return.dart │ │ │ │ │ ├── prefer_new_line_before_return.diff │ │ │ │ │ └── prefer_new_line_before_return_test.dart │ │ │ ├── avoid_mutable_global_variables.dart │ │ │ ├── avoid_abbreviations_in_doc_comments.dart │ │ │ ├── prefer_library_prefixes │ │ │ │ ├── prefer_library_prefixes.dart │ │ │ │ └── fix │ │ │ │ │ ├── prefer_library_prefixes.dart │ │ │ │ │ ├── prefer_library_prefixes.diff │ │ │ │ │ └── prefer_library_prefixes_test.dart │ │ │ ├── always_specify_parameter_names.dart │ │ │ ├── avoid_redundant_pattern_field_names │ │ │ │ ├── avoid_redundant_pattern_field_names.dart │ │ │ │ └── fix │ │ │ │ │ ├── avoid_redundant_pattern_field_names.dart │ │ │ │ │ ├── avoid_redundant_pattern_field_names.diff │ │ │ │ │ └── avoid_redundant_pattern_field_names_test.dart │ │ │ ├── avoid_duplicate_imports.dart │ │ │ ├── avoid_positional_fields_in_records.dart │ │ │ ├── avoid_empty_blocks.dart │ │ │ ├── no_self_comparisons.dart │ │ │ ├── prefer_const_constructor_declarations │ │ │ │ ├── fix │ │ │ │ │ ├── prefer_const_constructor_declarations.dart │ │ │ │ │ ├── prefer_const_constructor_declarations.diff │ │ │ │ │ └── prefer_const_constructor_declarations_test.dart │ │ │ │ └── prefer_const_constructor_declarations.dart │ │ │ ├── unnecessary_nullable_return_type │ │ │ │ ├── fix │ │ │ │ │ ├── unnecessary_nullable_return_type.dart │ │ │ │ │ ├── unnecessary_nullable_return_type.diff │ │ │ │ │ └── unnecessary_nullable_return_type_test.dart │ │ │ │ └── unnecessary_nullable_return_type.dart │ │ │ ├── avoid_inverted_boolean_expressions │ │ │ │ ├── avoid_inverted_boolean_expressions.dart │ │ │ │ └── fix │ │ │ │ │ ├── avoid_inverted_boolean_expressions.dart │ │ │ │ │ ├── avoid_inverted_boolean_expressions_test.dart │ │ │ │ │ └── avoid_inverted_boolean_expressions.diff │ │ │ ├── always_put_doc_comments_before_annotations │ │ │ │ ├── fix │ │ │ │ │ ├── always_put_doc_comments_before_annotations.dart │ │ │ │ │ ├── always_put_doc_comments_before_annotations.diff │ │ │ │ │ └── always_put_doc_comments_before_annotations_test.dart │ │ │ │ └── always_put_doc_comments_before_annotations.dart │ │ │ ├── avoid_nested_if.dart │ │ │ ├── prefer_iterable_first │ │ │ │ ├── fix │ │ │ │ │ ├── prefer_iterable_first.dart │ │ │ │ │ ├── prefer_iterable_first.diff │ │ │ │ │ └── prefer_iterable_first_test.dart │ │ │ │ └── prefer_iterable_first.dart │ │ │ ├── prefer_async_await.dart │ │ │ ├── prefer_iterable_last │ │ │ │ ├── fix │ │ │ │ │ ├── prefer_iterable_last.dart │ │ │ │ │ ├── prefer_iterable_last.diff │ │ │ │ │ └── prefer_iterable_last_test.dart │ │ │ │ └── prefer_iterable_last.dart │ │ │ ├── avoid_unused_parameters.dart │ │ │ ├── prefer_underscore_for_unused_callback_parameters.dart │ │ │ ├── avoid_dynamic.dart │ │ │ ├── boolean_prefixes.dart │ │ │ ├── prefer_iterable_any │ │ │ │ ├── fix │ │ │ │ │ ├── prefer_iterable_any_test.dart │ │ │ │ │ └── prefer_iterable_any.dart │ │ │ │ └── prefer_iterable_any.dart │ │ │ ├── prefer_iterable_every │ │ │ │ ├── fix │ │ │ │ │ ├── prefer_iterable_every_test.dart │ │ │ │ │ └── prefer_iterable_every.dart │ │ │ │ └── prever_iterable_every.dart │ │ │ └── max_switch_cases.dart │ │ └── flutter │ │ │ ├── prefer_border_radius_all │ │ │ ├── prefer_border_radius_all.dart │ │ │ └── fix │ │ │ │ ├── prefer_border_radius_all.dart │ │ │ │ ├── prefer_border_radius_all.diff │ │ │ │ └── prefer_border_radius_all_test.dart │ │ │ ├── prefer_void_callback │ │ │ ├── fix │ │ │ │ ├── prefer_void_callback.dart │ │ │ │ ├── prefer_void_callback.diff │ │ │ │ └── prefer_void_callback_test.dart │ │ │ └── prefer_void_callback.dart │ │ │ ├── proper_from_environment │ │ │ ├── fix │ │ │ │ ├── proper_from_environment.dart │ │ │ │ ├── proper_from_environment.diff │ │ │ │ └── proper_from_environment_test.dart │ │ │ └── proper_from_environment.dart │ │ │ ├── prefer_async_callback │ │ │ ├── fix │ │ │ │ ├── prefer_async_callback.dart │ │ │ │ ├── prefer_async_callback.diff │ │ │ │ └── prefer_async_callback_test.dart │ │ │ └── prefer_async_callback.dart │ │ │ ├── avoid_single_child_in_flex │ │ │ ├── fix │ │ │ │ ├── avoid_single_child_in_flex.dart │ │ │ │ ├── avoid_single_child_in_flex_1.diff │ │ │ │ └── avoid_single_child_in_flex_2.diff │ │ │ └── avoid_single_child_in_flex.dart │ │ │ ├── prefer_border_from_border_side │ │ │ ├── prefer_border_from_border_side.dart │ │ │ └── fix │ │ │ │ ├── prefer_border_from_border_side.dart │ │ │ │ ├── prefer_border_from_border_side.diff │ │ │ │ └── prefer_border_from_border_side_test.dart │ │ │ ├── prefer_text_rich │ │ │ ├── prefer_text_rich.dart │ │ │ └── fix │ │ │ │ ├── prefer_text_rich.dart │ │ │ │ ├── prefer_text_rich.diff │ │ │ │ └── prefer_text_rich_test.dart │ │ │ ├── use_spacer │ │ │ ├── fix │ │ │ │ ├── use_spacer.dart │ │ │ │ ├── use_spacer.diff │ │ │ │ └── use_spacer_test.dart │ │ │ └── use_spacer.dart │ │ │ ├── proper_expanded_and_flexible.dart │ │ │ ├── proper_super_dispose │ │ │ ├── fix │ │ │ │ ├── proper_super_dispose.diff │ │ │ │ ├── proper_super_dispose.dart │ │ │ │ └── proper_super_dispose_test.dart │ │ │ └── proper_super_dispose.dart │ │ │ ├── specify_icon_button_tooltip │ │ │ ├── fix │ │ │ │ ├── specify_icon_button_tooltip.dart │ │ │ │ ├── specify_icon_button_tooltip.diff │ │ │ │ └── specify_icon_button_tooltip_test.dart │ │ │ └── specify_icon_button_tooltip.dart │ │ │ ├── proper_super_init_state │ │ │ ├── fix │ │ │ │ ├── proper_super_init_state.diff │ │ │ │ ├── proper_super_init_state.dart │ │ │ │ └── proper_super_init_state_test.dart │ │ │ └── proper_super_init_state.dart │ │ │ ├── avoid_widget_state_public_members.dart │ │ │ ├── dispose_controllers │ │ │ └── fix │ │ │ │ └── dispose_controllers_test.dart │ │ │ ├── proper_edge_insets_constructors │ │ │ ├── fix │ │ │ │ ├── proper_edge_insets_constructors_test.dart │ │ │ │ └── proper_edge_insets_constructors.dart │ │ │ └── proper_edge_insets_constructors.dart │ │ │ ├── prefer_dedicated_media_query_functions │ │ │ └── fix │ │ │ │ └── prefer_dedicated_media_query_functions_test.dart │ │ │ └── avoid_returning_widgets.dart │ ├── assists │ │ ├── dart │ │ │ ├── invert_boolean_expression │ │ │ │ ├── invert_boolean_expression.dart │ │ │ │ ├── invert_boolean_expression.diff │ │ │ │ └── invert_boolean_expression_test.dart │ │ │ ├── swap_then_else_expression │ │ │ │ ├── swap_then_else_expression.dart │ │ │ │ ├── swap_then_else_expression.diff │ │ │ │ └── swap_then_else_expression_test.dart │ │ │ └── convert_to_for_in_iterable_indexed_loop │ │ │ │ ├── convert_to_for_in_iterable_indexed_loop.dart │ │ │ │ ├── convert_to_for_in_iterable_indexed_loop.diff │ │ │ │ └── convert_to_for_in_iterable_indexed_loop_test.dart │ │ └── flutter │ │ │ ├── use_edge_insets_zero │ │ │ ├── use_edge_insets_zero.dart │ │ │ ├── use_edge_insets_zero_test.dart │ │ │ └── use_edge_insets_zero.diff │ │ │ ├── wrap_with_layout_builder │ │ │ ├── wrap_with_layout_builder.dart │ │ │ ├── wrap_with_layout_builder.diff │ │ │ └── wrap_with_layout_builder_test.dart │ │ │ ├── wrap_with_stack │ │ │ ├── wrap_with_stack.dart │ │ │ ├── wrap_with_stack_test.dart │ │ │ └── wrap_with_stack.diff │ │ │ ├── wrap_all_children_with_expanded │ │ │ ├── wrap_all_children_with_expanded.dart │ │ │ ├── wrap_all_children_with_expanded_test.dart │ │ │ └── wrap_all_children_with_expanded.diff │ │ │ └── wrap_with_listenable_builder │ │ │ ├── wrap_with_listenable_builder.dart │ │ │ ├── wrap_with_listenable_builder.diff │ │ │ └── wrap_with_listenable_builder_test.dart │ └── utils │ │ └── version_constraint_extension_test.dart │ └── pubspec.yaml ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── 3-documentation-update.yml │ ├── 1-feature-request.yml │ └── 2-bug-report.yml ├── dependabot.yml └── pull_request_template.md ├── docs ├── assets │ ├── favicon.ico │ ├── logo-192x192.png │ ├── wrap_with_stack.gif │ ├── wrap_with_expanded.gif │ ├── use_edge_insets_zero.gif │ ├── proper_super_init_state.gif │ ├── invert_boolean_expression.gif │ ├── swap_then_else_expression.gif │ ├── wrap_with_layout_builder.gif │ └── convert_to_for_in_iterable_indexed_loop.gif ├── dart-assists │ ├── invert_boolean_expression.mdx │ ├── convert_to_for_in_iterable_indexed_loop.mdx │ └── swap_then_else_expression.mdx ├── flutter-assists │ ├── wrap_with_listenable_builder.mdx │ ├── wrap_with_stack.mdx │ ├── use_edge_insets_zero.mdx │ ├── wrap_with_layout_builder.mdx │ └── wrap_all_children_with_expanded.mdx ├── flutter-lints │ ├── prefer_void_callback.mdx │ ├── prefer_async_callback.mdx │ ├── use_spacer.mdx │ ├── proper_super_dispose.mdx │ ├── avoid_single_child_in_flex.mdx │ ├── proper_super_init_state.mdx │ ├── specify_icon_button_tooltip.mdx │ ├── proper_expanded_and_flexible.mdx │ ├── prefer_text_rich.mdx │ ├── prefer_border_radius_all.mdx │ ├── prefer_border_from_border_side.mdx │ ├── proper_from_environment.mdx │ ├── dispose_controllers.mdx │ ├── avoid_public_members_in_states.mdx │ ├── prefer_dedicated_media_query_functions.mdx │ └── proper_edge_insets_constructors.mdx ├── dart-lints │ ├── no_self_comparisons.mdx │ ├── avoid_mutable_global_variables.mdx │ ├── avoid_empty_blocks.mdx │ ├── prefer_new_line_before_return.mdx │ ├── prefer_iterable_any.mdx │ ├── prefer_iterable_every.mdx │ ├── prefer_immediate_return.mdx │ ├── unnecessary_nullable_return_type.mdx │ ├── prefer_iterable_first.mdx │ ├── no_duplicate_imports.mdx │ ├── prefer_iterable_last.mdx │ ├── avoid_redundant_pattern_field_names.mdx │ ├── avoid_positional_fields_in_records.mdx │ ├── avoid_inverted_boolean_expressions.mdx │ ├── always_put_doc_comments_before_annotations.mdx │ ├── max_lines_for_file.mdx │ ├── max_switch_cases.mdx │ ├── max_lines_for_function.mdx │ ├── prefer_const_constructor_declarations.mdx │ ├── always_specify_parameter_names.mdx │ ├── avoid_dynamic.mdx │ ├── prefer_underscore_for_unused_callback_parameters.mdx │ ├── prefer_async_await.mdx │ ├── avoid_nested_if.mdx │ ├── avoid_unused_parameters.mdx │ ├── avoid_abbreviations_in_doc_comments.mdx │ ├── prefer_library_prefixes.mdx │ └── boolean_prefixes.mdx └── index.mdx ├── .gitignore └── LICENSE /packages/pyramid_lint/LICENSE: -------------------------------------------------------------------------------- 1 | ../../LICENSE -------------------------------------------------------------------------------- /packages/pyramid_lint/README.md: -------------------------------------------------------------------------------- 1 | ../../README.md -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | -------------------------------------------------------------------------------- /packages/pyramid_lint/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: ../../analysis_options.yaml 2 | -------------------------------------------------------------------------------- /packages/pyramid_lint/lib/src/utils/typedef.dart: -------------------------------------------------------------------------------- 1 | typedef Json = Map; 2 | -------------------------------------------------------------------------------- /docs/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlescyt/pyramid_lint/HEAD/docs/assets/favicon.ico -------------------------------------------------------------------------------- /docs/assets/logo-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlescyt/pyramid_lint/HEAD/docs/assets/logo-192x192.png -------------------------------------------------------------------------------- /docs/assets/wrap_with_stack.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlescyt/pyramid_lint/HEAD/docs/assets/wrap_with_stack.gif -------------------------------------------------------------------------------- /docs/assets/wrap_with_expanded.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlescyt/pyramid_lint/HEAD/docs/assets/wrap_with_expanded.gif -------------------------------------------------------------------------------- /docs/assets/use_edge_insets_zero.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlescyt/pyramid_lint/HEAD/docs/assets/use_edge_insets_zero.gif -------------------------------------------------------------------------------- /docs/assets/proper_super_init_state.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlescyt/pyramid_lint/HEAD/docs/assets/proper_super_init_state.gif -------------------------------------------------------------------------------- /docs/assets/invert_boolean_expression.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlescyt/pyramid_lint/HEAD/docs/assets/invert_boolean_expression.gif -------------------------------------------------------------------------------- /docs/assets/swap_then_else_expression.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlescyt/pyramid_lint/HEAD/docs/assets/swap_then_else_expression.gif -------------------------------------------------------------------------------- /docs/assets/wrap_with_layout_builder.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlescyt/pyramid_lint/HEAD/docs/assets/wrap_with_layout_builder.gif -------------------------------------------------------------------------------- /packages/pyramid_lint/example/example.md: -------------------------------------------------------------------------------- 1 | Check out the [Getting Started guide](https://docs.page/charlescyt/pyramid_lint/getting-started) for more information. 2 | -------------------------------------------------------------------------------- /docs/assets/convert_to_for_in_iterable_indexed_loop.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlescyt/pyramid_lint/HEAD/docs/assets/convert_to_for_in_iterable_indexed_loop.gif -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | day: "saturday" 8 | -------------------------------------------------------------------------------- /packages/pyramid_lint/screenshots/proper_super_init_state_demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlescyt/pyramid_lint/HEAD/packages/pyramid_lint/screenshots/proper_super_init_state_demo.gif -------------------------------------------------------------------------------- /packages/pyramid_lint/screenshots/wrap_with_layout_builder_demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlescyt/pyramid_lint/HEAD/packages/pyramid_lint/screenshots/wrap_with_layout_builder_demo.gif -------------------------------------------------------------------------------- /packages/pyramid_lint/lib/src/utils/constants.dart: -------------------------------------------------------------------------------- 1 | const docUrl = 'https://docs.page/charlescyt/pyramid_lint'; 2 | const dartLintDocUrl = '$docUrl/dart-lints'; 3 | const flutterLintDocUrl = '$docUrl/flutter-lints'; 4 | -------------------------------------------------------------------------------- /packages/pyramid_lint/screenshots/prefer_dedicated_media_query_method_demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/charlescyt/pyramid_lint/HEAD/packages/pyramid_lint/screenshots/prefer_dedicated_media_query_method_demo.gif -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_immediate_return/prefer_immediate_return.dart: -------------------------------------------------------------------------------- 1 | int sum(int a, int b) { 2 | final sum = a + b; 3 | // expect_lint: prefer_immediate_return 4 | return sum; 5 | } 6 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_immediate_return/fix/prefer_immediate_return.dart: -------------------------------------------------------------------------------- 1 | int sum(int a, int b) { 2 | final sum = a + b; 3 | // expect_lint: prefer_immediate_return 4 | return sum; 5 | } 6 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/prefer_border_radius_all/prefer_border_radius_all.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/painting.dart'; 2 | 3 | // expect_lint: prefer_border_radius_all 4 | final borderRadius = BorderRadius.circular(8); 5 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/prefer_border_radius_all/fix/prefer_border_radius_all.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/painting.dart'; 2 | 3 | // expect_lint: prefer_border_radius_all 4 | final borderRadius = BorderRadius.circular(8); 5 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/prefer_void_callback/fix/prefer_void_callback.dart: -------------------------------------------------------------------------------- 1 | // expect_lint: prefer_void_callback 2 | typedef A = void Function(); 3 | 4 | // expect_lint: prefer_void_callback 5 | typedef B = void Function()?; 6 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/proper_from_environment/fix/proper_from_environment.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: prefer_const_constructors 2 | 3 | // expect_lint: proper_from_environment 4 | final string = String.fromEnvironment('String'); 5 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_new_line_before_return/prefer_new_line_before_return.dart: -------------------------------------------------------------------------------- 1 | String example(bool isTrue) { 2 | if (isTrue) { 3 | return 'true'; 4 | } // expect_lint: prefer_new_line_before_return 5 | return 'false'; 6 | } 7 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_new_line_before_return/fix/prefer_new_line_before_return.dart: -------------------------------------------------------------------------------- 1 | String example(bool isTrue) { 2 | if (isTrue) { 3 | return 'true'; 4 | } // expect_lint: prefer_new_line_before_return 5 | return 'false'; 6 | } 7 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/prefer_async_callback/fix/prefer_async_callback.dart: -------------------------------------------------------------------------------- 1 | // expect_lint: prefer_async_callback 2 | typedef A = Future Function(); 3 | 4 | // expect_lint: prefer_async_callback 5 | typedef B = Future Function()?; 6 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/avoid_single_child_in_flex/fix/avoid_single_child_in_flex.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | // expect_lint: avoid_single_child_in_flex 4 | const singleChild = Column( 5 | children: [ 6 | Placeholder(), 7 | ], 8 | ); 9 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/prefer_border_from_border_side/prefer_border_from_border_side.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/painting.dart'; 2 | 3 | // expect_lint: prefer_border_from_border_side 4 | final border = Border.all( 5 | width: 1, 6 | style: BorderStyle.solid, 7 | ); 8 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/avoid_mutable_global_variables.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_multiple_declarations_per_line 2 | 3 | // expect_lint: avoid_mutable_global_variables 4 | double pi = 3.14; 5 | 6 | // expect_lint: avoid_mutable_global_variables 7 | String a = 'a', b = 'b'; 8 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/prefer_border_from_border_side/fix/prefer_border_from_border_side.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | // expect_lint: prefer_border_from_border_side 4 | final border = Border.all( 5 | width: 1, 6 | style: BorderStyle.solid, 7 | ); 8 | -------------------------------------------------------------------------------- /packages/pyramid_lint/lib/src/utils/dart_type_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/element/nullability_suffix.dart'; 2 | import 'package:analyzer/dart/element/type.dart'; 3 | 4 | extension DartTypeExtension on DartType { 5 | bool get isNullable => nullabilitySuffix == NullabilitySuffix.question || isDartCoreNull; 6 | } 7 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/avoid_abbreviations_in_doc_comments.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_empty_blocks 2 | 3 | /// This do something 4 | /// 5 | /// expect_lint: avoid_abbreviations_in_doc_comments 6 | /// e.g. 7 | /// expect_lint: avoid_abbreviations_in_doc_comments 8 | /// approx. 9 | void a() {} 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .atom/ 3 | .idea/* 4 | .vscode/* 5 | 6 | # Files and directories created by pub 7 | .dart_tool/ 8 | .packages 9 | pubspec.lock 10 | 11 | # Conventional directory for build outputs 12 | build/ 13 | 14 | # Exceptions to above rules. 15 | !.vscode/dart.code-snippets 16 | 17 | # Log files 18 | *.log 19 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/dart/invert_boolean_expression/invert_boolean_expression.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_print 2 | 3 | void example(int number) { 4 | if (number == 0) { 5 | print('The number is zero'); 6 | } 7 | 8 | if (number > 0) { 9 | print('The number is positive'); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_library_prefixes/prefer_library_prefixes.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_print 2 | 3 | import 'dart:developer' as developer; 4 | // expect_lint: prefer_library_prefixes 5 | import 'dart:math'; 6 | 7 | void example() { 8 | print(log(e)); 9 | developer.log('message'); 10 | } 11 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/prefer_text_rich/prefer_text_rich.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | // expect_lint: prefer_text_rich 4 | final text = RichText( 5 | text: const TextSpan( 6 | text: 'Pyramid', 7 | children: [ 8 | TextSpan(text: 'Lint'), 9 | ], 10 | ), 11 | ); 12 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/prefer_text_rich/fix/prefer_text_rich.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | // expect_lint: prefer_text_rich 4 | final text = RichText( 5 | text: const TextSpan( 6 | text: 'Pyramid', 7 | children: [ 8 | TextSpan(text: 'Lint'), 9 | ], 10 | ), 11 | ); 12 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/always_specify_parameter_names.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | typedef ItemBuilder = 4 | Widget? Function( 5 | // expect_lint: always_specify_parameter_names 6 | BuildContext, 7 | // expect_lint: always_specify_parameter_names 8 | int, 9 | ); 10 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_library_prefixes/fix/prefer_library_prefixes.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: unused_import 2 | 3 | // expect_lint: prefer_library_prefixes 4 | import 'dart:developer'; 5 | // expect_lint: prefer_library_prefixes 6 | import 'dart:io'; 7 | // expect_lint: prefer_library_prefixes 8 | import 'dart:math'; 9 | -------------------------------------------------------------------------------- /packages/pyramid_lint/lib/src/utils/pubspec_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:pub_semver/pub_semver.dart'; 2 | import 'package:pubspec_parse/pubspec_parse.dart'; 3 | 4 | extension PubspecExtension on Pubspec { 5 | bool get isFlutterProject => dependencies.containsKey('flutter'); 6 | 7 | VersionConstraint? get dartSdkVersion => environment['sdk']; 8 | } 9 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/dart/swap_then_else_expression/swap_then_else_expression.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_print, unused_local_variable 2 | 3 | void example(bool condition) { 4 | if (condition) { 5 | print('then'); 6 | } else { 7 | print('else'); 8 | } 9 | 10 | final text = condition ? 'then' : 'else'; 11 | } 12 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/avoid_redundant_pattern_field_names/avoid_redundant_pattern_field_names.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_empty_blocks, unused_local_variable 2 | 3 | void fn(Map map) { 4 | // expect_lint: avoid_redundant_pattern_field_names 5 | for (final MapEntry(key: key, value: value) in map.entries) {} 6 | } 7 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/avoid_redundant_pattern_field_names/fix/avoid_redundant_pattern_field_names.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_empty_blocks, unused_local_variable 2 | 3 | void fn(Map map) { 4 | // expect_lint: avoid_redundant_pattern_field_names 5 | for (final MapEntry(key: key, value: value) in map.entries) {} 6 | } 7 | -------------------------------------------------------------------------------- /packages/pyramid_lint/lib/src/utils/iterable_extension.dart: -------------------------------------------------------------------------------- 1 | extension IterableExtension on Iterable { 2 | Iterable get duplicates { 3 | return removeAll(toSet()); 4 | } 5 | 6 | Iterable removeAll(Iterable items) { 7 | final list = toList(); 8 | for (final item in items) { 9 | list.remove(item); 10 | } 11 | return list; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/avoid_duplicate_imports.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: unused_local_variable, prefer_library_prefixes 2 | 3 | // expect_lint: no_duplicate_imports 4 | import 'dart:math' as math show max; 5 | // expect_lint: no_duplicate_imports 6 | import 'dart:math'; 7 | 8 | void example() { 9 | final a = math.max(1, 10); 10 | final b = min(1, 10); 11 | } 12 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/use_spacer/fix/use_spacer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | final column = Column( 4 | children: [ 5 | // expect_lint: use_spacer 6 | const Expanded( 7 | child: SizedBox(), 8 | ), 9 | // expect_lint: use_spacer 10 | Expanded( 11 | flex: 2, 12 | child: Container(), 13 | ), 14 | ], 15 | ); 16 | -------------------------------------------------------------------------------- /packages/pyramid_lint/lib/src/utils/token_type_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/token.dart'; 2 | 3 | extension TokenTypeExtension on TokenType { 4 | bool get isLogicalOperator { 5 | return this == TokenType.AMPERSAND_AMPERSAND || this == TokenType.BAR_BAR; 6 | } 7 | 8 | bool get isEqualityOrRelationalOperator { 9 | return isEqualityOperator || isRelationalOperator; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/proper_expanded_and_flexible.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | const stack = Stack( 4 | children: [ 5 | // expect_lint: proper_expanded_and_flexible 6 | Expanded( 7 | child: Text('Pyramid'), 8 | ), 9 | // expect_lint: proper_expanded_and_flexible 10 | Flexible( 11 | child: Text('Lint'), 12 | ), 13 | ], 14 | ); 15 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/avoid_positional_fields_in_records.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: unused_local_variable 2 | 3 | void fn((String, String, {int a, bool b}) record) { 4 | // expect_lint: avoid_positional_fields_in_records 5 | final first = record.$1; 6 | // expect_lint: avoid_positional_fields_in_records 7 | final second = record.$2; 8 | final a = record.a; 9 | final b = record.b; 10 | } 11 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/prefer_void_callback/prefer_void_callback.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart' show VoidCallback; 2 | 3 | typedef A = VoidCallback; 4 | // expect_lint: prefer_void_callback 5 | typedef B = void Function(); 6 | // expect_lint: prefer_void_callback 7 | typedef C = void Function()?; 8 | typedef D = void Function(int index); 9 | typedef E = void Function({required int index}); 10 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/dart/convert_to_for_in_iterable_indexed_loop/convert_to_for_in_iterable_indexed_loop.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: unused_local_variable, avoid_empty_blocks, omit_local_variable_types 2 | 3 | void fn(Iterable numbers) { 4 | for (final int number in numbers) {} 5 | for (final number in numbers) {} 6 | } 7 | 8 | void fn2(String word) { 9 | for (final String char in word.split('')) {} 10 | } 11 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/proper_from_environment/fix/proper_from_environment.diff: -------------------------------------------------------------------------------- 1 | Message: `Add const keyword` 2 | Priority: 80 3 | Diff for file `test/lints/flutter/proper_from_environment/fix/proper_from_environment.dart:4`: 4 | ``` 5 | 6 | // expect_lint: proper_from_environment 7 | - final string = String.fromEnvironment('String'); 8 | + final string = const String.fromEnvironment('String'); 9 | ``` 10 | --- 11 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/proper_from_environment/proper_from_environment.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: prefer_const_constructors 2 | 3 | // expect_lint: proper_from_environment 4 | final boolean = bool.fromEnvironment('bool'); 5 | 6 | // expect_lint: proper_from_environment 7 | final integer = int.fromEnvironment('int'); 8 | 9 | // expect_lint: proper_from_environment 10 | final string = String.fromEnvironment('String'); 11 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/avoid_empty_blocks.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: flutter_style_todos 2 | 3 | // expect_lint: avoid_empty_blocks 4 | void emptyFunction() {} 5 | 6 | void emptyIfBlock(bool condition) { 7 | // expect_lint: avoid_empty_blocks 8 | if (condition) {} 9 | } 10 | 11 | void emptyBlockWithTodo() { 12 | // It's okay to have an empty block with a TODO comment. 13 | // TODO: Implement this function. 14 | } 15 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/prefer_text_rich/fix/prefer_text_rich.diff: -------------------------------------------------------------------------------- 1 | Message: `Replace with Text.rich` 2 | Priority: 80 3 | Diff for file `test/lints/flutter/prefer_text_rich/fix/prefer_text_rich.dart:4`: 4 | ``` 5 | 6 | // expect_lint: prefer_text_rich 7 | - final text = RichText( 8 | - text: const TextSpan( 9 | + final text = Text.rich( 10 | + const TextSpan( 11 | text: 'Pyramid', 12 | children: [ 13 | ``` 14 | --- 15 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/prefer_border_radius_all/fix/prefer_border_radius_all.diff: -------------------------------------------------------------------------------- 1 | Message: `Replace with BorderRadius.all` 2 | Priority: 80 3 | Diff for file `test/lints/flutter/prefer_border_radius_all/fix/prefer_border_radius_all.dart:4`: 4 | ``` 5 | 6 | // expect_lint: prefer_border_radius_all 7 | - final borderRadius = BorderRadius.circular(8); 8 | + final borderRadius = BorderRadius.all(Radius.circular(8)); 9 | ``` 10 | --- 11 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/flutter/use_edge_insets_zero/use_edge_insets_zero.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: use_named_constants, proper_edge_insets_constructors 2 | 3 | import 'package:flutter/painting.dart'; 4 | 5 | const edgeInsets1 = EdgeInsets.all(0); 6 | const edgeInsets2 = EdgeInsets.fromLTRB(0, 0, 0, 0); 7 | const edgeInsets4 = EdgeInsets.only(left: 0, top: 0, right: 0, bottom: 0); 8 | const edgeInsets3 = EdgeInsets.symmetric(vertical: 0, horizontal: 0); 9 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/flutter/wrap_with_layout_builder/wrap_with_layout_builder.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class A extends StatelessWidget { 4 | const A({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return LayoutBuilder( 9 | builder: (_, _) { 10 | return const Center( 11 | child: Placeholder(), 12 | ); 13 | }, 14 | ); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/no_self_comparisons.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: unnecessary_parenthesis 2 | 3 | void example(int number) { 4 | // expect_lint: no_self_comparisons 5 | if (number == number) { 6 | return; 7 | } 8 | 9 | // expect_lint: no_self_comparisons 10 | if (number > (number)) { 11 | return; 12 | } 13 | 14 | // expect_lint: no_self_comparisons 15 | if ((number.sign) == number.sign) { 16 | return; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_immediate_return/fix/prefer_immediate_return.diff: -------------------------------------------------------------------------------- 1 | Message: `Return the value immediately.` 2 | Priority: 80 3 | Diff for file `test/lints/dart/prefer_immediate_return/fix/prefer_immediate_return.dart:2`: 4 | ``` 5 | int sum(int a, int b) { 6 | - final sum = a + b; 7 | - // expect_lint: prefer_immediate_return 8 | - return sum; 9 | + // expect_lint: prefer_immediate_return 10 | + return a + b; 11 | } 12 | ``` 13 | --- 14 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/avoid_single_child_in_flex/fix/avoid_single_child_in_flex_1.diff: -------------------------------------------------------------------------------- 1 | Message: `Replace with Align` 2 | Priority: 80 3 | Diff for file `test/lints/flutter/avoid_single_child_in_flex/fix/avoid_single_child_in_flex.dart:4`: 4 | ``` 5 | 6 | // expect_lint: avoid_single_child_in_flex 7 | - const singleChild = Column( 8 | - children: [ 9 | - Placeholder(), 10 | - ], 11 | + const singleChild = Align(child: Placeholder(),); 12 | ``` 13 | --- 14 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_const_constructor_declarations/fix/prefer_const_constructor_declarations.dart: -------------------------------------------------------------------------------- 1 | class Point { 2 | final double x; 3 | final double y; 4 | 5 | // expect_lint: prefer_const_constructor_declarations 6 | Point(this.x, this.y); 7 | 8 | // expect_lint: prefer_const_constructor_declarations 9 | Point.origin() : x = 0.0, y = 0.0; 10 | } 11 | 12 | class A { 13 | // expect_lint: prefer_const_constructor_declarations 14 | A(); 15 | } 16 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/avoid_single_child_in_flex/fix/avoid_single_child_in_flex_2.diff: -------------------------------------------------------------------------------- 1 | Message: `Replace with Center` 2 | Priority: 80 3 | Diff for file `test/lints/flutter/avoid_single_child_in_flex/fix/avoid_single_child_in_flex.dart:4`: 4 | ``` 5 | 6 | // expect_lint: avoid_single_child_in_flex 7 | - const singleChild = Column( 8 | - children: [ 9 | - Placeholder(), 10 | - ], 11 | + const singleChild = Center(child: Placeholder(),); 12 | ``` 13 | --- 14 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/unnecessary_nullable_return_type/fix/unnecessary_nullable_return_type.dart: -------------------------------------------------------------------------------- 1 | // expect_lint: unnecessary_nullable_return_type 2 | int? sum(int a, int b) { 3 | return a + b; 4 | } 5 | 6 | class A { 7 | // expect_lint: unnecessary_nullable_return_type 8 | int? sum(int a, int b) => a + b; 9 | 10 | String? method(bool isNull) { 11 | if (isNull) { 12 | return null; 13 | } else { 14 | return 'not null'; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_new_line_before_return/fix/prefer_new_line_before_return.diff: -------------------------------------------------------------------------------- 1 | Message: `Add new line before return statement.` 2 | Priority: 80 3 | Diff for file `test/lints/dart/prefer_new_line_before_return/fix/prefer_new_line_before_return.dart:4`: 4 | ``` 5 | if (isTrue) { 6 | return 'true'; 7 | - } // expect_lint: prefer_new_line_before_return 8 | + } 9 | + // expect_lint: prefer_new_line_before_return 10 | return 'false'; 11 | } 12 | ``` 13 | --- 14 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/flutter/wrap_with_layout_builder/wrap_with_layout_builder.diff: -------------------------------------------------------------------------------- 1 | Message: `Wrap with LayoutBuilder` 2 | Priority: 29 3 | Diff for file `test/assists/flutter/wrap_with_layout_builder/wrap_with_layout_builder.dart:11`: 4 | ``` 5 | builder: (_, _) { 6 | return const Center( 7 | - child: Placeholder(), 8 | + child: LayoutBuilder(builder: (context, constraints) { return Placeholder(); },), 9 | ); 10 | }, 11 | ``` 12 | --- 13 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/avoid_inverted_boolean_expressions/avoid_inverted_boolean_expressions.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_empty_blocks, unused_local_variable 2 | 3 | void example(int number) { 4 | // expect_lint: avoid_inverted_boolean_expressions 5 | if (!(number == 0)) {} 6 | 7 | // expect_lint: avoid_inverted_boolean_expressions 8 | if (!(number > 0)) {} 9 | 10 | // expect_lint: avoid_inverted_boolean_expressions 11 | final anotherNumber = !(number == 0) ? 1 : 2; 12 | } 13 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/avoid_inverted_boolean_expressions/fix/avoid_inverted_boolean_expressions.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_empty_blocks, unused_local_variable 2 | 3 | void example(int number) { 4 | // expect_lint: avoid_inverted_boolean_expressions 5 | if (!(number == 0)) {} 6 | 7 | // expect_lint: avoid_inverted_boolean_expressions 8 | if (!(number > 0)) {} 9 | 10 | // expect_lint: avoid_inverted_boolean_expressions 11 | final anotherNumber = !(number == 0) ? 1 : 2; 12 | } 13 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/always_put_doc_comments_before_annotations/fix/always_put_doc_comments_before_annotations.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart' show immutable; 2 | 3 | @immutable 4 | // expect_lint: always_put_doc_comments_before_annotations 5 | /// Some documentation. 6 | class A {} 7 | 8 | @immutable 9 | // expect_lint: always_put_doc_comments_before_annotations 10 | /// Some documentation. 11 | /// 12 | /// More documentation. 13 | @Deprecated('Use A instead') 14 | class B {} 15 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/avoid_nested_if.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: literal_only_boolean_expressions, avoid_empty_blocks 2 | 3 | void fn() { 4 | // expect_lint: avoid_nested_if 5 | if (true) { 6 | if (true) { 7 | if (true) {} 8 | } 9 | } 10 | } 11 | 12 | void fn2(int a) { 13 | if (a == 1) { 14 | } else if (a == 2) { 15 | // expect_lint: avoid_nested_if 16 | } else if (a == 3) { 17 | if (true) { 18 | if (true) {} 19 | } 20 | } else if (a == 4) {} 21 | } 22 | -------------------------------------------------------------------------------- /docs/dart-assists/invert_boolean_expression.mdx: -------------------------------------------------------------------------------- 1 | # invert_boolean_expression 2 | 3 | Invert a boolean expression. 4 | 5 | ```dart title="Before" 6 | void fn(bool condition) { 7 | if (number == 0) { 8 | // ^^^^^^^^^^^ 9 | // ... 10 | } 11 | } 12 | ``` 13 | 14 | ```dart title="After" 15 | void fn(bool condition) { 16 | if (number != 0) { 17 | // ... 18 | } 19 | } 20 | ``` 21 | 22 | ![invert_boolean_expression](https://raw.githubusercontent.com/charlescyt/pyramid_lint/main/docs/assets/invert_boolean_expression.gif) 23 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_iterable_first/fix/prefer_iterable_first.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: unused_local_variable 2 | 3 | const numbers = [1, 2, 3]; 4 | 5 | // expect_lint: prefer_iterable_first 6 | final a = numbers[0]; 7 | 8 | // expect_lint: prefer_iterable_first 9 | final b = numbers.elementAt(0); 10 | 11 | final listOfLists = [ 12 | [1, 2, 3], 13 | [4, 5, 6], 14 | [7, 8, 9], 15 | ]; 16 | 17 | void fn() { 18 | // expect_lint: prefer_iterable_first 19 | final value = listOfLists[0][1]; 20 | } 21 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_iterable_first/prefer_iterable_first.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: unused_local_variable 2 | 3 | const numbers = [1, 2, 3]; 4 | 5 | // expect_lint: prefer_iterable_first 6 | final a = numbers[0]; 7 | 8 | // expect_lint: prefer_iterable_first 9 | final b = numbers.elementAt(0); 10 | 11 | final listOfLists = [ 12 | [1, 2, 3], 13 | [4, 5, 6], 14 | [7, 8, 9], 15 | ]; 16 | 17 | void fn() { 18 | // expect_lint: prefer_iterable_first 19 | final value = listOfLists[0][1]; 20 | } 21 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_async_await.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_print, unnecessary_lambdas 2 | 3 | void fetchData() { 4 | // expect_lint: prefer_async_await 5 | performAsyncOperation() 6 | .then((result) { 7 | print(result); 8 | }) 9 | .catchError((Object? error) { 10 | print('Error: $error'); 11 | }) 12 | .whenComplete(() { 13 | print('Done'); 14 | }); 15 | } 16 | 17 | Future performAsyncOperation() async { 18 | return 'Result'; 19 | } 20 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/flutter/wrap_with_stack/wrap_with_stack.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class A extends StatelessWidget { 4 | const A({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return const Column( 9 | children: [ 10 | Text( 11 | 'Pyramid', 12 | style: TextStyle( 13 | fontSize: 20, 14 | fontWeight: FontWeight.bold, 15 | ), 16 | ), 17 | Text('Lint'), 18 | ], 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /docs/flutter-assists/wrap_with_listenable_builder.mdx: -------------------------------------------------------------------------------- 1 | # wrap_with_listenable_builder 2 | 3 | Wrap the selected widget with a `ListenableBuilder`. 4 | 5 | ```dart title="Before" 6 | Widget build(BuildContext context) { 7 | return Text('${counterNotifier.count}'); 8 | // ^^^^ 9 | } 10 | ``` 11 | 12 | ```dart title="After" 13 | Widget build(BuildContext context) { 14 | return ListenableBuilder( 15 | listenable: counterNotifier, 16 | builder: (context, child) { 17 | return Text('${counterNotifier.count}'); 18 | }, 19 | ); 20 | } 21 | ``` 22 | -------------------------------------------------------------------------------- /docs/flutter-assists/wrap_with_stack.mdx: -------------------------------------------------------------------------------- 1 | # wrap_with_stack 2 | 3 | Wrap the selected widget with a Stack. 4 | 5 | ```dart title="Before" 6 | Widget build(BuildContext context) { 7 | return const Placeholder(); 8 | // ^^^^^^^^^^^^^^^^^ 9 | } 10 | ``` 11 | 12 | ```dart title="After" 13 | Widget build(BuildContext context) { 14 | return const Stack( 15 | children: [ 16 | Placeholder(), 17 | ], 18 | ); 19 | } 20 | ``` 21 | 22 | ![wrap_with_stack](https://raw.githubusercontent.com/charlescyt/pyramid_lint/main/docs/assets/wrap_with_stack.gif) 23 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/proper_super_dispose/fix/proper_super_dispose.diff: -------------------------------------------------------------------------------- 1 | Message: `Place super.dispose() at the end of the dispose method` 2 | Priority: 80 3 | Diff for file `test/lints/flutter/proper_super_dispose/fix/proper_super_dispose.dart:16`: 4 | ``` 5 | void dispose() { 6 | // expect_lint: proper_super_dispose 7 | - super.dispose(); 8 | - _dispose(); 9 | - _dispose2(); 10 | - _dispose3(); 11 | + _dispose(); 12 | + _dispose2(); 13 | + _dispose3(); 14 | + super.dispose(); 15 | } 16 | 17 | ``` 18 | --- 19 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/prefer_border_from_border_side/fix/prefer_border_from_border_side.diff: -------------------------------------------------------------------------------- 1 | Message: `Replace with Border.fromBorderSide` 2 | Priority: 80 3 | Diff for file `test/lints/flutter/prefer_border_from_border_side/fix/prefer_border_from_border_side.dart:4`: 4 | ``` 5 | 6 | // expect_lint: prefer_border_from_border_side 7 | - final border = Border.all( 8 | - width: 1, 9 | - style: BorderStyle.solid, 10 | + final border = Border.fromBorderSide(BorderSide( 11 | + width: 1, 12 | + style: BorderStyle.solid, 13 | + ),); 14 | ``` 15 | --- 16 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/use_spacer/use_spacer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | final column = Column( 4 | children: [ 5 | // expect_lint: use_spacer 6 | const Expanded( 7 | child: SizedBox(), 8 | ), 9 | // expect_lint: use_spacer 10 | Expanded( 11 | flex: 2, 12 | child: Container(), 13 | ), 14 | // Using Expanded with a non-empty Container or SizedBox is fine. 15 | const Expanded( 16 | child: SizedBox( 17 | child: Placeholder(), 18 | ), 19 | ), 20 | ], 21 | ); 22 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_iterable_last/fix/prefer_iterable_last.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: unused_local_variable 2 | 3 | const numbers = [1, 2, 3]; 4 | 5 | // expect_lint: prefer_iterable_last 6 | final a = numbers[numbers.length - 1]; 7 | 8 | // expect_lint: prefer_iterable_last 9 | final b = numbers.elementAt(numbers.length - 1); 10 | 11 | final listOfLists = [ 12 | [1, 2, 3], 13 | [4, 5, 6], 14 | [7, 8, 9], 15 | ]; 16 | 17 | void fn() { 18 | // expect_lint: prefer_iterable_last 19 | final value = listOfLists[listOfLists.length - 1][1]; 20 | } 21 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_iterable_last/prefer_iterable_last.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: unused_local_variable 2 | 3 | const numbers = [1, 2, 3]; 4 | 5 | // expect_lint: prefer_iterable_last 6 | final a = numbers[numbers.length - 1]; 7 | 8 | // expect_lint: prefer_iterable_last 9 | final b = numbers.elementAt(numbers.length - 1); 10 | 11 | final listOfLists = [ 12 | [1, 2, 3], 13 | [4, 5, 6], 14 | [7, 8, 9], 15 | ]; 16 | 17 | void fn() { 18 | // expect_lint: prefer_iterable_last 19 | final value = listOfLists[listOfLists.length - 1][1]; 20 | } 21 | -------------------------------------------------------------------------------- /packages/pyramid_lint/lib/src/utils/version_constraint_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:pub_semver/pub_semver.dart'; 2 | 3 | extension VersionConstraintExtension on VersionConstraint { 4 | bool meetMinimumRequiredVersion(Version minimumRequiredDartSdkVersion) { 5 | return switch (this) { 6 | final Version version => version >= minimumRequiredDartSdkVersion, 7 | final VersionRange range when range.isAny || range.isEmpty => false, 8 | final VersionRange range => range.min != null && range.min! >= minimumRequiredDartSdkVersion, 9 | _ => false, 10 | }; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/pyramid_lint/lib/src/utils/string_extension.dart: -------------------------------------------------------------------------------- 1 | extension StringExtension on String { 2 | /// Capitalizes the first letter of this string. 3 | String capitalize() => switch (length) { 4 | 0 => this, 5 | 1 => toUpperCase(), 6 | _ => '${this[0].toUpperCase()}${substring(1)}', 7 | }; 8 | 9 | /// Returns `true` if this string contains only underscores. 10 | bool get containsOnlyUnderscores => switch (length) { 11 | 0 => false, 12 | 1 => this == '_', 13 | 2 => this == '__', 14 | 3 => this == '___', 15 | _ => RegExp(r'^_+$').hasMatch(this), 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: pyramid_lint_test 2 | description: Test for pyramid_lint 3 | publish_to: none 4 | 5 | environment: 6 | sdk: ">=3.10.0 <4.0.0" 7 | flutter: ">=3.38.1" 8 | 9 | dependencies: 10 | collection: ^1.19.0 11 | flutter: 12 | sdk: flutter 13 | 14 | dev_dependencies: 15 | analyzer: ^8.0.0 16 | analyzer_plugin: ^0.13.5 17 | custom_lint: ^0.8.1 18 | custom_lint_core: ^0.8.1 19 | flutter_test: 20 | sdk: flutter 21 | path: ^1.8.3 22 | pub_semver: ^2.1.4 23 | pubspec_parse: ^1.5.0 24 | pyramid_lint: 25 | path: ../pyramid_lint 26 | test: ^1.26.3 27 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/always_put_doc_comments_before_annotations/always_put_doc_comments_before_annotations.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart' show immutable; 2 | 3 | @immutable 4 | // expect_lint: always_put_doc_comments_before_annotations 5 | /// Some documentation. 6 | class A {} 7 | 8 | @immutable 9 | // expect_lint: always_put_doc_comments_before_annotations 10 | /// Some documentation. 11 | /// 12 | /// More documentation. 13 | @Deprecated('Use A instead') 14 | class B {} 15 | 16 | /// Some documentation. 17 | /// 18 | /// More documentation. 19 | @immutable 20 | class C {} 21 | -------------------------------------------------------------------------------- /docs/flutter-assists/use_edge_insets_zero.mdx: -------------------------------------------------------------------------------- 1 | # use_edge_insets_zero 2 | 3 | Replace 4 | 5 | - EdgeInsets.all(0) 6 | - EdgeInsets.fromLTRB(0, 0, 0, 0) 7 | - EdgeInsets.only(left: 0, top: 0, right: 0, bottom: 0) 8 | - EdgeInsets.symmetric(horizontal: 0, vertical: 0) 9 | 10 | with EdgeInsets.zero. 11 | 12 | ```dart title="Before" 13 | const edgeInsets = EdgeInsets.all(0); 14 | // ^^^^^^^^^^^^^^ 15 | ``` 16 | 17 | ```dart title="After" 18 | const edgeInsets = EdgeInsets.zero; 19 | ``` 20 | 21 | ![use_edge_insets_zero](https://raw.githubusercontent.com/charlescyt/pyramid_lint/main/docs/assets/use_edge_insets_zero.gif) 22 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/unnecessary_nullable_return_type/unnecessary_nullable_return_type.dart: -------------------------------------------------------------------------------- 1 | // expect_lint: unnecessary_nullable_return_type 2 | int? sum(int a, int b) { 3 | return a + b; 4 | } 5 | 6 | String? function(bool isNull) { 7 | if (isNull) { 8 | return null; 9 | } else { 10 | return 'not null'; 11 | } 12 | } 13 | 14 | class A { 15 | // expect_lint: unnecessary_nullable_return_type 16 | int? sum(int a, int b) => a + b; 17 | 18 | String? method(bool isNull) { 19 | if (isNull) { 20 | return null; 21 | } else { 22 | return 'not null'; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/specify_icon_button_tooltip/fix/specify_icon_button_tooltip.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_empty_blocks 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | // expect_lint: specify_icon_button_tooltip 6 | final Widget a = IconButton( 7 | icon: const Icon(Icons.add), 8 | onPressed: () {}, 9 | ); 10 | 11 | // expect_lint: specify_icon_button_tooltip 12 | final Widget b = IconButton.filled( 13 | icon: const Icon(Icons.add), 14 | onPressed: () {}, 15 | ); 16 | 17 | final Widget c = IconButton( 18 | tooltip: '', 19 | icon: const Icon(Icons.add), 20 | onPressed: () {}, 21 | ); 22 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/specify_icon_button_tooltip/specify_icon_button_tooltip.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_empty_blocks 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | // expect_lint: specify_icon_button_tooltip 6 | final Widget a = IconButton( 7 | icon: const Icon(Icons.add), 8 | onPressed: () {}, 9 | ); 10 | 11 | // expect_lint: specify_icon_button_tooltip 12 | final Widget b = IconButton.filled( 13 | icon: const Icon(Icons.add), 14 | onPressed: () {}, 15 | ); 16 | 17 | final Widget c = IconButton( 18 | tooltip: '', 19 | icon: const Icon(Icons.add), 20 | onPressed: () {}, 21 | ); 22 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/proper_super_init_state/fix/proper_super_init_state.diff: -------------------------------------------------------------------------------- 1 | Message: `Put super.initState() at the start of the initState method` 2 | Priority: 80 3 | Diff for file `test/lints/flutter/proper_super_init_state/fix/proper_super_init_state.dart:15`: 4 | ``` 5 | @override 6 | void initState() { 7 | - _init(); 8 | - _init2(); 9 | - _init3(); 10 | - // expect_lint: proper_super_init_state 11 | - super.initState(); 12 | + super.initState(); 13 | + _init(); 14 | + _init2(); 15 | + // expect_lint: proper_super_init_state 16 | + _init3(); 17 | } 18 | 19 | ``` 20 | --- 21 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/avoid_single_child_in_flex/avoid_single_child_in_flex.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | // expect_lint: avoid_single_child_in_flex 4 | const singleChild = Column( 5 | children: [ 6 | Placeholder(), 7 | ], 8 | ); 9 | 10 | // Spread elements will not trigger the lint. 11 | final spread = Row( 12 | children: [ 13 | ...[1, 2, 3].map((e) => Text('$e')), 14 | ], 15 | ); 16 | 17 | // Collection for will not trigger the lint. 18 | final collectionFor = Flex( 19 | direction: Axis.vertical, 20 | children: [ 21 | for (final e in [1, 2, 3]) Text('$e'), 22 | ], 23 | ); 24 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/avoid_unused_parameters.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_empty_blocks, avoid_print 2 | 3 | void function1(String s) { 4 | print(s); 5 | } 6 | 7 | // expect_lint: avoid_unused_parameters 8 | void function2(String s) {} 9 | 10 | class A { 11 | void method1(String s) => print(s); 12 | 13 | // expect_lint: avoid_unused_parameters 14 | void method2(String s) {} 15 | } 16 | 17 | class B extends A { 18 | @override 19 | void method1(String s) {} // overridden method will not trigger the lint 20 | } 21 | 22 | abstract class C { 23 | void method(String s); // abstract class will not trigger the lint 24 | } 25 | -------------------------------------------------------------------------------- /docs/dart-assists/convert_to_for_in_iterable_indexed_loop.mdx: -------------------------------------------------------------------------------- 1 | # convert_to_for_in_iterable_indexed_loop 2 | 3 | Convert a `for-in` loop to a `for-in` loop that uses `iterable.indexed` instead of `iterable` directly. 4 | 5 | ```dart title="Before" 6 | void fn(Iterable numbers) { 7 | for (final int number in numbers) {} 8 | } 9 | ``` 10 | 11 | ```dart title="After" 12 | void fn(Iterable numbers) { 13 | for (final (int index, int number) in numbers.indexed) {} 14 | } 15 | ``` 16 | 17 | ![convert_to_for_in_iterable_indexed_loop](https://raw.githubusercontent.com/charlescyt/pyramid_lint/main/docs/assets/convert_to_for_in_iterable_indexed_loop.gif) 18 | -------------------------------------------------------------------------------- /docs/dart-assists/swap_then_else_expression.mdx: -------------------------------------------------------------------------------- 1 | # swap_then_else_expression 2 | 3 | Swap the then and else expression of a if-else expression or conditional expression. 4 | 5 | ```dart title="Before" 6 | void fn(bool condition) { 7 | if (condition) { 8 | //^^^^^^^^^^^^^^ 9 | print('then'); 10 | } else { 11 | print('else'); 12 | } 13 | } 14 | ``` 15 | 16 | ```dart title="After" 17 | void fn(bool condition) { 18 | if (condition) { 19 | print('else'); 20 | } else { 21 | print('then'); 22 | } 23 | } 24 | ``` 25 | 26 | ![swap_then_else_expression](https://raw.githubusercontent.com/charlescyt/pyramid_lint/main/docs/assets/swap_then_else_expression.gif) 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/3-documentation-update.yml: -------------------------------------------------------------------------------- 1 | name: 📚 Documentation Update 2 | description: Suggest improvements or clarifications in documentation 3 | labels: ["documentation", "needs triage"] 4 | assignees: [charlescyt] 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: Documentation Update Description 9 | description: A clear and concise description of the documentation update you would like to see. 10 | validations: 11 | required: true 12 | 13 | - type: textarea 14 | attributes: 15 | label: Additional Information 16 | description: Add any additional information that could help us understand the requested documentation update better. 17 | -------------------------------------------------------------------------------- /docs/flutter-lints/prefer_void_callback.mdx: -------------------------------------------------------------------------------- 1 | # prefer_void_callback 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **PREFER** using the built-in typedef `VoidCallback` instead of declaring a custom one. 10 | 11 | ```dart title="Bad" 12 | final void Function() cb; 13 | ``` 14 | 15 | ```dart title="Good" 16 | final VoidCallback cb; 17 | ``` 18 | 19 | ## Usage 20 | 21 | To enable the `prefer_void_callback` rule, add `prefer_void_callback` under custom_lint > rules in your `analysis_options.yaml` file: 22 | 23 | ```yaml 24 | custom_lint: 25 | rules: 26 | - prefer_void_callback 27 | ``` 28 | -------------------------------------------------------------------------------- /docs/dart-lints/no_self_comparisons.mdx: -------------------------------------------------------------------------------- 1 | # no_self_comparisons 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Warning | ❌ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** avoid self comparisons. 10 | 11 | ```dart title="Bad" 12 | if (foo == foo) {} 13 | if (foo.property == foo.property) {} 14 | ``` 15 | 16 | ```dart title="Good" 17 | if (foo == bar) {} 18 | if (foo.property == bar.property) {} 19 | ``` 20 | 21 | ## Usage 22 | 23 | To enable the `no_self_comparisons` rule, add `no_self_comparisons` under custom_lint > rules in your `analysis_options.yaml` file: 24 | 25 | ```yaml 26 | custom_lint: 27 | rules: 28 | - no_self_comparisons 29 | ``` 30 | -------------------------------------------------------------------------------- /docs/dart-lints/avoid_mutable_global_variables.mdx: -------------------------------------------------------------------------------- 1 | # avoid_mutable_global_variables 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Warning | ❌ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** avoid using mutable global variables. 10 | 11 | ```dart title="Bad" 12 | double pi = 3.14; 13 | ``` 14 | 15 | ```dart title="Good" 16 | const double pi = 3.14; 17 | const pi = 3.14; 18 | ``` 19 | 20 | ## Usage 21 | 22 | To enable the `avoid_mutable_global_variables` rule, add `avoid_mutable_global_variables` under custom_lint > rules in your `analysis_options.yaml` file: 23 | 24 | ```yaml 25 | custom_lint: 26 | rules: 27 | - avoid_mutable_global_variables 28 | ``` 29 | -------------------------------------------------------------------------------- /docs/flutter-lints/prefer_async_callback.mdx: -------------------------------------------------------------------------------- 1 | # prefer_async_callback 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **PREFER** using the built-in typedef `AsyncCallback` instead of declaring a custom one. 10 | 11 | ```dart title="Bad" 12 | final Future Function() cb; 13 | ``` 14 | 15 | ```dart title="Good" 16 | final AsyncCallback cb; 17 | ``` 18 | 19 | ## Usage 20 | 21 | To enable the `prefer_async_callback` rule, add `prefer_async_callback` under custom_lint > rules in your `analysis_options.yaml` file: 22 | 23 | ```yaml 24 | custom_lint: 25 | rules: 26 | - prefer_async_callback 27 | ``` 28 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/dart/invert_boolean_expression/invert_boolean_expression.diff: -------------------------------------------------------------------------------- 1 | Message: `Invert boolean expression` 2 | Priority: 80 3 | Diff for file `test/assists/dart/invert_boolean_expression/invert_boolean_expression.dart:4`: 4 | ``` 5 | 6 | void example(int number) { 7 | - if (number == 0) { 8 | + if (number != 0) { 9 | print('The number is zero'); 10 | } 11 | ``` 12 | --- 13 | Message: `Invert boolean expression` 14 | Priority: 80 15 | Diff for file `test/assists/dart/invert_boolean_expression/invert_boolean_expression.dart:8`: 16 | ``` 17 | } 18 | 19 | - if (number > 0) { 20 | + if (number <= 0) { 21 | print('The number is positive'); 22 | } 23 | ``` 24 | --- 25 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/prefer_void_callback/fix/prefer_void_callback.diff: -------------------------------------------------------------------------------- 1 | Message: `Replace with VoidCallback` 2 | Priority: 80 3 | Diff for file `test/lints/flutter/prefer_void_callback/fix/prefer_void_callback.dart:2`: 4 | ``` 5 | // expect_lint: prefer_void_callback 6 | - typedef A = void Function(); 7 | + typedef A = VoidCallback; 8 | 9 | // expect_lint: prefer_void_callback 10 | ``` 11 | --- 12 | Message: `Replace with VoidCallback?` 13 | Priority: 80 14 | Diff for file `test/lints/flutter/prefer_void_callback/fix/prefer_void_callback.dart:5`: 15 | ``` 16 | 17 | // expect_lint: prefer_void_callback 18 | - typedef B = void Function()?; 19 | + typedef B = VoidCallback?; 20 | ``` 21 | --- 22 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/prefer_async_callback/fix/prefer_async_callback.diff: -------------------------------------------------------------------------------- 1 | Message: `Replace with AsyncCallback` 2 | Priority: 80 3 | Diff for file `test/lints/flutter/prefer_async_callback/fix/prefer_async_callback.dart:2`: 4 | ``` 5 | // expect_lint: prefer_async_callback 6 | - typedef A = Future Function(); 7 | + typedef A = AsyncCallback; 8 | 9 | // expect_lint: prefer_async_callback 10 | ``` 11 | --- 12 | Message: `Replace with AsyncCallback?` 13 | Priority: 80 14 | Diff for file `test/lints/flutter/prefer_async_callback/fix/prefer_async_callback.dart:5`: 15 | ``` 16 | 17 | // expect_lint: prefer_async_callback 18 | - typedef B = Future Function()?; 19 | + typedef B = AsyncCallback?; 20 | ``` 21 | --- 22 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/use_spacer/fix/use_spacer.diff: -------------------------------------------------------------------------------- 1 | Message: `Replace with Spacer` 2 | Priority: 80 3 | Diff for file `test/lints/flutter/use_spacer/fix/use_spacer.dart:6`: 4 | ``` 5 | children: [ 6 | // expect_lint: use_spacer 7 | - const Expanded( 8 | - child: SizedBox(), 9 | - ), 10 | + const Spacer(), 11 | // expect_lint: use_spacer 12 | Expanded( 13 | ``` 14 | --- 15 | Message: `Replace with Spacer` 16 | Priority: 80 17 | Diff for file `test/lints/flutter/use_spacer/fix/use_spacer.dart:10`: 18 | ``` 19 | ), 20 | // expect_lint: use_spacer 21 | - Expanded( 22 | - flex: 2, 23 | - child: Container(), 24 | - ), 25 | + Spacer(flex: 2), 26 | ], 27 | ); 28 | ``` 29 | --- 30 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/flutter/wrap_all_children_with_expanded/wrap_all_children_with_expanded.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class A extends StatelessWidget { 4 | const A({super.key}); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return const Column( 9 | children: [ 10 | Row( 11 | children: [ 12 | Flexible( 13 | child: Text('Pyramid'), 14 | ), 15 | Spacer(), 16 | Text('Lint'), 17 | ], 18 | ), 19 | Row( 20 | children: [ 21 | Text('Is'), 22 | Spacer(), 23 | Text('Great'), 24 | ], 25 | ), 26 | ], 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /docs/dart-lints/avoid_empty_blocks.mdx: -------------------------------------------------------------------------------- 1 | # avoid_empty_blocks 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Warning | ❌ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** avoid empty blocks as they are usually a sign of missing implementation. 10 | 11 | ```dart title="Bad" 12 | void doSomething() {} 13 | ``` 14 | 15 | ```dart title="Good" 16 | void doSomething() { 17 | actuallyDoSomething(); 18 | } 19 | 20 | void doSomething() { 21 | // TODO: implement doSomething 22 | } 23 | ``` 24 | 25 | ## Usage 26 | 27 | To enable the `avoid_empty_blocks` rule, add `avoid_empty_blocks` under custom_lint > rules in your `analysis_options.yaml` file: 28 | 29 | ```yaml 30 | custom_lint: 31 | rules: 32 | - avoid_empty_blocks 33 | ``` 34 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/proper_super_init_state/proper_super_init_state.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_empty_blocks 2 | 3 | import 'package:flutter/widgets.dart'; 4 | 5 | class Example extends StatefulWidget { 6 | const Example({super.key}); 7 | 8 | @override 9 | State createState() => _ExampleState(); 10 | } 11 | 12 | class _ExampleState extends State { 13 | @override 14 | void initState() { 15 | _init(); 16 | _init2(); 17 | _init3(); 18 | // expect_lint: proper_super_init_state 19 | super.initState(); 20 | } 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return const Placeholder(); 25 | } 26 | 27 | void _init() {} 28 | void _init2() {} 29 | void _init3() {} 30 | } 31 | -------------------------------------------------------------------------------- /docs/flutter-lints/use_spacer.mdx: -------------------------------------------------------------------------------- 1 | # use_spacer 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **PREFER** using `Spacer` instead of `Expanded` with an empty Container or SizedBox. 10 | 11 | ```dart title="Bad" 12 | Column( 13 | children: [ 14 | Expanded( 15 | flex: 2, 16 | child: SizedBox(), 17 | ), 18 | ], 19 | ) 20 | ``` 21 | 22 | ```dart title="Good" 23 | Column( 24 | children: [ 25 | Spacer(flex: 2), 26 | ], 27 | ) 28 | ``` 29 | 30 | ## Usage 31 | 32 | To enable the `use_spacer` rule, add `use_spacer` under custom_lint > rules in your `analysis_options.yaml` file: 33 | 34 | ```yaml 35 | custom_lint: 36 | rules: 37 | - use_spacer 38 | ``` 39 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/proper_super_dispose/proper_super_dispose.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_empty_blocks 2 | 3 | import 'package:flutter/widgets.dart'; 4 | 5 | class Example extends StatefulWidget { 6 | const Example({super.key}); 7 | 8 | @override 9 | State createState() => _ExampleState(); 10 | } 11 | 12 | class _ExampleState extends State { 13 | @override 14 | void dispose() { 15 | // expect_lint: proper_super_dispose 16 | super.dispose(); 17 | _dispose(); 18 | _dispose2(); 19 | _dispose3(); 20 | } 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return const Placeholder(); 25 | } 26 | 27 | void _dispose() {} 28 | void _dispose2() {} 29 | void _dispose3() {} 30 | } 31 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/proper_super_init_state/fix/proper_super_init_state.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_empty_blocks 2 | 3 | import 'package:flutter/widgets.dart'; 4 | 5 | class Example extends StatefulWidget { 6 | const Example({super.key}); 7 | 8 | @override 9 | State createState() => _ExampleState(); 10 | } 11 | 12 | class _ExampleState extends State { 13 | @override 14 | void initState() { 15 | _init(); 16 | _init2(); 17 | _init3(); 18 | // expect_lint: proper_super_init_state 19 | super.initState(); 20 | } 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return const Placeholder(); 25 | } 26 | 27 | void _init() {} 28 | void _init2() {} 29 | void _init3() {} 30 | } 31 | -------------------------------------------------------------------------------- /docs/dart-lints/prefer_new_line_before_return.mdx: -------------------------------------------------------------------------------- 1 | # prefer_new_line_before_return 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** add a new line before the return statement. 10 | 11 | ```dart title="Bad" 12 | if (...) { 13 | ... 14 | return ...; 15 | } 16 | return ...; 17 | ``` 18 | 19 | ```dart title="Good" 20 | if (...) { 21 | ... 22 | 23 | return ...; 24 | } 25 | 26 | return ...; 27 | ``` 28 | 29 | ## Usage 30 | 31 | To enable the `prefer_new_line_before_return` rule, add `prefer_new_line_before_return` under custom_lint > rules in your `analysis_options.yaml` file: 32 | 33 | ```yaml 34 | custom_lint: 35 | rules: 36 | - prefer_new_line_before_return 37 | ``` 38 | -------------------------------------------------------------------------------- /docs/flutter-lints/proper_super_dispose.mdx: -------------------------------------------------------------------------------- 1 | # proper_super_dispose 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Error | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** call `super.dispose()` at the ***end*** of the `dispose` method. 10 | 11 | ```dart title="Bad" 12 | @override 13 | void dispose() { 14 | super.dispose(); 15 | _dispose(); 16 | } 17 | ``` 18 | 19 | ```dart title="Good" 20 | @override 21 | void dispose() { 22 | _dispose(); 23 | super.dispose(); 24 | } 25 | ``` 26 | 27 | ## Usage 28 | 29 | To enable the `proper_super_dispose` rule, add `proper_super_dispose` under custom_lint > rules in your `analysis_options.yaml` file: 30 | 31 | ```yaml 32 | custom_lint: 33 | rules: 34 | - proper_super_dispose 35 | ``` 36 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/proper_super_dispose/fix/proper_super_dispose.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_empty_blocks 2 | 3 | import 'package:flutter/widgets.dart'; 4 | 5 | class Example extends StatefulWidget { 6 | const Example({super.key}); 7 | 8 | @override 9 | State createState() => _ExampleState(); 10 | } 11 | 12 | class _ExampleState extends State { 13 | @override 14 | void dispose() { 15 | // expect_lint: proper_super_dispose 16 | super.dispose(); 17 | _dispose(); 18 | _dispose2(); 19 | _dispose3(); 20 | } 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return const Placeholder(); 25 | } 26 | 27 | void _dispose() {} 28 | void _dispose2() {} 29 | void _dispose3() {} 30 | } 31 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/prefer_async_callback/prefer_async_callback.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart' show AsyncCallback; 2 | 3 | typedef A = AsyncCallback; 4 | // expect_lint: prefer_async_callback 5 | typedef B = Future Function(); 6 | typedef B2 = Future Function(); 7 | // ignore: strict_raw_type 8 | typedef B3 = Future Function(); 9 | // expect_lint: prefer_async_callback 10 | typedef C = Future Function()?; 11 | typedef D = Future Function(int index); 12 | typedef E = Future Function({required int index}); 13 | 14 | Future thenOrNull(Future Function() cb) async { 15 | try { 16 | return await cb(); 17 | // ignore: avoid_catches_without_on_clauses 18 | } catch (_) { 19 | return null; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/dart/swap_then_else_expression/swap_then_else_expression.diff: -------------------------------------------------------------------------------- 1 | Message: `Swap then and else expression` 2 | Priority: 80 3 | Diff for file `test/assists/dart/swap_then_else_expression/swap_then_else_expression.dart:5`: 4 | ``` 5 | void example(bool condition) { 6 | if (condition) { 7 | - print('then'); 8 | - } else { 9 | - print('else'); 10 | + print('else'); 11 | + } else { 12 | + print('then'); 13 | } 14 | 15 | ``` 16 | --- 17 | Message: `Swap then and else expression` 18 | Priority: 80 19 | Diff for file `test/assists/dart/swap_then_else_expression/swap_then_else_expression.dart:10`: 20 | ``` 21 | } 22 | 23 | - final text = condition ? 'then' : 'else'; 24 | + final text = condition ? 'else' : 'then'; 25 | } 26 | ``` 27 | --- 28 | -------------------------------------------------------------------------------- /docs/flutter-lints/avoid_single_child_in_flex.mdx: -------------------------------------------------------------------------------- 1 | # avoid_single_child_in_flex 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **Avoid** using Column or Row with only one child. 10 | 11 | ```dart title="Bad" 12 | Row( 13 | children: [ 14 | Placeholder(), 15 | ], 16 | ) 17 | ``` 18 | 19 | ```dart title="Good" 20 | Align( 21 | child: Placeholder(), 22 | ) 23 | 24 | // or 25 | 26 | Center( 27 | child: Placeholder(), 28 | ) 29 | ``` 30 | 31 | ## Usage 32 | 33 | To enable the `avoid_single_child_in_flex` rule, add `avoid_single_child_in_flex` under custom_lint > rules in your `analysis_options.yaml` file: 34 | 35 | ```yaml 36 | custom_lint: 37 | rules: 38 | - avoid_single_child_in_flex 39 | ``` 40 | -------------------------------------------------------------------------------- /docs/flutter-lints/proper_super_init_state.mdx: -------------------------------------------------------------------------------- 1 | # proper_super_init_state 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Error | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** call `super.initState()` at the ***start*** of the `initState` method. 10 | 11 | ```dart title="Bad" 12 | @override 13 | void initState() { 14 | _init(); 15 | super.initState(); 16 | } 17 | ``` 18 | 19 | ```dart title="Good" 20 | @override 21 | void initState() { 22 | super.initState(); 23 | _init(); 24 | } 25 | ``` 26 | 27 | ## Usage 28 | 29 | To enable the `proper_super_init_state` rule, add `proper_super_init_state` under custom_lint > rules in your `analysis_options.yaml` file: 30 | 31 | ```yaml 32 | custom_lint: 33 | rules: 34 | - proper_super_init_state 35 | ``` 36 | -------------------------------------------------------------------------------- /docs/dart-lints/prefer_iterable_any.mdx: -------------------------------------------------------------------------------- 1 | # prefer_iterable_any 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** use `iterable.any` instead of `iterable.where().isNotEmpty`. 10 | 11 | ```dart title="Bad" 12 | const numbers = [1, 2, 3]; 13 | final hasPositiveNumber = numbers.where((number) => number > 0).isNotEmpty; 14 | ``` 15 | 16 | ```dart title="Good" 17 | const numbers = [1, 2, 3]; 18 | final hasPositiveNumber = numbers.any((number) => number > 0); 19 | ``` 20 | 21 | ## Usage 22 | 23 | To enable the `prefer_iterable_any` rule, add `prefer_iterable_any` under custom_lint > rules in your `analysis_options.yaml` file: 24 | 25 | ```yaml 26 | custom_lint: 27 | rules: 28 | - prefer_iterable_any 29 | ``` 30 | -------------------------------------------------------------------------------- /docs/dart-lints/prefer_iterable_every.mdx: -------------------------------------------------------------------------------- 1 | # prefer_iterable_every 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** use `iterable.every` instead of `iterable.where().isEmpty`. 10 | 11 | ```dart title="Bad" 12 | const numbers = [1, 2, 3]; 13 | final areAllPositive = numbers.where((number) => number < 0).isEmpty; 14 | ``` 15 | 16 | ```dart title="Good" 17 | const numbers = [1, 2, 3]; 18 | final areAllPositive = numbers.every((number) => number > 0); 19 | ``` 20 | 21 | ## Usage 22 | 23 | To enable the `prefer_iterable_every` rule, add `prefer_iterable_every` under custom_lint > rules in your `analysis_options.yaml` file: 24 | 25 | ```yaml 26 | custom_lint: 27 | rules: 28 | - prefer_iterable_every 29 | ``` 30 | -------------------------------------------------------------------------------- /docs/dart-lints/prefer_immediate_return.mdx: -------------------------------------------------------------------------------- 1 | # prefer_immediate_return 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** return the result of an expression directly instead of assigning it to a variable and then returning the variable. 10 | 11 | ```dart title="Bad" 12 | int add(int a, int b) { 13 | final result = a + b; 14 | return result; 15 | } 16 | ``` 17 | 18 | ```dart title="Good" 19 | int add(int a, int b) { 20 | return a + b; 21 | } 22 | ``` 23 | 24 | ## Usage 25 | 26 | To enable the `prefer_immediate_return` rule, add `prefer_immediate_return` under custom_lint > rules in your `analysis_options.yaml` file: 27 | 28 | ```yaml 29 | custom_lint: 30 | rules: 31 | - prefer_immediate_return 32 | ``` 33 | -------------------------------------------------------------------------------- /docs/dart-lints/unnecessary_nullable_return_type.mdx: -------------------------------------------------------------------------------- 1 | # unnecessary_nullable_return_type 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Warning | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **AVOID** declaring functions or methods with a nullable return type when the return value is never null. 10 | 11 | ```dart title="Bad" 12 | int? sum(int a, int b) { 13 | return a + b; 14 | } 15 | ``` 16 | 17 | ```dart title="Good" 18 | int sum(int a, int b) { 19 | return a + b; 20 | } 21 | ``` 22 | 23 | ## Usage 24 | 25 | To enable the `unnecessary_nullable_return_type` rule, add `unnecessary_nullable_return_type` under custom_lint > rules in your `analysis_options.yaml` file: 26 | 27 | ```yaml 28 | custom_lint: 29 | rules: 30 | - unnecessary_nullable_return_type 31 | ``` 32 | -------------------------------------------------------------------------------- /packages/pyramid_lint/lib/src/utils/lint_code_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/error/error.dart' hide LintCode; 2 | import 'package:custom_lint_builder/custom_lint_builder.dart'; 3 | 4 | extension LintCodeExtension on LintCode { 5 | LintCode copyWith({ 6 | String? name, 7 | String? problemMessage, 8 | String? correctionMessage, 9 | String? uniqueName, 10 | String? url, 11 | DiagnosticSeverity? errorSeverity, 12 | }) { 13 | return LintCode( 14 | name: name ?? this.name, 15 | problemMessage: problemMessage ?? this.problemMessage, 16 | correctionMessage: correctionMessage ?? this.correctionMessage, 17 | uniqueName: uniqueName ?? this.uniqueName, 18 | url: url ?? this.url, 19 | errorSeverity: errorSeverity ?? this.errorSeverity, 20 | ); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /docs/dart-lints/prefer_iterable_first.mdx: -------------------------------------------------------------------------------- 1 | # prefer_iterable_first 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** use `iterable.first` instead of `iterable[0]` or `iterable.elementAt(0)`. 10 | 11 | ```dart title="Bad" 12 | const numbers = [1, 2, 3]; 13 | 14 | final firstNumber = numbers[0]; 15 | // or 16 | final firstNumber = numbers.elementAt(0); 17 | ``` 18 | 19 | ```dart title="Good" 20 | const numbers = [1, 2, 3]; 21 | final firstNumber = numbers.first; 22 | ``` 23 | 24 | ## Usage 25 | 26 | To enable the `prefer_iterable_first` rule, add `prefer_iterable_first` under custom_lint > rules in your `analysis_options.yaml` file: 27 | 28 | ```yaml 29 | custom_lint: 30 | rules: 31 | - prefer_iterable_first 32 | ``` 33 | -------------------------------------------------------------------------------- /docs/flutter-lints/specify_icon_button_tooltip.mdx: -------------------------------------------------------------------------------- 1 | # specify_icon_button_tooltip 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **PREFER** specifying a tooltip for an icon button for accessibility. 10 | 11 | ```dart title="Bad" 12 | IconButton( 13 | icon: Icon(Icons.add), 14 | onPressed: () {}, 15 | ) 16 | ``` 17 | 18 | ```dart title="Good" 19 | IconButton( 20 | tooltip: 'Add', 21 | icon: Icon(Icons.add), 22 | onPressed: () {}, 23 | ) 24 | ``` 25 | 26 | ## Usage 27 | 28 | To enable the `specify_icon_button_tooltip` rule, add `specify_icon_button_tooltip` under custom_lint > rules in your `analysis_options.yaml` file: 29 | 30 | ```yaml 31 | custom_lint: 32 | rules: 33 | - specify_icon_button_tooltip 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/flutter-lints/proper_expanded_and_flexible.mdx: -------------------------------------------------------------------------------- 1 | # proper_expanded_and_flexible 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Error | ❌ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** place Expanded and Flexible widgets inside a Row, Column, or Flex. 10 | 11 | ```dart title="Bad" 12 | Center( 13 | child: Expanded( 14 | child: Container(), 15 | ), 16 | ) 17 | ``` 18 | 19 | ```dart title="Good" 20 | Row( 21 | children: [ 22 | Expanded( 23 | child: Container(), 24 | ), 25 | ], 26 | ) 27 | ``` 28 | 29 | ## Usage 30 | 31 | To enable the `proper_expanded_and_flexible` rule, add `proper_expanded_and_flexible` under custom_lint > rules in your `analysis_options.yaml` file: 32 | 33 | ```yaml 34 | custom_lint: 35 | rules: 36 | - proper_expanded_and_flexible 37 | ``` 38 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_underscore_for_unused_callback_parameters.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_empty_blocks, avoid_unused_parameters 2 | 3 | import 'package:flutter/widgets.dart'; 4 | 5 | class Example extends StatelessWidget { 6 | const Example({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return ListView.builder( 11 | itemCount: 10, 12 | itemBuilder: 13 | ( 14 | // expect_lint: prefer_underscore_for_unused_callback_parameters 15 | context, 16 | // Used parameter will not trigger the lint. 17 | index, 18 | ) { 19 | return Text('Item $index'); 20 | }, 21 | ); 22 | } 23 | } 24 | 25 | // Function declarations will not trigger the lint. 26 | void log(String message) {} 27 | -------------------------------------------------------------------------------- /docs/dart-lints/no_duplicate_imports.mdx: -------------------------------------------------------------------------------- 1 | # no_duplicate_imports 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ❌ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** avoid duplicate imports as they can lead to confusion. 10 | 11 | ```dart title="Bad" 12 | import 'dart:math' as math show max; 13 | import 'dart:math'; 14 | 15 | final a = math.max(1, 10); 16 | final b = min(1, 10); 17 | ``` 18 | 19 | ```dart title="Good" 20 | import 'dart:math' as math show max, min; 21 | 22 | final a = math.max(1, 10); 23 | final b = math.min(1, 10); 24 | ``` 25 | 26 | ## Usage 27 | 28 | To enable the `no_duplicate_imports` rule, add `no_duplicate_imports` under custom_lint > rules in your `analysis_options.yaml` file: 29 | 30 | ```yaml 31 | custom_lint: 32 | rules: 33 | - no_duplicate_imports 34 | ``` 35 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/unnecessary_nullable_return_type/fix/unnecessary_nullable_return_type.diff: -------------------------------------------------------------------------------- 1 | Message: `Replace with non-nullable type` 2 | Priority: 80 3 | Diff for file `test/lints/dart/unnecessary_nullable_return_type/fix/unnecessary_nullable_return_type.dart:2`: 4 | ``` 5 | // expect_lint: unnecessary_nullable_return_type 6 | - int? sum(int a, int b) { 7 | + int sum(int a, int b) { 8 | return a + b; 9 | } 10 | ``` 11 | --- 12 | Message: `Replace with non-nullable type` 13 | Priority: 80 14 | Diff for file `test/lints/dart/unnecessary_nullable_return_type/fix/unnecessary_nullable_return_type.dart:8`: 15 | ``` 16 | class A { 17 | // expect_lint: unnecessary_nullable_return_type 18 | - int? sum(int a, int b) => a + b; 19 | + int sum(int a, int b) => a + b; 20 | 21 | String? method(bool isNull) { 22 | ``` 23 | --- 24 | -------------------------------------------------------------------------------- /docs/flutter-assists/wrap_with_layout_builder.mdx: -------------------------------------------------------------------------------- 1 | # wrap_with_layout_builder 2 | 3 | Wrap the selected widget with a LayoutBuilder. 4 | 5 | This assist is only available if the selected widget is not a LayoutBuilder. 6 | 7 | This assist is only available if the selected widget is not already wrapped with a LayoutBuilder. 8 | 9 | ```dart title="Before" 10 | Widget build(BuildContext context) { 11 | return const Placeholder(); 12 | // ^^^^^^^^^^^^^^^^^ 13 | } 14 | ``` 15 | 16 | ```dart title="After" 17 | Widget build(BuildContext context) { 18 | return LayoutBuilder( 19 | builder: (context, constraints) { 20 | return const Placeholder(); 21 | }, 22 | ); 23 | } 24 | ``` 25 | 26 | ![wrap_with_layout_builder](https://raw.githubusercontent.com/charlescyt/pyramid_lint/main/docs/assets/wrap_with_layout_builder.gif) 27 | -------------------------------------------------------------------------------- /docs/flutter-assists/wrap_all_children_with_expanded.mdx: -------------------------------------------------------------------------------- 1 | # wrap_all_children_with_expanded 2 | 3 | Wrap all children with `Expanded` widget. 4 | 5 | This assist is only available when the selected widget is a direct child of a `Flex` widget. 6 | 7 | This assist will skip children that are `Expanded`, `Flexible` or `Spacer`. 8 | 9 | ```dart title="Before" 10 | Widget build(BuildContext context) { 11 | return const Column( 12 | // ^^^^^^^^^^^^ 13 | children: [ 14 | Text('Hello'), 15 | Spacer(), 16 | Text('World'), 17 | ], 18 | ); 19 | } 20 | ``` 21 | 22 | ```dart title="After" 23 | Widget build(BuildContext context) { 24 | return const Column( 25 | children: [ 26 | Expanded(child: Text('Hello')), 27 | Spacer(), 28 | Expanded(child: Text('World')), 29 | ], 30 | ); 31 | } 32 | ``` 33 | -------------------------------------------------------------------------------- /docs/dart-lints/prefer_iterable_last.mdx: -------------------------------------------------------------------------------- 1 | # prefer_iterable_last 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** use `iterable.last` instead of `iterable[iterable.length - 1]` or `iterable.elementAt(iterable.length - 1)`. 10 | 11 | ```dart title="Bad" 12 | const numbers = [1, 2, 3]; 13 | 14 | final lastNumber = numbers[numbers.length - 1]; 15 | // or 16 | final lastNumber = numbers.elementAt(numbers.length - 1); 17 | ``` 18 | 19 | ```dart title="Good" 20 | const numbers = [1, 2, 3]; 21 | final lastNumber = numbers.last; 22 | ``` 23 | 24 | ## Usage 25 | 26 | To enable the `prefer_iterable_last` rule, add `prefer_iterable_last` under custom_lint > rules in your `analysis_options.yaml` file: 27 | 28 | ```yaml 29 | custom_lint: 30 | rules: 31 | - prefer_iterable_last 32 | ``` 33 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/avoid_dynamic.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_print 2 | 3 | // expect_lint: avoid_dynamic 4 | const dynamic thing = 'text'; 5 | 6 | // expect_lint: avoid_dynamic 7 | void log(dynamic something) => print(something); 8 | 9 | // expect_lint: avoid_dynamic 10 | typedef MyFunction = void Function(dynamic a, dynamic b); 11 | 12 | // Using dynamic in a map is allowed 13 | const Map map = {}; 14 | 15 | // expect_lint: avoid_dynamic 16 | const List list = [1, 2, 3]; 17 | 18 | // expect_lint: avoid_dynamic 19 | const Set set = {'a', 'b', 'c'}; 20 | 21 | // Using dynamic in a map literal is allowed 22 | final mapLiteral = {}; 23 | 24 | // expect_lint: avoid_dynamic 25 | final listLiteral = [1, 2, 3]; 26 | 27 | // expect_lint: avoid_dynamic 28 | final setLiteral = {'a', 'b', 'c'}; 29 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/specify_icon_button_tooltip/fix/specify_icon_button_tooltip.diff: -------------------------------------------------------------------------------- 1 | Message: `Add tooltip` 2 | Priority: 80 3 | Diff for file `test/lints/flutter/specify_icon_button_tooltip/fix/specify_icon_button_tooltip.dart:7`: 4 | ``` 5 | // expect_lint: specify_icon_button_tooltip 6 | final Widget a = IconButton( 7 | - icon: const Icon(Icons.add), 8 | + tooltip: 'tooltip', 9 | + icon: const Icon(Icons.add), 10 | onPressed: () {}, 11 | ); 12 | ``` 13 | --- 14 | Message: `Add tooltip` 15 | Priority: 80 16 | Diff for file `test/lints/flutter/specify_icon_button_tooltip/fix/specify_icon_button_tooltip.dart:13`: 17 | ``` 18 | // expect_lint: specify_icon_button_tooltip 19 | final Widget b = IconButton.filled( 20 | - icon: const Icon(Icons.add), 21 | + tooltip: 'tooltip', 22 | + icon: const Icon(Icons.add), 23 | onPressed: () {}, 24 | ); 25 | ``` 26 | --- 27 | -------------------------------------------------------------------------------- /docs/dart-lints/avoid_redundant_pattern_field_names.mdx: -------------------------------------------------------------------------------- 1 | # avoid_redundant_pattern_field_names 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** avoid redundant pattern field names when it can be inferred. 10 | 11 | ```dart title="Bad" 12 | void fn(Map map) { 13 | for (final MapEntry(key: key, value: value) in map.entries) {} 14 | } 15 | ``` 16 | 17 | ```dart title="Good" 18 | void fn(Map map) { 19 | for (final MapEntry(:key, :value) in map.entries) {} 20 | } 21 | ``` 22 | 23 | ## Usage 24 | 25 | To enable the `avoid_redundant_pattern_field_names` rule, add `avoid_redundant_pattern_field_names` under custom_lint > rules in your `analysis_options.yaml` file: 26 | 27 | ```yaml 28 | custom_lint: 29 | rules: 30 | - avoid_redundant_pattern_field_names 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/dart-lints/avoid_positional_fields_in_records.mdx: -------------------------------------------------------------------------------- 1 | # avoid_positional_fields_in_records 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ❌ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** avoid using record positional field getters. 10 | 11 | ```dart title="Bad" 12 | void fn((String, String) record) { 13 | final first = record.$1; 14 | final second = record.$2; 15 | } 16 | ``` 17 | 18 | ```dart title="Good" 19 | void fn(({String first, String second}) record) { 20 | final first = record.first; 21 | final second = record.second; 22 | } 23 | ``` 24 | 25 | ## Usage 26 | 27 | To enable the `avoid_positional_fields_in_records` rule, add `avoid_positional_fields_in_records` under custom_lint > rules in your `analysis_options.yaml` file: 28 | 29 | ```yaml 30 | custom_lint: 31 | rules: 32 | - avoid_positional_fields_in_records 33 | ``` 34 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/flutter/wrap_with_listenable_builder/wrap_with_listenable_builder.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class WrapWithListenableBuilderExample extends StatefulWidget { 4 | const WrapWithListenableBuilderExample({super.key}); 5 | 6 | @override 7 | State createState() => _WrapWithListenableBuilderExampleState(); 8 | } 9 | 10 | class _WrapWithListenableBuilderExampleState extends State { 11 | final _focusNode = FocusNode(); 12 | 13 | @override 14 | void dispose() { 15 | _focusNode.dispose(); 16 | super.dispose(); 17 | } 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return Column( 22 | children: [ 23 | Text(_focusNode.hasFocus ? 'Focused' : 'Unfocused'), 24 | TextFormField(focusNode: _focusNode), 25 | ], 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /docs/dart-lints/avoid_inverted_boolean_expressions.mdx: -------------------------------------------------------------------------------- 1 | # avoid_inverted_boolean_expressions 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** avoid using inverted boolean expressions unnecessarily. 10 | 11 | Unnecessary inverted boolean expression should be avoided since it decreases code readability. 12 | 13 | ```dart title="Bad" 14 | if (!(number > 0)) {} 15 | final text = !(number == 0) ? 'Not zero' : 'Zero'; 16 | ``` 17 | 18 | ```dart title="Good" 19 | if (number <= 0) {} 20 | final text = number != 0 ? 'Not zero' : 'Zero'; 21 | ``` 22 | 23 | ## Usage 24 | 25 | To enable the `avoid_inverted_boolean_expressions` rule, add `avoid_inverted_boolean_expressions` under custom_lint > rules in your `analysis_options.yaml` file: 26 | 27 | ```yaml 28 | custom_lint: 29 | rules: 30 | - avoid_inverted_boolean_expressions 31 | ``` 32 | -------------------------------------------------------------------------------- /docs/dart-lints/always_put_doc_comments_before_annotations.mdx: -------------------------------------------------------------------------------- 1 | # always_put_doc_comments_before_annotations 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** place doc comments before metadata annotations. 10 | 11 | see: [effective-dart](https://dart.dev/effective-dart/documentation#do-put-doc-comments-before-metadata-annotations) 12 | 13 | ```dart title="Bad" 14 | @immutable 15 | /// Some documentation. 16 | class A {} 17 | ``` 18 | 19 | ```dart title="Good" 20 | /// Some documentation. 21 | @immutable 22 | class A {} 23 | ``` 24 | 25 | ## Usage 26 | 27 | To enable the `always_put_doc_comments_before_annotations` rule, add `always_put_doc_comments_before_annotations` under custom_lint > rules in your `analysis_options.yaml` file: 28 | 29 | ```yaml 30 | custom_lint: 31 | rules: 32 | - always_put_doc_comments_before_annotations 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/dart-lints/max_lines_for_file.mdx: -------------------------------------------------------------------------------- 1 | # max_lines_for_file 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ❌ | ✅ | 6 | 7 | ## Details 8 | 9 | **DO** limit the number of lines in a file. 10 | 11 | ## Usage 12 | 13 | To enable the `max_lines_for_file` rule, add `max_lines_for_file` under custom_lint > rules in your `analysis_options.yaml` file: 14 | 15 | ```yaml 16 | custom_lint: 17 | rules: 18 | - max_lines_for_file 19 | ``` 20 | 21 | ## Options 22 | 23 | | Option | Type | Description | Default Value | 24 | | ----------- | ----- | ---------------------------- |:-------------:| 25 | | `max_lines` | `int` | The maximum number of lines. | `200` | 26 | 27 | To configure the maximum number of lines, add an `int` to the `max_lines` option: 28 | 29 | ```yaml 30 | custom_lint: 31 | rules: 32 | - max_lines_for_file: 33 | max_lines: 500 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/dart-lints/max_switch_cases.mdx: -------------------------------------------------------------------------------- 1 | # max_switch_cases 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Warning | ❌ | ✅ | 6 | 7 | ## Details 8 | 9 | **DO** limit the number of cases in a switch statement or switch expression. 10 | 11 | ## Usage 12 | 13 | To enable the `max_switch_cases` rule, add `max_switch_cases` under `rules` in your `analysis_options.yaml` file: 14 | 15 | ```yaml 16 | custom_lint: 17 | rules: 18 | - max_switch_cases 19 | ``` 20 | 21 | ## Options 22 | 23 | | Option | Type | Description | Default Value | 24 | | ----------- | ----- | ---------------------------- |:-------------:| 25 | | `max_cases` | `int` | The maximum number of cases. | `10` | 26 | 27 | To configure the maximum number of cases, add an `int` to the `max_cases` option: 28 | 29 | ```yaml 30 | custom_lint: 31 | rules: 32 | - max_switch_cases: 33 | max_cases: 5 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/flutter-lints/prefer_text_rich.mdx: -------------------------------------------------------------------------------- 1 | # prefer_text_rich 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **PREFER** using `Text.rich` instead of `RichText`. 10 | 11 | Using `Text.rich` is more efficient than `RichText` because of possible const constructor usage. 12 | 13 | ```dart title="Bad" 14 | RichText( 15 | text: const TextSpan( 16 | text: 'Pyramid', 17 | children: [ 18 | TextSpan(text: 'Lint'), 19 | ], 20 | ), 21 | ) 22 | ``` 23 | 24 | ```dart title="Good" 25 | const Text.rich( 26 | TextSpan( 27 | text: 'Pyramid', 28 | children: [ 29 | TextSpan(text: 'Lint'), 30 | ], 31 | ), 32 | ) 33 | ``` 34 | 35 | ## Usage 36 | 37 | To enable the `prefer_text_rich` rule, add `prefer_text_rich` under custom_lint > rules in your `analysis_options.yaml` file: 38 | 39 | ```yaml 40 | custom_lint: 41 | rules: 42 | - prefer_text_rich 43 | ``` 44 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/avoid_redundant_pattern_field_names/fix/avoid_redundant_pattern_field_names.diff: -------------------------------------------------------------------------------- 1 | Message: `Remove the field name` 2 | Priority: 80 3 | Diff for file `test/lints/dart/avoid_redundant_pattern_field_names/fix/avoid_redundant_pattern_field_names.dart:5`: 4 | ``` 5 | void fn(Map map) { 6 | // expect_lint: avoid_redundant_pattern_field_names 7 | - for (final MapEntry(key: key, value: value) in map.entries) {} 8 | + for (final MapEntry(:key, value: value) in map.entries) {} 9 | } 10 | ``` 11 | --- 12 | Message: `Remove the field name` 13 | Priority: 80 14 | Diff for file `test/lints/dart/avoid_redundant_pattern_field_names/fix/avoid_redundant_pattern_field_names.dart:5`: 15 | ``` 16 | void fn(Map map) { 17 | // expect_lint: avoid_redundant_pattern_field_names 18 | - for (final MapEntry(key: key, value: value) in map.entries) {} 19 | + for (final MapEntry(key: key, :value) in map.entries) {} 20 | } 21 | ``` 22 | --- 23 | -------------------------------------------------------------------------------- /docs/dart-lints/max_lines_for_function.mdx: -------------------------------------------------------------------------------- 1 | # max_lines_for_function 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ❌ | ✅ | 6 | 7 | ## Details 8 | 9 | A function should not exceed a certain number of lines. 10 | 11 | ## Usage 12 | 13 | To enable the `max_lines_for_function` rule, add `max_lines_for_function` under custom_lint > rules in your `analysis_options.yaml` file: 14 | 15 | ```yaml 16 | custom_lint: 17 | rules: 18 | - max_lines_for_function 19 | ``` 20 | 21 | ## Options 22 | 23 | | Option | Type | Description | Default Value | 24 | | ----------- | ----- | ---------------------------- |:-------------:| 25 | | `max_lines` | `int` | The maximum number of lines. | `100` | 26 | 27 | To configure the maximum number of lines, add an `int` to the `max_lines` option: 28 | 29 | ```yaml 30 | custom_lint: 31 | rules: 32 | - max_lines_for_function: 33 | max_lines: 200 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/flutter-lints/prefer_border_radius_all.mdx: -------------------------------------------------------------------------------- 1 | # prefer_border_radius_all 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **PREFER** using `BorderRadius.all` instead of `BorderRadius.circular`. 10 | 11 | Using `BorderRadius.all` is more efficient than `BorderRadius.circular` because of possible const constructor usage. 12 | 13 | ```dart title="Bad" 14 | DecoratedBox( 15 | decoration: BoxDecoration( 16 | borderRadius: BorderRadius.circular(8), 17 | ), 18 | ) 19 | ``` 20 | 21 | ```dart title="Good" 22 | const DecoratedBox( 23 | decoration: BoxDecoration( 24 | borderRadius: BorderRadius.all(Radius.circular(8)), 25 | ), 26 | ) 27 | ``` 28 | 29 | ## Usage 30 | 31 | To enable the `prefer_border_radius_all` rule, add `prefer_border_radius_all` under custom_lint > rules in your `analysis_options.yaml` file: 32 | 33 | ```yaml 34 | custom_lint: 35 | rules: 36 | - prefer_border_radius_all 37 | ``` 38 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/boolean_prefixes.dart: -------------------------------------------------------------------------------- 1 | // expect_lint: boolean_prefixes 2 | const debugMode = true; 3 | 4 | // "at" is a valid prefix since we specified it in the analysis_options.yaml. 5 | const atOrigin = true; 6 | 7 | class Point { 8 | final double x; 9 | final double y; 10 | 11 | const Point(this.x, this.y); 12 | 13 | // expect_lint: boolean_prefixes 14 | bool get origin => x == 0 && y == 0; 15 | 16 | // expect_lint: boolean_prefixes 17 | bool samePoint(covariant Point other) => x == other.x && y == other.y; 18 | } 19 | 20 | class Point3D extends Point { 21 | final double z; 22 | 23 | const Point3D(super.x, super.y, this.z); 24 | 25 | @override 26 | bool samePoint(Point3D other) => super.samePoint(other) && z == other.z; 27 | } 28 | 29 | // expect_lint: boolean_prefixes 30 | bool samePoint(Point a, Point b) => a.x == b.x && a.y == b.y; 31 | 32 | extension PointExtension on Point { 33 | // expect_lint: boolean_prefixes 34 | bool get onXAxis => y == 0; 35 | } 36 | -------------------------------------------------------------------------------- /docs/dart-lints/prefer_const_constructor_declarations.mdx: -------------------------------------------------------------------------------- 1 | # prefer_const_constructor_declarations 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** declare constructors as const when possible. 10 | 11 | ```dart title="Bad" 12 | class Point { 13 | final double x; 14 | final double y; 15 | 16 | Point(this.x, this.y); 17 | 18 | Point.origin() 19 | : x = 0, 20 | y = 0; 21 | } 22 | ``` 23 | 24 | ```dart title="Good" 25 | class Point { 26 | final double x; 27 | final double y; 28 | 29 | const Point(this.x, this.y); 30 | 31 | const Point.origin() 32 | : x = 0, 33 | y = 0; 34 | } 35 | ``` 36 | 37 | ## Usage 38 | 39 | To enable the `prefer_const_constructor_declarations` rule, add `prefer_const_constructor_declarations` under custom_lint > rules in your `analysis_options.yaml` file: 40 | 41 | ```yaml 42 | custom_lint: 43 | rules: 44 | - prefer_const_constructor_declarations 45 | ``` 46 | -------------------------------------------------------------------------------- /docs/dart-lints/always_specify_parameter_names.mdx: -------------------------------------------------------------------------------- 1 | # always_specify_parameter_names 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ❌ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** specify parameter names in function types to enhance code readability and enable IDEs to provide code completion suggestions. 10 | 11 | ```dart title="Bad" 12 | typedef ItemBuilder = Widget? Function(BuildContext, int); 13 | 14 | // IDE's code completion with default parameter names p0, p1, ... 15 | itemBuilder: (p0, p1) {}, 16 | ``` 17 | 18 | ```dart title="Good" 19 | typedef ItemBuilder = Widget? Function(BuildContext context, int index); 20 | 21 | // IDE's code completion with descriptive parameter names 22 | itemBuilder: (context, index) {}, 23 | ``` 24 | 25 | ## Usage 26 | 27 | To enable the `always_specify_parameter_names` rule, add `always_specify_parameter_names` under custom_lint > rules in your `analysis_options.yaml` file: 28 | 29 | ```yaml 30 | custom_lint: 31 | rules: 32 | - always_specify_parameter_names 33 | ``` 34 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/dart/invert_boolean_expression/invert_boolean_expression_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/source/source_range.dart'; 2 | import 'package:pyramid_lint/src/assists/dart/invert_boolean_expression.dart'; 3 | import 'package:test/test.dart'; 4 | 5 | import '../../../golden.dart'; 6 | 7 | void main() { 8 | testGolden( 9 | 'Invert boolean expression', 10 | 'assists/dart/invert_boolean_expression/invert_boolean_expression.diff', 11 | sourcePath: 'test/assists/dart/invert_boolean_expression/invert_boolean_expression.dart', 12 | (result) async { 13 | final assist = InvertBooleanExpression(); 14 | 15 | final changes = [ 16 | // if (number == 0) 17 | // ^^ 18 | ...await assist.testRun(result, const SourceRange(74, 0)), 19 | 20 | // if (number > 0) 21 | // ^^ 22 | ...await assist.testRun(result, const SourceRange(133, 0)), 23 | ]; 24 | 25 | expect(changes, hasLength(2)); 26 | 27 | return changes; 28 | }, 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_iterable_first/fix/prefer_iterable_first.diff: -------------------------------------------------------------------------------- 1 | Message: `Replace with iterable.first` 2 | Priority: 80 3 | Diff for file `test/lints/dart/prefer_iterable_first/fix/prefer_iterable_first.dart:6`: 4 | ``` 5 | 6 | // expect_lint: prefer_iterable_first 7 | - final a = numbers[0]; 8 | + final a = numbers.first; 9 | 10 | // expect_lint: prefer_iterable_first 11 | ``` 12 | --- 13 | Message: `Replace with iterable.first` 14 | Priority: 80 15 | Diff for file `test/lints/dart/prefer_iterable_first/fix/prefer_iterable_first.dart:9`: 16 | ``` 17 | 18 | // expect_lint: prefer_iterable_first 19 | - final b = numbers.elementAt(0); 20 | + final b = numbers.first; 21 | 22 | final listOfLists = [ 23 | ``` 24 | --- 25 | Message: `Replace with iterable.first` 26 | Priority: 80 27 | Diff for file `test/lints/dart/prefer_iterable_first/fix/prefer_iterable_first.dart:19`: 28 | ``` 29 | void fn() { 30 | // expect_lint: prefer_iterable_first 31 | - final value = listOfLists[0][1]; 32 | + final value = listOfLists.first[1]; 33 | } 34 | ``` 35 | --- 36 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/flutter/wrap_with_stack/wrap_with_stack_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/source/source_range.dart'; 2 | import 'package:pubspec_parse/pubspec_parse.dart'; 3 | import 'package:pyramid_lint/src/assists/flutter/wrap_with_stack.dart'; 4 | import 'package:test/test.dart'; 5 | 6 | import '../../../golden.dart'; 7 | 8 | void main() { 9 | testGolden( 10 | 'Wrap with Stack', 11 | 'assists/flutter/wrap_with_stack/wrap_with_stack.diff', 12 | sourcePath: 'test/assists/flutter/wrap_with_stack/wrap_with_stack.dart', 13 | (result) async { 14 | final assist = WrapWithStack(); 15 | final pubspec = Pubspec('test', dependencies: {'flutter': SdkDependency('flutter')}); 16 | 17 | final changes = [ 18 | // Column 19 | ...await assist.testRun(result, const SourceRange(170, 0), pubspec: pubspec), 20 | 21 | // Text 22 | ...await assist.testRun(result, const SourceRange(203, 0), pubspec: pubspec), 23 | ]; 24 | 25 | expect(changes, hasLength(2)); 26 | 27 | return changes; 28 | }, 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /docs/dart-lints/avoid_dynamic.mdx: -------------------------------------------------------------------------------- 1 | # avoid_dynamic 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ❌ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** avoid using the `dynamic` type unnecessarily. 10 | 11 | While the `dynamic` type can be useful in certain scenarios, it sacrifices the benefits of static typing and decreases code readability. 12 | 13 | Using `dynamic` with `Map` will not trigger the lint. 14 | 15 | ```dart title="Bad" 16 | dynamic thing = 'text'; 17 | void log(dynamic something) => print(something); 18 | List list = [1, 2, 3]; 19 | final setLiteral = {'a', 'b', 'c'}; 20 | ``` 21 | 22 | ```dart title="Good" 23 | String thing = 'text'; 24 | void log(String something) => print(something); 25 | List list = [1, 2, 3]; 26 | final setLiteral = {'a', 'b', 'c'}; 27 | ``` 28 | 29 | ## Usage 30 | 31 | To enable the `avoid_dynamic` rule, add `avoid_dynamic` under custom_lint > rules in your `analysis_options.yaml` file: 32 | 33 | ```yaml 34 | custom_lint: 35 | rules: 36 | - avoid_dynamic 37 | ``` 38 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_iterable_last/fix/prefer_iterable_last.diff: -------------------------------------------------------------------------------- 1 | Message: `Replace with iterable.last` 2 | Priority: 80 3 | Diff for file `test/lints/dart/prefer_iterable_last/fix/prefer_iterable_last.dart:6`: 4 | ``` 5 | 6 | // expect_lint: prefer_iterable_last 7 | - final a = numbers[numbers.length - 1]; 8 | + final a = numbers.last; 9 | 10 | // expect_lint: prefer_iterable_last 11 | ``` 12 | --- 13 | Message: `Replace with iterable.last` 14 | Priority: 80 15 | Diff for file `test/lints/dart/prefer_iterable_last/fix/prefer_iterable_last.dart:9`: 16 | ``` 17 | 18 | // expect_lint: prefer_iterable_last 19 | - final b = numbers.elementAt(numbers.length - 1); 20 | + final b = numbers.last; 21 | 22 | final listOfLists = [ 23 | ``` 24 | --- 25 | Message: `Replace with iterable.last` 26 | Priority: 80 27 | Diff for file `test/lints/dart/prefer_iterable_last/fix/prefer_iterable_last.dart:19`: 28 | ``` 29 | void fn() { 30 | // expect_lint: prefer_iterable_last 31 | - final value = listOfLists[listOfLists.length - 1][1]; 32 | + final value = listOfLists.last[1]; 33 | } 34 | ``` 35 | --- 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/1-feature-request.yml: -------------------------------------------------------------------------------- 1 | name: 🚀 Feature Request 2 | description: Suggest a new feature or enhancement 3 | labels: ["enhancement", "needs triage"] 4 | assignees: [charlescyt] 5 | body: 6 | - type: dropdown 7 | attributes: 8 | label: Feature Type 9 | description: Which type of feature are you requesting? 10 | multiple: true 11 | options: 12 | - Lint 13 | - Quick fix 14 | - Assist 15 | - Other 16 | validations: 17 | required: true 18 | 19 | - type: textarea 20 | attributes: 21 | label: Feature Description 22 | description: A clear and concise description of the feature you would like to see. 23 | validations: 24 | required: true 25 | 26 | - type: textarea 27 | attributes: 28 | label: Proposed Solution 29 | description: If you have a specific solution in mind, please describe it here. 30 | 31 | - type: textarea 32 | attributes: 33 | label: Additional Information 34 | description: Add any additional information that could help us understand the requested feature better. 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/2-bug-report.yml: -------------------------------------------------------------------------------- 1 | name: 🐞 Bug Report 2 | description: Report a bug 3 | labels: ["bug", "needs triage"] 4 | assignees: [charlescyt] 5 | body: 6 | - type: checkboxes 7 | attributes: 8 | label: Is there an existing issue for this bug? 9 | description: Please check if there is an existing issue for this bug before creating a new one. 10 | options: 11 | - label: I have searched the [existing issues](https://github.com/charlescyt/pyramid_lint/issues). 12 | required: true 13 | 14 | - type: textarea 15 | attributes: 16 | label: Bug Description 17 | description: A clear and concise description of the bug you have encountered. 18 | validations: 19 | required: true 20 | 21 | - type: textarea 22 | attributes: 23 | label: Reproduction Steps 24 | description: Steps or sample code to reproduce the bug. 25 | validations: 26 | required: true 27 | 28 | - type: textarea 29 | attributes: 30 | label: Additional Information 31 | description: Add any additontal information that could help us understand the bug better. 32 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/always_put_doc_comments_before_annotations/fix/always_put_doc_comments_before_annotations.diff: -------------------------------------------------------------------------------- 1 | Message: `Put doc comment before annotations.` 2 | Priority: 80 3 | Diff for file `test/lints/dart/always_put_doc_comments_before_annotations/fix/always_put_doc_comments_before_annotations.dart:3`: 4 | ``` 5 | import 'package:flutter/foundation.dart' show immutable; 6 | 7 | - @immutable 8 | - // expect_lint: always_put_doc_comments_before_annotations 9 | - /// Some documentation. 10 | + /// Some documentation. 11 | + @immutable 12 | class A {} 13 | 14 | ``` 15 | --- 16 | Message: `Put doc comment before annotations.` 17 | Priority: 80 18 | Diff for file `test/lints/dart/always_put_doc_comments_before_annotations/fix/always_put_doc_comments_before_annotations.dart:8`: 19 | ``` 20 | class A {} 21 | 22 | - @immutable 23 | - // expect_lint: always_put_doc_comments_before_annotations 24 | - /// Some documentation. 25 | - /// 26 | - /// More documentation. 27 | + /// Some documentation. 28 | + /// 29 | + /// More documentation. 30 | + @immutable 31 | @Deprecated('Use A instead') 32 | class B {} 33 | ``` 34 | --- 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Charles Tsang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_library_prefixes/fix/prefer_library_prefixes.diff: -------------------------------------------------------------------------------- 1 | Message: `Add prefix to the library` 2 | Priority: 80 3 | Diff for file `test/lints/dart/prefer_library_prefixes/fix/prefer_library_prefixes.dart:4`: 4 | ``` 5 | 6 | // expect_lint: prefer_library_prefixes 7 | - import 'dart:developer'; 8 | + import 'dart:developer' as developer; 9 | // expect_lint: prefer_library_prefixes 10 | import 'dart:io'; 11 | ``` 12 | --- 13 | Message: `Add prefix to the library` 14 | Priority: 80 15 | Diff for file `test/lints/dart/prefer_library_prefixes/fix/prefer_library_prefixes.dart:6`: 16 | ``` 17 | import 'dart:developer'; 18 | // expect_lint: prefer_library_prefixes 19 | - import 'dart:io'; 20 | + import 'dart:io' as io; 21 | // expect_lint: prefer_library_prefixes 22 | import 'dart:math'; 23 | ``` 24 | --- 25 | Message: `Add prefix to the library` 26 | Priority: 80 27 | Diff for file `test/lints/dart/prefer_library_prefixes/fix/prefer_library_prefixes.dart:8`: 28 | ``` 29 | import 'dart:io'; 30 | // expect_lint: prefer_library_prefixes 31 | - import 'dart:math'; 32 | + import 'dart:math' as math; 33 | ``` 34 | --- 35 | -------------------------------------------------------------------------------- /docs/flutter-lints/prefer_border_from_border_side.mdx: -------------------------------------------------------------------------------- 1 | # prefer_border_from_border_side 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **PREFER** using `Border.fromBorderSide` instead of `Border.all`. 10 | 11 | Using `Border.fromBorderSide` is more efficient than `Border.all` because of possible const constructor usage. 12 | 13 | ```dart title="Bad" 14 | DecoratedBox( 15 | decoration: BoxDecoration( 16 | border: Border.all( 17 | width: 1, 18 | style: BorderStyle.solid, 19 | ), 20 | ), 21 | ) 22 | ``` 23 | 24 | ```dart title="Good" 25 | const DecoratedBox( 26 | decoration: BoxDecoration( 27 | border: Border.fromBorderSide( 28 | BorderSide( 29 | width: 1, 30 | style: BorderStyle.solid, 31 | ), 32 | ), 33 | ), 34 | ) 35 | ``` 36 | 37 | ## Usage 38 | 39 | To enable the `prefer_border_from_border_side` rule, add `prefer_border_from_border_side` under custom_lint > rules in your `analysis_options.yaml` file: 40 | 41 | ```yaml 42 | custom_lint: 43 | rules: 44 | - prefer_border_from_border_side 45 | ``` 46 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_iterable_any/fix/prefer_iterable_any_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pyramid_lint/src/lints/dart/prefer_iterable_any.dart'; 4 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | import '../../../../golden.dart'; 8 | 9 | void main() { 10 | testGolden( 11 | 'Test for prefer_iterable_any fix', 12 | 'lints/dart/prefer_iterable_any/fix/prefer_iterable_any.diff', 13 | sourcePath: 'test/lints/dart/prefer_iterable_any/fix/prefer_iterable_any.dart', 14 | (result) async { 15 | const options = PyramidLintRuleOptions(params: null); 16 | final lint = PreferIterableAny(options: options); 17 | final fix = lint.getFixes().single as DartFix; 18 | 19 | final errors = await lint.testRun(result); 20 | expect(errors, hasLength(11)); 21 | 22 | final changes = await Future.wait([ 23 | for (final error in errors) fix.testRun(result, error, errors), 24 | ]); 25 | 26 | return changes.flattened; 27 | }, 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_iterable_last/fix/prefer_iterable_last_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pyramid_lint/src/lints/dart/prefer_iterable_last.dart'; 4 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | import '../../../../golden.dart'; 8 | 9 | void main() { 10 | testGolden( 11 | 'Test for prefer_iterable_last fix', 12 | 'lints/dart/prefer_iterable_last/fix/prefer_iterable_last.diff', 13 | sourcePath: 'test/lints/dart/prefer_iterable_last/fix/prefer_iterable_last.dart', 14 | (result) async { 15 | const options = PyramidLintRuleOptions(params: null); 16 | final lint = PreferIterableLast(options: options); 17 | final fix = lint.getFixes().single as DartFix; 18 | 19 | final errors = await lint.testRun(result); 20 | expect(errors, hasLength(3)); 21 | 22 | final changes = await Future.wait([ 23 | for (final error in errors) fix.testRun(result, error, errors), 24 | ]); 25 | 26 | return changes.flattened; 27 | }, 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/avoid_widget_state_public_members.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: unused_element, unused_field, avoid_multiple_declarations_per_line, avoid_empty_blocks, prefer_final_fields 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class Bar extends StatefulWidget { 6 | const Bar({super.key}); 7 | 8 | @override 9 | State createState() => _BarState(); 10 | } 11 | 12 | class _BarState extends State { 13 | // expect_lint: avoid_public_members_in_states 14 | int publicField = 1; 15 | 16 | @visibleForTesting 17 | int publicFieldWithVisibleForTestingAnnotation = 1; 18 | 19 | int _privateField = 0; 20 | 21 | // expect_lint: avoid_public_members_in_states 22 | String _privateField2 = '', publicField2 = ''; 23 | 24 | @override 25 | void initState() { 26 | super.initState(); 27 | } 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return const Placeholder(); 32 | } 33 | 34 | // expect_lint: avoid_public_members_in_states 35 | void publicMethod() {} 36 | 37 | @visibleForTesting 38 | void publicMethodWithVisibleForTestingAnnotation() {} 39 | 40 | void _privateMethod() {} 41 | } 42 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_iterable_every/fix/prefer_iterable_every_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pyramid_lint/src/lints/dart/prefer_iterable_every.dart'; 4 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | import '../../../../golden.dart'; 8 | 9 | void main() { 10 | testGolden( 11 | 'Test for prefer_iterable_every fix', 12 | 'lints/dart/prefer_iterable_every/fix/prefer_iterable_every.diff', 13 | sourcePath: 'test/lints/dart/prefer_iterable_every/fix/prefer_iterable_every.dart', 14 | (result) async { 15 | const options = PyramidLintRuleOptions(params: null); 16 | final lint = PreferIterableEvery(options: options); 17 | final fix = lint.getFixes().single as DartFix; 18 | 19 | final errors = await lint.testRun(result); 20 | expect(errors, hasLength(13)); 21 | 22 | final changes = await Future.wait([ 23 | for (final error in errors) fix.testRun(result, error, errors), 24 | ]); 25 | 26 | return changes.flattened; 27 | }, 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_iterable_first/fix/prefer_iterable_first_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pyramid_lint/src/lints/dart/prefer_iterable_first.dart'; 4 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | import '../../../../golden.dart'; 8 | 9 | void main() { 10 | testGolden( 11 | 'Test for prefer_iterable_first fix', 12 | 'lints/dart/prefer_iterable_first/fix/prefer_iterable_first.diff', 13 | sourcePath: 'test/lints/dart/prefer_iterable_first/fix/prefer_iterable_first.dart', 14 | (result) async { 15 | const options = PyramidLintRuleOptions(params: null); 16 | final lint = PreferIterableFirst(options: options); 17 | final fix = lint.getFixes().single as DartFix; 18 | 19 | final errors = await lint.testRun(result); 20 | expect(errors, hasLength(3)); 21 | 22 | final changes = await Future.wait([ 23 | for (final error in errors) fix.testRun(result, error, errors), 24 | ]); 25 | 26 | return changes.flattened; 27 | }, 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /docs/dart-lints/prefer_underscore_for_unused_callback_parameters.mdx: -------------------------------------------------------------------------------- 1 | # prefer_underscore_for_unused_callback_parameters 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ❌ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** use `_` for unused callback parameters. 10 | 11 | Since Dart 3.7.0, local parameters named `_` can be declared multiple times without collisions. 12 | 13 | see: [effective-dart](https://dart.dev/effective-dart/style#prefer-using-_-__-etc-for-unused-callback-parameters) and 14 | [wildcard variables](https://dart.dev/language/variables#wildcard-variables) 15 | 16 | ```dart title="Bad" 17 | itemBuilder: (context, index) { 18 | return Text('Item $index'); 19 | } 20 | ``` 21 | 22 | ```dart title="Good" 23 | itemBuilder: (_, index) { 24 | return Text('Item $index'); 25 | } 26 | ``` 27 | 28 | ## Usage 29 | 30 | To enable the `prefer_underscore_for_unused_callback_parameters` rule, add `prefer_underscore_for_unused_callback_parameters` under custom_lint > rules in your `analysis_options.yaml` file: 31 | 32 | ```yaml 33 | custom_lint: 34 | rules: 35 | - prefer_underscore_for_unused_callback_parameters 36 | ``` 37 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_const_constructor_declarations/fix/prefer_const_constructor_declarations.diff: -------------------------------------------------------------------------------- 1 | Message: `Add const keyword` 2 | Priority: 80 3 | Diff for file `test/lints/dart/prefer_const_constructor_declarations/fix/prefer_const_constructor_declarations.dart:6`: 4 | ``` 5 | 6 | // expect_lint: prefer_const_constructor_declarations 7 | - Point(this.x, this.y); 8 | + const Point(this.x, this.y); 9 | 10 | // expect_lint: prefer_const_constructor_declarations 11 | ``` 12 | --- 13 | Message: `Add const keyword` 14 | Priority: 80 15 | Diff for file `test/lints/dart/prefer_const_constructor_declarations/fix/prefer_const_constructor_declarations.dart:9`: 16 | ``` 17 | 18 | // expect_lint: prefer_const_constructor_declarations 19 | - Point.origin() : x = 0.0, y = 0.0; 20 | + const Point.origin() : x = 0.0, y = 0.0; 21 | } 22 | 23 | ``` 24 | --- 25 | Message: `Add const keyword` 26 | Priority: 80 27 | Diff for file `test/lints/dart/prefer_const_constructor_declarations/fix/prefer_const_constructor_declarations.dart:14`: 28 | ``` 29 | class A { 30 | // expect_lint: prefer_const_constructor_declarations 31 | - A(); 32 | + const A(); 33 | } 34 | ``` 35 | --- 36 | -------------------------------------------------------------------------------- /docs/flutter-lints/proper_from_environment.mdx: -------------------------------------------------------------------------------- 1 | # proper_from_environment 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Error | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** use `const` when invoking `bool.fromEnvironment`, `int.fromEnvironment` and `String.fromEnvironment`. 10 | 11 | The constructors `bool.fromEnvironment`, `int.fromEnvironment` and `String.fromEnvironment` are only guaranteed to work when invoked as a `const` constructor. 12 | 13 | see: [dart-lang-issue](https://github.com/dart-lang/sdk/issues/42177) 14 | 15 | ```dart title="Bad" 16 | final boolean = bool.fromEnvironment('bool'); 17 | int integer = int.fromEnvironment('int'); 18 | var string = String.fromEnvironment('String'); 19 | ``` 20 | 21 | ```dart title="Good" 22 | const boolean = bool.fromEnvironment('bool'); 23 | const integer = int.fromEnvironment('int'); 24 | const string = String.fromEnvironment('String'); 25 | ``` 26 | 27 | ## Usage 28 | 29 | To enable the `proper_from_environment` rule, add `proper_from_environment` under custom_lint > rules in your `analysis_options.yaml` file: 30 | 31 | ```yaml 32 | custom_lint: 33 | rules: 34 | - proper_from_environment 35 | ``` 36 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_immediate_return/fix/prefer_immediate_return_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pyramid_lint/src/lints/dart/prefer_immediate_return.dart'; 4 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | import '../../../../golden.dart'; 8 | 9 | void main() { 10 | testGolden( 11 | 'Test for prefer_immediate_return fix', 12 | 'lints/dart/prefer_immediate_return/fix/prefer_immediate_return.diff', 13 | sourcePath: 'test/lints/dart/prefer_immediate_return/fix/prefer_immediate_return.dart', 14 | (result) async { 15 | const options = PyramidLintRuleOptions(params: null); 16 | final lint = PreferImmediateReturn(options: options); 17 | final fix = lint.getFixes().single as DartFix; 18 | 19 | final errors = await lint.testRun(result); 20 | expect(errors, hasLength(1)); 21 | 22 | final changes = await Future.wait([ 23 | for (final error in errors) fix.testRun(result, error, errors), 24 | ]); 25 | 26 | return changes.flattened; 27 | }, 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /packages/pyramid_lint/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: pyramid_lint 2 | description: >- 3 | Linting tool for Dart and Flutter projects to encourage good coding practices. 4 | version: 2.4.0 5 | repository: https://github.com/charlescyt/pyramid_lint 6 | issue_tracker: https://github.com/charlescyt/pyramid_lint/issues 7 | documentation: https://docs.page/charlescyt/pyramid_lint 8 | topics: [lint, lints, linter, analyzer] 9 | 10 | screenshots: 11 | - description: >- 12 | Demonstration of the proper_super_init_state lint and its quick fix. 13 | path: screenshots/proper_super_init_state_demo.gif 14 | - description: >- 15 | Demonstration of the prefer_dedicated_media_query_method_demo lint and its quick fix. 16 | path: screenshots/prefer_dedicated_media_query_method_demo.gif 17 | - description: >- 18 | Demonstration of the wrap_with_layout_builder assist. 19 | path: screenshots/wrap_with_layout_builder_demo.gif 20 | 21 | environment: 22 | sdk: ">=3.10.0 <4.0.0" 23 | 24 | dependencies: 25 | analyzer: ^8.0.0 26 | analyzer_plugin: ^0.13.5 27 | collection: ^1.19.0 28 | custom_lint_builder: ^0.8.1 29 | meta: ^1.16.0 30 | pub_semver: ^2.1.4 31 | pubspec_parse: ^1.5.0 32 | yaml: ^3.1.2 33 | -------------------------------------------------------------------------------- /docs/dart-lints/prefer_async_await.mdx: -------------------------------------------------------------------------------- 1 | # prefer_async_await 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ❌ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** use async/await instead of using raw futures. 10 | 11 | Using async/await improves code readability. 12 | 13 | see: [effective-dart](https://dart.dev/effective-dart/usage#prefer-asyncawait-over-using-raw-futures) 14 | 15 | ```dart title="Bad" 16 | void fetchData() { 17 | performAsyncOperation().then((result) { 18 | print(result); 19 | }).catchError((Object? error) { 20 | print('Error: $error'); 21 | }).whenComplete(() { 22 | print('Done'); 23 | }); 24 | } 25 | ``` 26 | 27 | ```dart title="Good" 28 | Future fetchData() async { 29 | try { 30 | final result = await performAsyncOperation(); 31 | print(result); 32 | } catch (error) { 33 | print('Error: $error'); 34 | } finally { 35 | print('Done'); 36 | } 37 | } 38 | ``` 39 | 40 | ## Usage 41 | 42 | To enable the `prefer_async_await` rule, add `prefer_async_await` under custom_lint > rules in your `analysis_options.yaml` file: 43 | 44 | ```yaml 45 | custom_lint: 46 | rules: 47 | - prefer_async_await 48 | ``` 49 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/dart/swap_then_else_expression/swap_then_else_expression_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/source/source_range.dart'; 2 | import 'package:pyramid_lint/src/assists/dart/swap_then_else_expression.dart'; 3 | import 'package:test/test.dart'; 4 | 5 | import '../../../golden.dart'; 6 | 7 | void main() { 8 | testGolden( 9 | 'Swap then else expression', 10 | 'assists/dart/swap_then_else_expression/swap_then_else_expression.diff', 11 | sourcePath: 'test/assists/dart/swap_then_else_expression/swap_then_else_expression.dart', 12 | (result) async { 13 | final assist = SwapThenElseExpression(); 14 | 15 | final changes = [ 16 | // if (condition) 17 | // ^^ 18 | ...await assist.testRun(result, const SourceRange(98, 0)), 19 | 20 | // print('then'); 21 | // ^^ 22 | ...await assist.testRun(result, const SourceRange(113, 0)), 23 | 24 | // final text = condition ? 'then' : 'else'; 25 | // ^^ 26 | ...await assist.testRun(result, const SourceRange(190, 0)), 27 | ]; 28 | 29 | expect(changes, hasLength(2)); 30 | 31 | return changes; 32 | }, 33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/flutter/wrap_all_children_with_expanded/wrap_all_children_with_expanded_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/source/source_range.dart'; 2 | import 'package:pubspec_parse/pubspec_parse.dart'; 3 | import 'package:pyramid_lint/src/assists/flutter/wrap_all_children_with_expanded.dart'; 4 | import 'package:test/test.dart'; 5 | 6 | import '../../../golden.dart'; 7 | 8 | void main() { 9 | testGolden( 10 | 'Wrap all children with Expanded', 11 | 'assists/flutter/wrap_all_children_with_expanded/wrap_all_children_with_expanded.diff', 12 | sourcePath: 'test/assists/flutter/wrap_all_children_with_expanded/wrap_all_children_with_expanded.dart', 13 | (result) async { 14 | final assist = WrapAllChildrenWithExpanded(); 15 | final pubspec = Pubspec('test', dependencies: {'flutter': SdkDependency('flutter')}); 16 | 17 | final changes = [ 18 | // Column 19 | ...await assist.testRun(result, const SourceRange(169, 0), pubspec: pubspec), 20 | 21 | // Row 22 | ...await assist.testRun(result, const SourceRange(202, 0), pubspec: pubspec), 23 | ]; 24 | 25 | expect(changes, hasLength(2)); 26 | 27 | return changes; 28 | }, 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /docs/dart-lints/avoid_nested_if.mdx: -------------------------------------------------------------------------------- 1 | # avoid_nested_if 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Warning | ❌ | ✅ | 6 | 7 | ## Details 8 | 9 | **DO** avoid too many nested `if` statements. 10 | 11 | ```dart title="Bad" 12 | if (x) { 13 | if (y) { 14 | if (z) { 15 | doSomething(); 16 | } 17 | } 18 | } 19 | ``` 20 | 21 | ```dart title="Good" 22 | if (!x) return; 23 | if (!y) return; 24 | if (!z) return; 25 | doSomething(); 26 | ``` 27 | 28 | ## Usage 29 | 30 | To enable the `avoid_nested_if` rule, add `avoid_nested_if` under custom_lint > rules in your `analysis_options.yaml` file: 31 | 32 | ```yaml 33 | custom_lint: 34 | rules: 35 | - avoid_nested_if 36 | ``` 37 | 38 | ## Options 39 | 40 | | Option | Type | Description | Default Value | 41 | | ------------------- | ----- | ---------------------------------- |:-------------:| 42 | | `max_nesting_level` | `int` | The maximum allowed nesting level. | `2` | 43 | 44 | To configure the max nesting level, add an integer to the `max_nesting_level` option: 45 | 46 | ```yaml 47 | custom_lint: 48 | rules: 49 | - avoid_nested_if: 50 | max_nesting_level: 3 51 | ``` 52 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_new_line_before_return/fix/prefer_new_line_before_return_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pyramid_lint/src/lints/dart/prefer_new_line_before_return.dart'; 4 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | import '../../../../golden.dart'; 8 | 9 | void main() { 10 | testGolden( 11 | 'Test for prefer_new_line_before_return fix', 12 | 'lints/dart/prefer_new_line_before_return/fix/prefer_new_line_before_return.diff', 13 | sourcePath: 'test/lints/dart/prefer_new_line_before_return/fix/prefer_new_line_before_return.dart', 14 | (result) async { 15 | const options = PyramidLintRuleOptions(params: null); 16 | final lint = PreferNewLineBeforeReturn(options: options); 17 | final fix = lint.getFixes().single as DartFix; 18 | 19 | final errors = await lint.testRun(result); 20 | expect(errors, hasLength(1)); 21 | 22 | final changes = await Future.wait([ 23 | for (final error in errors) fix.testRun(result, error, errors), 24 | ]); 25 | 26 | return changes.flattened; 27 | }, 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /docs/index.mdx: -------------------------------------------------------------------------------- 1 |

2 | logo 3 |

4 | 5 |

6 | Linting tool for Dart and Flutter projects. 7 |

8 | 9 | # What is Pyramid Lint? 10 | 11 | Pyramid Lint is a linting tool for Dart and Flutter projects. It provides a set of additional lints and quick fixes to help developers identify issues within their Dart code, offers suggestions for potential fixes, and enforces consistent coding styles. 12 | 13 | Pyramid Lint is built with [custom_lint][custom_lint]. 14 | 15 | ## Lints 16 | 17 | Identifies potential issues in your code. 18 | 19 | ## Quick fixes 20 | 21 | Suggests potential fixes for the issues identified by lints. 22 | 23 | ![proper_super_init_state](https://raw.githubusercontent.com/charlescyt/pyramid_lint/main/docs/assets/proper_super_init_state.gif) 24 | 25 | ## Assist 26 | 27 | Provides refactoring and code completion for common tasks. 28 | 29 | ![wrap_with_layout_builder](https://raw.githubusercontent.com/charlescyt/pyramid_lint/main/docs/assets/wrap_with_layout_builder.gif) 30 | 31 | 32 | 33 | [custom_lint]: https://pub.dev/packages/custom_lint 34 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/use_spacer/fix/use_spacer_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pubspec_parse/pubspec_parse.dart'; 4 | import 'package:pyramid_lint/src/lints/flutter/use_spacer.dart'; 5 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import '../../../../golden.dart'; 9 | 10 | void main() { 11 | testGolden( 12 | 'Test for use_spacer fix', 13 | 'lints/flutter/use_spacer/fix/use_spacer.diff', 14 | sourcePath: 'test/lints/flutter/use_spacer/fix/use_spacer.dart', 15 | (result) async { 16 | const options = PyramidLintRuleOptions(params: null); 17 | final lint = UseSpacer(options: options); 18 | final fix = lint.getFixes().single as DartFix; 19 | final pubspec = Pubspec('test', dependencies: {'flutter': SdkDependency('flutter')}); 20 | 21 | final errors = await lint.testRun(result, pubspec: pubspec); 22 | expect(errors, hasLength(2)); 23 | 24 | final changes = await Future.wait([ 25 | for (final error in errors) fix.testRun(result, error, errors), 26 | ]); 27 | 28 | return changes.flattened; 29 | }, 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/unnecessary_nullable_return_type/fix/unnecessary_nullable_return_type_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pyramid_lint/src/lints/dart/unnecessary_nullable_return_type.dart'; 4 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | import '../../../../golden.dart'; 8 | 9 | void main() { 10 | testGolden( 11 | 'Test for unnecessary_nullable_return_type fix', 12 | 'lints/dart/unnecessary_nullable_return_type/fix/unnecessary_nullable_return_type.diff', 13 | sourcePath: 'test/lints/dart/unnecessary_nullable_return_type/fix/unnecessary_nullable_return_type.dart', 14 | (result) async { 15 | const options = PyramidLintRuleOptions(params: null); 16 | final lint = UnnecessaryNullableReturnType(options: options); 17 | final fix = lint.getFixes().single as DartFix; 18 | 19 | final errors = await lint.testRun(result); 20 | expect(errors, hasLength(2)); 21 | 22 | final changes = await Future.wait([ 23 | for (final error in errors) fix.testRun(result, error, errors), 24 | ]); 25 | 26 | return changes.flattened; 27 | }, 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/avoid_inverted_boolean_expressions/fix/avoid_inverted_boolean_expressions_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pyramid_lint/src/lints/dart/avoid_inverted_boolean_expressions.dart'; 4 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | import '../../../../golden.dart'; 8 | 9 | void main() { 10 | testGolden( 11 | 'Test for avoid_inverted_boolean_expressions fix', 12 | 'lints/dart/avoid_inverted_boolean_expressions/fix/avoid_inverted_boolean_expressions.diff', 13 | sourcePath: 'test/lints/dart/avoid_inverted_boolean_expressions/fix/avoid_inverted_boolean_expressions.dart', 14 | (result) async { 15 | const options = PyramidLintRuleOptions(params: null); 16 | final lint = AvoidInvertedBooleanExpressions(options: options); 17 | final fix = lint.getFixes().single as DartFix; 18 | 19 | final errors = await lint.testRun(result); 20 | expect(errors, hasLength(3)); 21 | 22 | final changes = await Future.wait([ 23 | for (final error in errors) fix.testRun(result, error, errors), 24 | ]); 25 | 26 | return changes.flattened; 27 | }, 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/max_switch_cases.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_print 2 | 3 | enum Direction { 4 | north, 5 | northEast, 6 | east, 7 | southEast, 8 | south, 9 | southWest, 10 | west, 11 | northWest, 12 | } 13 | 14 | void fn(Direction direction) { 15 | // expect_lint: max_switch_cases 16 | switch (direction) { 17 | case Direction.north: 18 | print('north'); 19 | case Direction.northEast: 20 | print('northEast'); 21 | case Direction.east: 22 | print('east'); 23 | case Direction.southEast: 24 | print('southEast'); 25 | case Direction.south: 26 | print('south'); 27 | case Direction.southWest: 28 | print('southWest'); 29 | case Direction.west: 30 | print('west'); 31 | case Direction.northWest: 32 | print('northWest'); 33 | } 34 | } 35 | 36 | String fn2(Direction direction) { 37 | // expect_lint: max_switch_cases 38 | return switch (direction) { 39 | Direction.north => 'north', 40 | Direction.northEast => 'northEast', 41 | Direction.east => 'east', 42 | Direction.southEast => 'southEast', 43 | Direction.south => 'south', 44 | Direction.southWest => 'southWest', 45 | Direction.west => 'west', 46 | Direction.northWest => 'northWest', 47 | }; 48 | } 49 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/flutter/wrap_with_layout_builder/wrap_with_layout_builder_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/source/source_range.dart'; 2 | import 'package:pubspec_parse/pubspec_parse.dart'; 3 | import 'package:pyramid_lint/src/assists/flutter/wrap_with_layout_builder.dart'; 4 | import 'package:test/test.dart'; 5 | 6 | import '../../../golden.dart'; 7 | 8 | void main() { 9 | testGolden( 10 | 'Wrap with LayoutBuilder', 11 | 'assists/flutter/wrap_with_layout_builder/wrap_with_layout_builder.diff', 12 | sourcePath: 'test/assists/flutter/wrap_with_layout_builder/wrap_with_layout_builder.dart', 13 | (result) async { 14 | final assist = WrapWithLayoutBuilder(); 15 | final pubspec = Pubspec('test', dependencies: {'flutter': SdkDependency('flutter')}); 16 | 17 | final changes = [ 18 | // LayoutBuilder 19 | ...await assist.testRun(result, const SourceRange(166, 0), pubspec: pubspec), 20 | 21 | // Center 22 | ...await assist.testRun(result, const SourceRange(225, 0), pubspec: pubspec), 23 | 24 | // Placeholder 25 | ...await assist.testRun(result, const SourceRange(252, 0), pubspec: pubspec), 26 | ]; 27 | 28 | expect(changes, hasLength(1)); 29 | 30 | return changes; 31 | }, 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/avoid_redundant_pattern_field_names/fix/avoid_redundant_pattern_field_names_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pyramid_lint/src/lints/dart/avoid_redundant_pattern_field_names.dart'; 4 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | import '../../../../golden.dart'; 8 | 9 | void main() { 10 | testGolden( 11 | 'Test for avoid_redundant_pattern_field_names fix', 12 | 'lints/dart/avoid_redundant_pattern_field_names/fix/avoid_redundant_pattern_field_names.diff', 13 | sourcePath: 'test/lints/dart/avoid_redundant_pattern_field_names/fix/avoid_redundant_pattern_field_names.dart', 14 | (result) async { 15 | const options = PyramidLintRuleOptions(params: null); 16 | final lint = AvoidRedundantPatternFieldNames(options: options); 17 | final fix = lint.getFixes().single as DartFix; 18 | 19 | final errors = await lint.testRun(result); 20 | expect(errors, hasLength(2)); 21 | 22 | final changes = await Future.wait([ 23 | for (final error in errors) fix.testRun(result, error, errors), 24 | ]); 25 | 26 | return changes.flattened; 27 | }, 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /docs/dart-lints/avoid_unused_parameters.mdx: -------------------------------------------------------------------------------- 1 | # avoid_unused_parameters 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Warning | ❌ | ✅ | 6 | 7 | ## Details 8 | 9 | **DO** avoid unused parameters. 10 | 11 | Unused parameters should be removed to avoid confusion. 12 | 13 | ```dart title="Bad" 14 | void log(String a, String b) { 15 | print(a); 16 | } 17 | ``` 18 | 19 | ```dart title="Good" 20 | void log(String a) { 21 | print(a); 22 | } 23 | ``` 24 | 25 | ## Usage 26 | 27 | To enable the `avoid_unused_parameters` rule, add `avoid_unused_parameters` under custom_lint > rules in your `analysis_options.yaml` file: 28 | 29 | ```yaml 30 | custom_lint: 31 | rules: 32 | - avoid_unused_parameters 33 | ``` 34 | 35 | ## Options 36 | 37 | | Option | Type | Description | Default Value | 38 | | -------------------- | -------------- | ------------------------------- |:-------------:| 39 | | `ignored_parameters` | `List` | A list of parameters to ignore. | `[]` | 40 | 41 | To configure the list of ignored parameters, add a list of strings to the `ignored_parameters` option: 42 | 43 | ```yaml 44 | custom_lint: 45 | rules: 46 | - avoid_unused_parameters: 47 | ignored_parameters: ['ref'] 48 | ``` 49 | -------------------------------------------------------------------------------- /packages/pyramid_lint/lib/src/assists/flutter/wrap_with_stack.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/source/source_range.dart'; 2 | import 'package:custom_lint_builder/custom_lint_builder.dart'; 3 | 4 | import '../../utils/ast_node_extensions.dart'; 5 | import '../../utils/pubspec_extension.dart'; 6 | import '../../utils/type_checker.dart'; 7 | 8 | class WrapWithStack extends DartAssist { 9 | @override 10 | Future run( 11 | CustomLintResolver resolver, 12 | ChangeReporter reporter, 13 | CustomLintContext context, 14 | SourceRange target, 15 | ) async { 16 | if (!context.pubspec.isFlutterProject) return; 17 | 18 | context.registry.addInstanceCreationExpression((node) { 19 | final sourceRange = node.keywordAndConstructorNameSourceRange; 20 | if (!sourceRange.covers(target)) return; 21 | 22 | final type = node.staticType; 23 | if (type == null || !widgetChecker.isSuperTypeOf(type)) return; 24 | 25 | final changeBuilder = reporter.createChangeBuilder( 26 | message: 'Wrap with Stack', 27 | priority: 28, 28 | ); 29 | 30 | changeBuilder.addDartFileEdit((builder) { 31 | builder.addSimpleInsertion(node.offset, 'Stack(children: ['); 32 | builder.addSimpleInsertion(node.end, ',],)'); 33 | }); 34 | }); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_library_prefixes/fix/prefer_library_prefixes_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pyramid_lint/src/lints/dart/prefer_library_prefixes.dart'; 4 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | import '../../../../golden.dart'; 8 | 9 | void main() { 10 | testGolden( 11 | 'Test for prefer_library_prefixes fix', 12 | 'lints/dart/prefer_library_prefixes/fix/prefer_library_prefixes.diff', 13 | sourcePath: 'test/lints/dart/prefer_library_prefixes/fix/prefer_library_prefixes.dart', 14 | (result) async { 15 | const params = PreferLibraryPrefixesOptions(includeDefaultLibraries: true, libraries: ['dart:io']); 16 | const options = PyramidLintRuleOptions(params: params); 17 | final lint = PreferLibraryPrefixes(options: options); 18 | final fix = lint.getFixes().single as DartFix; 19 | 20 | final errors = await lint.testRun(result); 21 | expect(errors, hasLength(3)); 22 | 23 | final changes = await Future.wait([ 24 | for (final error in errors) fix.testRun(result, error, errors), 25 | ]); 26 | 27 | return changes.flattened; 28 | }, 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/avoid_inverted_boolean_expressions/fix/avoid_inverted_boolean_expressions.diff: -------------------------------------------------------------------------------- 1 | Message: `Replace with positive boolean expression` 2 | Priority: 80 3 | Diff for file `test/lints/dart/avoid_inverted_boolean_expressions/fix/avoid_inverted_boolean_expressions.dart:5`: 4 | ``` 5 | void example(int number) { 6 | // expect_lint: avoid_inverted_boolean_expressions 7 | - if (!(number == 0)) {} 8 | + if (number != 0) {} 9 | 10 | // expect_lint: avoid_inverted_boolean_expressions 11 | ``` 12 | --- 13 | Message: `Replace with positive boolean expression` 14 | Priority: 80 15 | Diff for file `test/lints/dart/avoid_inverted_boolean_expressions/fix/avoid_inverted_boolean_expressions.dart:8`: 16 | ``` 17 | 18 | // expect_lint: avoid_inverted_boolean_expressions 19 | - if (!(number > 0)) {} 20 | + if (number <= 0) {} 21 | 22 | // expect_lint: avoid_inverted_boolean_expressions 23 | ``` 24 | --- 25 | Message: `Replace with positive boolean expression` 26 | Priority: 80 27 | Diff for file `test/lints/dart/avoid_inverted_boolean_expressions/fix/avoid_inverted_boolean_expressions.dart:11`: 28 | ``` 29 | 30 | // expect_lint: avoid_inverted_boolean_expressions 31 | - final anotherNumber = !(number == 0) ? 1 : 2; 32 | + final anotherNumber = number != 0 ? 2 : 1; 33 | } 34 | ``` 35 | --- 36 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/prefer_text_rich/fix/prefer_text_rich_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pubspec_parse/pubspec_parse.dart'; 4 | import 'package:pyramid_lint/src/lints/flutter/prefer_text_rich.dart'; 5 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import '../../../../golden.dart'; 9 | 10 | void main() { 11 | testGolden( 12 | 'Test for prefer_text_rich fix', 13 | 'lints/flutter/prefer_text_rich/fix/prefer_text_rich.diff', 14 | sourcePath: 'test/lints/flutter/prefer_text_rich/fix/prefer_text_rich.dart', 15 | (result) async { 16 | const options = PyramidLintRuleOptions(params: null); 17 | final lint = PreferTextRich(options: options); 18 | final fix = lint.getFixes().single as DartFix; 19 | final pubspec = Pubspec('test', dependencies: {'flutter': SdkDependency('flutter')}); 20 | 21 | final errors = await lint.testRun(result, pubspec: pubspec); 22 | expect(errors, hasLength(1)); 23 | 24 | final changes = await Future.wait([ 25 | for (final error in errors) fix.testRun(result, error, errors), 26 | ]); 27 | 28 | return changes.flattened; 29 | }, 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/flutter/wrap_with_listenable_builder/wrap_with_listenable_builder.diff: -------------------------------------------------------------------------------- 1 | Message: `Wrap with ListenableBuilder` 2 | Priority: 29 3 | Diff for file `test/assists/flutter/wrap_with_listenable_builder/wrap_with_listenable_builder.dart:21`: 4 | ``` 5 | @override 6 | Widget build(BuildContext context) { 7 | - return Column( 8 | - children: [ 9 | - Text(_focusNode.hasFocus ? 'Focused' : 'Unfocused'), 10 | - TextFormField(focusNode: _focusNode), 11 | - ], 12 | - ); 13 | + return ListenableBuilder(listenable: _focusNode,builder: (context, child) { return Column( 14 | + children: [ 15 | + Text(_focusNode.hasFocus ? 'Focused' : 'Unfocused'), 16 | + TextFormField(focusNode: _focusNode), 17 | + ], 18 | + ); },); 19 | } 20 | } 21 | ``` 22 | --- 23 | Message: `Wrap with ListenableBuilder` 24 | Priority: 29 25 | Diff for file `test/assists/flutter/wrap_with_listenable_builder/wrap_with_listenable_builder.dart:24`: 26 | ``` 27 | children: [ 28 | Text(_focusNode.hasFocus ? 'Focused' : 'Unfocused'), 29 | - TextFormField(focusNode: _focusNode), 30 | + ListenableBuilder(listenable: _focusNode,builder: (context, child) { return TextFormField(focusNode: _focusNode); },), 31 | ], 32 | ); 33 | ``` 34 | --- 35 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_const_constructor_declarations/fix/prefer_const_constructor_declarations_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pyramid_lint/src/lints/dart/prefer_const_constructor_declarations.dart'; 4 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | import '../../../../golden.dart'; 8 | 9 | void main() { 10 | testGolden( 11 | 'Test for prefer_const_constructor_declarations fix', 12 | 'lints/dart/prefer_const_constructor_declarations/fix/prefer_const_constructor_declarations.diff', 13 | sourcePath: 'test/lints/dart/prefer_const_constructor_declarations/fix/prefer_const_constructor_declarations.dart', 14 | (result) async { 15 | const options = PyramidLintRuleOptions(params: null); 16 | final lint = PreferConstConstructorDeclarations(options: options); 17 | final fix = lint.getFixes().single as DartFix; 18 | 19 | final errors = await lint.testRun(result); 20 | expect(errors, hasLength(3)); 21 | 22 | final changes = await Future.wait([ 23 | for (final error in errors) fix.testRun(result, error, errors), 24 | ]); 25 | 26 | return changes.flattened; 27 | }, 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/dart/convert_to_for_in_iterable_indexed_loop/convert_to_for_in_iterable_indexed_loop.diff: -------------------------------------------------------------------------------- 1 | Message: `Convert to for-in iterable.indexed loop` 2 | Priority: 30 3 | Diff for file `test/assists/dart/convert_to_for_in_iterable_indexed_loop/convert_to_for_in_iterable_indexed_loop.dart:4`: 4 | ``` 5 | 6 | void fn(Iterable numbers) { 7 | - for (final int number in numbers) {} 8 | + for (final (int index, int number) in numbers.indexed) {} 9 | for (final number in numbers) {} 10 | } 11 | ``` 12 | --- 13 | Message: `Convert to for-in iterable.indexed loop` 14 | Priority: 30 15 | Diff for file `test/assists/dart/convert_to_for_in_iterable_indexed_loop/convert_to_for_in_iterable_indexed_loop.dart:5`: 16 | ``` 17 | void fn(Iterable numbers) { 18 | for (final int number in numbers) {} 19 | - for (final number in numbers) {} 20 | + for (final (index, number) in numbers.indexed) {} 21 | } 22 | 23 | ``` 24 | --- 25 | Message: `Convert to for-in iterable.indexed loop` 26 | Priority: 30 27 | Diff for file `test/assists/dart/convert_to_for_in_iterable_indexed_loop/convert_to_for_in_iterable_indexed_loop.dart:9`: 28 | ``` 29 | 30 | void fn2(String word) { 31 | - for (final String char in word.split('')) {} 32 | + for (final (int index, String char) in word.split('').indexed) {} 33 | } 34 | ``` 35 | --- 36 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/dispose_controllers/fix/dispose_controllers_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pubspec_parse/pubspec_parse.dart'; 4 | import 'package:pyramid_lint/src/lints/flutter/dispose_controllers.dart'; 5 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import '../../../../golden.dart'; 9 | 10 | void main() { 11 | testGolden( 12 | 'Test for dispose_controllers fix', 13 | 'lints/flutter/dispose_controllers/fix/dispose_controllers.diff', 14 | sourcePath: 'test/lints/flutter/dispose_controllers/fix/dispose_controllers.dart', 15 | (result) async { 16 | const options = PyramidLintRuleOptions(params: null); 17 | final lint = DisposeControllers(options: options); 18 | final fix = lint.getFixes().single as DartFix; 19 | final pubspec = Pubspec('test', dependencies: {'flutter': SdkDependency('flutter')}); 20 | 21 | final errors = await lint.testRun(result, pubspec: pubspec); 22 | expect(errors, hasLength(10)); 23 | 24 | final changes = await Future.wait([ 25 | for (final error in errors) fix.testRun(result, error, errors), 26 | ]); 27 | 28 | return changes.flattened; 29 | }, 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/prefer_void_callback/fix/prefer_void_callback_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pubspec_parse/pubspec_parse.dart'; 4 | import 'package:pyramid_lint/src/lints/flutter/prefer_void_callback.dart'; 5 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import '../../../../golden.dart'; 9 | 10 | void main() { 11 | testGolden( 12 | 'Test for prefer_void_callback fix', 13 | 'lints/flutter/prefer_void_callback/fix/prefer_void_callback.diff', 14 | sourcePath: 'test/lints/flutter/prefer_void_callback/fix/prefer_void_callback.dart', 15 | (result) async { 16 | const options = PyramidLintRuleOptions(params: null); 17 | final lint = PreferVoidCallback(options: options); 18 | final fix = lint.getFixes().single as DartFix; 19 | final pubspec = Pubspec('test', dependencies: {'flutter': SdkDependency('flutter')}); 20 | 21 | final errors = await lint.testRun(result, pubspec: pubspec); 22 | expect(errors, hasLength(2)); 23 | 24 | final changes = await Future.wait([ 25 | for (final error in errors) fix.testRun(result, error, errors), 26 | ]); 27 | 28 | return changes.flattened; 29 | }, 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/proper_super_dispose/fix/proper_super_dispose_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pubspec_parse/pubspec_parse.dart'; 4 | import 'package:pyramid_lint/src/lints/flutter/proper_super_dispose.dart'; 5 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import '../../../../golden.dart'; 9 | 10 | void main() { 11 | testGolden( 12 | 'Test for proper_super_dispose fix', 13 | 'lints/flutter/proper_super_dispose/fix/proper_super_dispose.diff', 14 | sourcePath: 'test/lints/flutter/proper_super_dispose/fix/proper_super_dispose.dart', 15 | (result) async { 16 | const options = PyramidLintRuleOptions(params: null); 17 | final lint = ProperSuperDispose(options: options); 18 | final fix = lint.getFixes().single as DartFix; 19 | final pubspec = Pubspec('test', dependencies: {'flutter': SdkDependency('flutter')}); 20 | 21 | final errors = await lint.testRun(result, pubspec: pubspec); 22 | expect(errors, hasLength(1)); 23 | 24 | final changes = await Future.wait([ 25 | for (final error in errors) fix.testRun(result, error, errors), 26 | ]); 27 | 28 | return changes.flattened; 29 | }, 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/prefer_async_callback/fix/prefer_async_callback_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pubspec_parse/pubspec_parse.dart'; 4 | import 'package:pyramid_lint/src/lints/flutter/prefer_async_callback.dart'; 5 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import '../../../../golden.dart'; 9 | 10 | void main() { 11 | testGolden( 12 | 'Test for prefer_async_callback fix', 13 | 'lints/flutter/prefer_async_callback/fix/prefer_async_callback.diff', 14 | sourcePath: 'test/lints/flutter/prefer_async_callback/fix/prefer_async_callback.dart', 15 | (result) async { 16 | const options = PyramidLintRuleOptions(params: null); 17 | final lint = PreferAsyncCallback(options: options); 18 | final fix = lint.getFixes().single as DartFix; 19 | final pubspec = Pubspec('test', dependencies: {'flutter': SdkDependency('flutter')}); 20 | 21 | final errors = await lint.testRun(result, pubspec: pubspec); 22 | expect(errors, hasLength(2)); 23 | 24 | final changes = await Future.wait([ 25 | for (final error in errors) fix.testRun(result, error, errors), 26 | ]); 27 | 28 | return changes.flattened; 29 | }, 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/always_put_doc_comments_before_annotations/fix/always_put_doc_comments_before_annotations_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pyramid_lint/src/lints/dart/always_put_doc_comments_before_annotations.dart'; 4 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 5 | import 'package:test/test.dart'; 6 | 7 | import '../../../../golden.dart'; 8 | 9 | void main() { 10 | testGolden( 11 | 'Test for always_put_doc_comments_before_annotations fix', 12 | 'lints/dart/always_put_doc_comments_before_annotations/fix/always_put_doc_comments_before_annotations.diff', 13 | sourcePath: 14 | 'test/lints/dart/always_put_doc_comments_before_annotations/fix/always_put_doc_comments_before_annotations.dart', 15 | (result) async { 16 | const options = PyramidLintRuleOptions(params: null); 17 | final lint = AlwaysPutDocCommentsBeforeAnnotations(options: options); 18 | final fix = lint.getFixes().single as DartFix; 19 | 20 | final errors = await lint.testRun(result); 21 | expect(errors, hasLength(2)); 22 | 23 | final changes = await Future.wait([ 24 | for (final error in errors) fix.testRun(result, error, errors), 25 | ]); 26 | 27 | return changes.flattened; 28 | }, 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/proper_from_environment/fix/proper_from_environment_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pubspec_parse/pubspec_parse.dart'; 4 | import 'package:pyramid_lint/src/lints/flutter/proper_from_environment.dart'; 5 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import '../../../../golden.dart'; 9 | 10 | void main() { 11 | testGolden( 12 | 'Test for proper_from_environment fix', 13 | 'lints/flutter/proper_from_environment/fix/proper_from_environment.diff', 14 | sourcePath: 'test/lints/flutter/proper_from_environment/fix/proper_from_environment.dart', 15 | (result) async { 16 | const options = PyramidLintRuleOptions(params: null); 17 | final lint = ProperFromEnvironment(options: options); 18 | final fix = lint.getFixes().single as DartFix; 19 | final pubspec = Pubspec('test', dependencies: {'flutter': SdkDependency('flutter')}); 20 | 21 | final errors = await lint.testRun(result, pubspec: pubspec); 22 | expect(errors, hasLength(1)); 23 | 24 | final changes = await Future.wait([ 25 | for (final error in errors) fix.testRun(result, error, errors), 26 | ]); 27 | 28 | return changes.flattened; 29 | }, 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/proper_super_init_state/fix/proper_super_init_state_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pubspec_parse/pubspec_parse.dart'; 4 | import 'package:pyramid_lint/src/lints/flutter/proper_super_init_state.dart'; 5 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import '../../../../golden.dart'; 9 | 10 | void main() { 11 | testGolden( 12 | 'Test for proper_super_init_state fix', 13 | 'lints/flutter/proper_super_init_state/fix/proper_super_init_state.diff', 14 | sourcePath: 'test/lints/flutter/proper_super_init_state/fix/proper_super_init_state.dart', 15 | (result) async { 16 | const options = PyramidLintRuleOptions(params: null); 17 | final lint = ProperSuperInitState(options: options); 18 | final fix = lint.getFixes().single as DartFix; 19 | final pubspec = Pubspec('test', dependencies: {'flutter': SdkDependency('flutter')}); 20 | 21 | final errors = await lint.testRun(result, pubspec: pubspec); 22 | expect(errors, hasLength(1)); 23 | 24 | final changes = await Future.wait([ 25 | for (final error in errors) fix.testRun(result, error, errors), 26 | ]); 27 | 28 | return changes.flattened; 29 | }, 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/prefer_border_radius_all/fix/prefer_border_radius_all_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pubspec_parse/pubspec_parse.dart'; 4 | import 'package:pyramid_lint/src/lints/flutter/prefer_border_radius_all.dart'; 5 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import '../../../../golden.dart'; 9 | 10 | void main() { 11 | testGolden( 12 | 'Test for prefer_border_radius_all fix', 13 | 'lints/flutter/prefer_border_radius_all/fix/prefer_border_radius_all.diff', 14 | sourcePath: 'test/lints/flutter/prefer_border_radius_all/fix/prefer_border_radius_all.dart', 15 | (result) async { 16 | const options = PyramidLintRuleOptions(params: null); 17 | final lint = PreferBorderRadiusAll(options: options); 18 | final fix = lint.getFixes().single as DartFix; 19 | final pubspec = Pubspec('test', dependencies: {'flutter': SdkDependency('flutter')}); 20 | 21 | final errors = await lint.testRun(result, pubspec: pubspec); 22 | expect(errors, hasLength(1)); 23 | 24 | final changes = await Future.wait([ 25 | for (final error in errors) fix.testRun(result, error, errors), 26 | ]); 27 | 28 | return changes.flattened; 29 | }, 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Please provide a summary of the changes made in this pull request and mention the related issue. 4 | 5 | Fixes #(issue) 6 | 7 | ## Type of change 8 | 9 | - [ ] Bug fix (non-breaking change that fixes an issue) 10 | - [ ] New feature (non-breaking change that adds functionality) 11 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 12 | - [ ] Code refactor 13 | - [ ] Documentation 14 | - [ ] Other 15 | 16 | ## Checklist 17 | 18 | - [ ] I have read the [CONTRIBUTING.md][contributing] document. 19 | - [ ] I have performed a self-review of my code. 20 | - [ ] I have linked the issue ticket in the description (if applicable). 21 | - [ ] I have made the necessary changes to the documentation. 22 | - [ ] I have formatted my code with `dart format .` in both `packages/pyramid_lint/` and `packages/pyramid_lint_test/`. 23 | - [ ] I have analyzed my code with `dart analyze .` in both `packages/pyramid_lint/` and `packages/pyramid_lint_test/`. 24 | - [ ] I have analyzed my code with `dart run custom_lint .` or `custom_lint .`(if you have `custom_lint` installed globally) in `packages/pyramid_lint_test/`. 25 | - [ ] I have tested my code with `dart test` in `packages/pyramid_lint_test/`. 26 | 27 | 28 | 29 | [contributing]: https://github.com/charlescyt/pyramid_lint/blob/main/CONTRIBUTING.md 30 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/specify_icon_button_tooltip/fix/specify_icon_button_tooltip_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pubspec_parse/pubspec_parse.dart'; 4 | import 'package:pyramid_lint/src/lints/flutter/specify_icon_button_tooltip.dart'; 5 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import '../../../../golden.dart'; 9 | 10 | void main() { 11 | testGolden( 12 | 'Test for specify_icon_button_tooltip fix', 13 | 'lints/flutter/specify_icon_button_tooltip/fix/specify_icon_button_tooltip.diff', 14 | sourcePath: 'test/lints/flutter/specify_icon_button_tooltip/fix/specify_icon_button_tooltip.dart', 15 | (result) async { 16 | const options = PyramidLintRuleOptions(params: null); 17 | final lint = SpecifyIconButtonTooltip(options: options); 18 | final fix = lint.getFixes().single as DartFix; 19 | final pubspec = Pubspec('test', dependencies: {'flutter': SdkDependency('flutter')}); 20 | 21 | final errors = await lint.testRun(result, pubspec: pubspec); 22 | expect(errors, hasLength(2)); 23 | 24 | final changes = await Future.wait([ 25 | for (final error in errors) fix.testRun(result, error, errors), 26 | ]); 27 | 28 | return changes.flattened; 29 | }, 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/dart/convert_to_for_in_iterable_indexed_loop/convert_to_for_in_iterable_indexed_loop_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/source/source_range.dart'; 2 | import 'package:pyramid_lint/src/assists/dart/convert_to_for_in_iterable_indexed_loop.dart'; 3 | import 'package:test/test.dart'; 4 | 5 | import '../../../golden.dart'; 6 | 7 | void main() { 8 | testGolden( 9 | 'Convert to for-in iterable indexed loop', 10 | 'assists/dart/convert_to_for_in_iterable_indexed_loop/convert_to_for_in_iterable_indexed_loop.diff', 11 | sourcePath: 12 | 'test/assists/dart/convert_to_for_in_iterable_indexed_loop/convert_to_for_in_iterable_indexed_loop.dart', 13 | (result) async { 14 | final assist = ConvertToForInIterableIndexedLoop(); 15 | 16 | final changes = [ 17 | // for (final String number in numbers) {} 18 | // ^^ 19 | ...await assist.testRun(result, const SourceRange(126, 0)), 20 | 21 | // for (final number in numbers) {} 22 | // ^^ 23 | ...await assist.testRun(result, const SourceRange(178, 0)), 24 | 25 | // for (final String char in word.split('')) {} 26 | // ^^ 27 | ...await assist.testRun(result, const SourceRange(260, 0)), 28 | ]; 29 | 30 | expect(changes, hasLength(3)); 31 | 32 | return changes; 33 | }, 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_iterable_every/prever_iterable_every.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: unnecessary_lambdas 2 | 3 | void fn(Iterable numbers) { 4 | // expect_lint: prefer_iterable_every 5 | numbers.where((n) => n.isEven).isEmpty; 6 | 7 | // expect_lint: prefer_iterable_every 8 | numbers.where((n) => n == 0).isEmpty; 9 | 10 | // expect_lint: prefer_iterable_every 11 | numbers.where((n) => n > 0).isEmpty; 12 | 13 | // expect_lint: prefer_iterable_every 14 | numbers.where((n) => n % 3 == 0).isEmpty; 15 | 16 | // expect_lint: prefer_iterable_every 17 | numbers.where((n) => isMultipleOfThree(n)).isEmpty; 18 | 19 | // expect_lint: prefer_iterable_every 20 | numbers.where(isMultipleOfThree).isEmpty; 21 | 22 | // expect_lint: prefer_iterable_every 23 | numbers.where((n) { 24 | return n.isEven; 25 | }).isEmpty; 26 | 27 | // expect_lint: prefer_iterable_every 28 | numbers.where((n) { 29 | return n == 0; 30 | }).isEmpty; 31 | 32 | // expect_lint: prefer_iterable_every 33 | numbers.where((n) { 34 | return n > 0; 35 | }).isEmpty; 36 | 37 | // expect_lint: prefer_iterable_every 38 | numbers.where((n) { 39 | return n % 3 == 0; 40 | }).isEmpty; 41 | 42 | // expect_lint: prefer_iterable_every 43 | numbers.where((n) { 44 | return isMultipleOfThree(n); 45 | }).isEmpty; 46 | } 47 | 48 | bool isMultipleOfThree(int number) => number % 3 == 0; 49 | -------------------------------------------------------------------------------- /docs/flutter-lints/dispose_controllers.mdx: -------------------------------------------------------------------------------- 1 | # dispose_controllers 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Error | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **DO** dispose `AnimationController` and all `ChangeNotifier` subclasses such as `TextEditingController`, `ScrollController` and `TabController`. 10 | 11 | Controllers should be disposed in `Widget`'s `dispose` method to avoid memory leaks. 12 | 13 | This lint supports custom controllers that extend `ChangeNotifier`. 14 | 15 | ```dart title="Bad" 16 | class _MyWidgetState extends State { 17 | final _textEditingController = TextEditingController(); 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | return Placeholder(); 22 | } 23 | } 24 | ``` 25 | 26 | ```dart title="Good" 27 | class _MyWidgetState extends State { 28 | final _textEditingController = TextEditingController(); 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | return Placeholder(); 33 | } 34 | 35 | @override 36 | void dispose() { 37 | _textEditingController.dispose(); 38 | super.dispose(); 39 | } 40 | } 41 | ``` 42 | 43 | ## Usage 44 | 45 | To enable the `dispose_controllers` rule, add `dispose_controllers` under custom_lint > rules in your `analysis_options.yaml` file: 46 | 47 | ```yaml 48 | custom_lint: 49 | rules: 50 | - dispose_controllers 51 | ``` 52 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/flutter/wrap_with_listenable_builder/wrap_with_listenable_builder_test.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: max_lines_for_function 2 | 3 | import 'package:analyzer/source/source_range.dart'; 4 | import 'package:pub_semver/pub_semver.dart'; 5 | import 'package:pubspec_parse/pubspec_parse.dart'; 6 | import 'package:pyramid_lint/src/assists/flutter/wrap_with_listenable_builder.dart'; 7 | import 'package:test/test.dart'; 8 | 9 | import '../../../golden.dart'; 10 | 11 | void main() { 12 | testGolden( 13 | 'Wrap with ListenableBuilder', 14 | 'assists/flutter/wrap_with_listenable_builder/wrap_with_listenable_builder.diff', 15 | sourcePath: 'test/assists/flutter/wrap_with_listenable_builder/wrap_with_listenable_builder.dart', 16 | (result) async { 17 | final assist = WrapWithListenableBuilder(); 18 | final pubspec = Pubspec( 19 | 'test', 20 | environment: {'sdk': VersionConstraint.parse('>=3.0.0 <4.0.0')}, 21 | dependencies: {'flutter': SdkDependency('flutter')}, 22 | ); 23 | 24 | final changes = [ 25 | // Column 26 | ...await assist.testRun(result, const SourceRange(555, 0), pubspec: pubspec), 27 | 28 | // TextFormField 29 | ...await assist.testRun(result, const SourceRange(657, 0), pubspec: pubspec), 30 | ]; 31 | 32 | expect(changes, hasLength(2)); 33 | 34 | return changes; 35 | }, 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_iterable_any/fix/prefer_iterable_any.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: unnecessary_lambdas 2 | 3 | void fn(Iterable numbers) { 4 | // expect_lint: prefer_iterable_any 5 | numbers.where((n) => n.isEven).isNotEmpty; 6 | 7 | // expect_lint: prefer_iterable_any 8 | numbers.where((n) => n == 0).isNotEmpty; 9 | 10 | // expect_lint: prefer_iterable_any 11 | numbers.where((n) => n > 0).isNotEmpty; 12 | 13 | // expect_lint: prefer_iterable_any 14 | numbers.where((n) => n % 3 == 0).isNotEmpty; 15 | 16 | // expect_lint: prefer_iterable_any 17 | numbers.where((n) => isMultipleOfThree(n)).isNotEmpty; 18 | 19 | // expect_lint: prefer_iterable_any 20 | numbers.where(isMultipleOfThree).isNotEmpty; 21 | 22 | // expect_lint: prefer_iterable_any 23 | numbers.where((item) { 24 | return item.isEven; 25 | }).isNotEmpty; 26 | 27 | // expect_lint: prefer_iterable_any 28 | numbers.where((n) { 29 | return n == 0; 30 | }).isNotEmpty; 31 | 32 | // expect_lint: prefer_iterable_any 33 | numbers.where((n) { 34 | return n > 0; 35 | }).isNotEmpty; 36 | 37 | // expect_lint: prefer_iterable_any 38 | numbers.where((n) { 39 | return n % 3 == 0; 40 | }).isNotEmpty; 41 | 42 | // expect_lint: prefer_iterable_any 43 | numbers.where((n) { 44 | return isMultipleOfThree(n); 45 | }).isNotEmpty; 46 | } 47 | 48 | bool isMultipleOfThree(int number) => number % 3 == 0; 49 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_iterable_any/prefer_iterable_any.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: unnecessary_lambdas 2 | 3 | void fn(Iterable numbers) { 4 | // expect_lint: prefer_iterable_any 5 | numbers.where((n) => n.isEven).isNotEmpty; 6 | 7 | // expect_lint: prefer_iterable_any 8 | numbers.where((n) => n == 0).isNotEmpty; 9 | 10 | // expect_lint: prefer_iterable_any 11 | numbers.where((n) => n > 0).isNotEmpty; 12 | 13 | // expect_lint: prefer_iterable_any 14 | numbers.where((n) => n % 3 == 0).isNotEmpty; 15 | 16 | // expect_lint: prefer_iterable_any 17 | numbers.where((n) => isMultipleOfThree(n)).isNotEmpty; 18 | 19 | // expect_lint: prefer_iterable_any 20 | numbers.where(isMultipleOfThree).isNotEmpty; 21 | 22 | // expect_lint: prefer_iterable_any 23 | numbers.where((item) { 24 | return item.isEven; 25 | }).isNotEmpty; 26 | 27 | // expect_lint: prefer_iterable_any 28 | numbers.where((n) { 29 | return n == 0; 30 | }).isNotEmpty; 31 | 32 | // expect_lint: prefer_iterable_any 33 | numbers.where((n) { 34 | return n > 0; 35 | }).isNotEmpty; 36 | 37 | // expect_lint: prefer_iterable_any 38 | numbers.where((n) { 39 | return n % 3 == 0; 40 | }).isNotEmpty; 41 | 42 | // expect_lint: prefer_iterable_any 43 | numbers.where((n) { 44 | return isMultipleOfThree(n); 45 | }).isNotEmpty; 46 | } 47 | 48 | bool isMultipleOfThree(int number) => number % 3 == 0; 49 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/prefer_border_from_border_side/fix/prefer_border_from_border_side_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pubspec_parse/pubspec_parse.dart'; 4 | import 'package:pyramid_lint/src/lints/flutter/prefer_border_from_border_side.dart'; 5 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import '../../../../golden.dart'; 9 | 10 | void main() { 11 | testGolden( 12 | 'Test for prefer_border_from_border_side fix', 13 | 'lints/flutter/prefer_border_from_border_side/fix/prefer_border_from_border_side.diff', 14 | sourcePath: 'test/lints/flutter/prefer_border_from_border_side/fix/prefer_border_from_border_side.dart', 15 | (result) async { 16 | const options = PyramidLintRuleOptions(params: null); 17 | final lint = PreferBorderFromBorderSide(options: options); 18 | final fix = lint.getFixes().single as DartFix; 19 | final pubspec = Pubspec('test', dependencies: {'flutter': SdkDependency('flutter')}); 20 | 21 | final errors = await lint.testRun(result, pubspec: pubspec); 22 | expect(errors, hasLength(1)); 23 | 24 | final changes = await Future.wait([ 25 | for (final error in errors) fix.testRun(result, error, errors), 26 | ]); 27 | 28 | return changes.flattened; 29 | }, 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/proper_edge_insets_constructors/fix/proper_edge_insets_constructors_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pubspec_parse/pubspec_parse.dart'; 4 | import 'package:pyramid_lint/src/lints/flutter/proper_edge_insets_constructors.dart'; 5 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import '../../../../golden.dart'; 9 | 10 | void main() { 11 | testGolden( 12 | 'Test for proper_edge_insets_constructors fix', 13 | 'lints/flutter/proper_edge_insets_constructors/fix/proper_edge_insets_constructors.diff', 14 | sourcePath: 'test/lints/flutter/proper_edge_insets_constructors/fix/proper_edge_insets_constructors.dart', 15 | (result) async { 16 | const options = PyramidLintRuleOptions(params: null); 17 | final lint = ProperEdgeInsetsConstructors(options: options); 18 | final fix = lint.getFixes().single as DartFix; 19 | final pubspec = Pubspec('test', dependencies: {'flutter': SdkDependency('flutter')}); 20 | 21 | final errors = await lint.testRun(result, pubspec: pubspec); 22 | expect(errors, hasLength(10)); 23 | 24 | final changes = await Future.wait([ 25 | for (final error in errors) fix.testRun(result, error, errors), 26 | ]); 27 | 28 | return changes.flattened; 29 | }, 30 | ); 31 | } 32 | -------------------------------------------------------------------------------- /docs/flutter-lints/avoid_public_members_in_states.mdx: -------------------------------------------------------------------------------- 1 | # avoid_public_members_in_states 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ❌ | ❌ | 6 | 7 | ## Details 8 | 9 | **AVOID** declaring public fields and methods in widget state classes. 10 | 11 | Public fields and methods with `@visibleForTesting` annotations are ignored. 12 | 13 | ```dart title="Bad" 14 | class _MyWidgetState extends State { 15 | int counter = 0; 16 | 17 | void increment() { 18 | setState(() { 19 | counter++; 20 | }); 21 | } 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return FilledButton( 26 | onPressed: increment, 27 | child: Text('$counter'), 28 | ); 29 | } 30 | } 31 | ``` 32 | 33 | ```dart title="Good" 34 | class _MyWidgetState extends State { 35 | int _counter = 0; 36 | 37 | void _increment() { 38 | setState(() { 39 | _counter++; 40 | }); 41 | } 42 | 43 | @override 44 | Widget build(BuildContext context) { 45 | return FilledButton( 46 | onPressed: _increment, 47 | child: Text('$_counter'), 48 | ); 49 | } 50 | } 51 | ``` 52 | 53 | ## Usage 54 | 55 | To enable the `avoid_public_members_in_states` rule, add `avoid_public_members_in_states` under custom_lint > rules in your `analysis_options.yaml` file: 56 | 57 | ```yaml 58 | custom_lint: 59 | rules: 60 | - avoid_public_members_in_states 61 | ``` 62 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/proper_edge_insets_constructors/proper_edge_insets_constructors.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: unused_local_variable 2 | 3 | import 'package:flutter/widgets.dart'; 4 | 5 | void example() { 6 | const a = 8.0; 7 | late EdgeInsets padding; 8 | 9 | // expect_lint: proper_edge_insets_constructors 10 | padding = const EdgeInsets.fromLTRB(a, a, a, a); 11 | // expect_lint: proper_edge_insets_constructors 12 | padding = const EdgeInsets.fromLTRB(a, 0, a, 0); 13 | // expect_lint: proper_edge_insets_constructors 14 | padding = const EdgeInsets.fromLTRB(a, 4, a, 4); 15 | // expect_lint: proper_edge_insets_constructors 16 | padding = const EdgeInsets.fromLTRB(a, 0, 2, 4); 17 | 18 | // expect_lint: proper_edge_insets_constructors 19 | padding = const EdgeInsets.only(left: a, top: a, right: a, bottom: a); 20 | // expect_lint: proper_edge_insets_constructors 21 | padding = const EdgeInsets.only(left: a, top: 0, right: a, bottom: 0); 22 | // expect_lint: proper_edge_insets_constructors 23 | padding = const EdgeInsets.only(left: a, top: 4, right: a, bottom: 4); 24 | // expect_lint: proper_edge_insets_constructors 25 | padding = const EdgeInsets.only(left: 2, top: 4, right: 6, bottom: a); 26 | 27 | // expect_lint: proper_edge_insets_constructors 28 | padding = const EdgeInsets.symmetric(horizontal: a, vertical: a); 29 | // expect_lint: proper_edge_insets_constructors 30 | padding = const EdgeInsets.symmetric(horizontal: a, vertical: 0); 31 | } 32 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/proper_edge_insets_constructors/fix/proper_edge_insets_constructors.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: unused_local_variable 2 | 3 | import 'package:flutter/widgets.dart'; 4 | 5 | void example() { 6 | const a = 8.0; 7 | late EdgeInsets padding; 8 | 9 | // expect_lint: proper_edge_insets_constructors 10 | padding = const EdgeInsets.fromLTRB(a, a, a, a); 11 | // expect_lint: proper_edge_insets_constructors 12 | padding = const EdgeInsets.fromLTRB(a, 0, a, 0); 13 | // expect_lint: proper_edge_insets_constructors 14 | padding = const EdgeInsets.fromLTRB(a, 4, a, 4); 15 | // expect_lint: proper_edge_insets_constructors 16 | padding = const EdgeInsets.fromLTRB(a, 0, 2, 4); 17 | 18 | // expect_lint: proper_edge_insets_constructors 19 | padding = const EdgeInsets.only(left: a, top: a, right: a, bottom: a); 20 | // expect_lint: proper_edge_insets_constructors 21 | padding = const EdgeInsets.only(left: a, top: 0, right: a, bottom: 0); 22 | // expect_lint: proper_edge_insets_constructors 23 | padding = const EdgeInsets.only(left: a, top: 4, right: a, bottom: 4); 24 | // expect_lint: proper_edge_insets_constructors 25 | padding = const EdgeInsets.only(left: 2, top: 4, right: 6, bottom: a); 26 | 27 | // expect_lint: proper_edge_insets_constructors 28 | padding = const EdgeInsets.symmetric(horizontal: a, vertical: a); 29 | // expect_lint: proper_edge_insets_constructors 30 | padding = const EdgeInsets.symmetric(horizontal: a, vertical: 0); 31 | } 32 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/prefer_dedicated_media_query_functions/fix/prefer_dedicated_media_query_functions_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:collection/collection.dart'; 2 | import 'package:custom_lint_core/custom_lint_core.dart'; 3 | import 'package:pubspec_parse/pubspec_parse.dart'; 4 | import 'package:pyramid_lint/src/lints/flutter/prefer_dedicated_media_query_functions.dart'; 5 | import 'package:pyramid_lint/src/pyramid_lint_rule.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import '../../../../golden.dart'; 9 | 10 | void main() { 11 | testGolden( 12 | 'Test for prefer_dedicated_media_query_functions fix', 13 | 'lints/flutter/prefer_dedicated_media_query_functions/fix/prefer_dedicated_media_query_functions.diff', 14 | sourcePath: 15 | 'test/lints/flutter/prefer_dedicated_media_query_functions/fix/prefer_dedicated_media_query_functions.dart', 16 | (result) async { 17 | const options = PyramidLintRuleOptions(params: null); 18 | final lint = PreferDedicatedMediaQueryFunctions(options: options); 19 | final fix = lint.getFixes().single as DartFix; 20 | final pubspec = Pubspec('test', dependencies: {'flutter': SdkDependency('flutter')}); 21 | 22 | final errors = await lint.testRun(result, pubspec: pubspec); 23 | expect(errors, hasLength(80)); 24 | 25 | final changes = await Future.wait([ 26 | for (final error in errors) fix.testRun(result, error, errors), 27 | ]); 28 | 29 | return changes.flattened; 30 | }, 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/flutter/wrap_all_children_with_expanded/wrap_all_children_with_expanded.diff: -------------------------------------------------------------------------------- 1 | Message: `Wrap all children with Expanded` 2 | Priority: 27 3 | Diff for file `test/assists/flutter/wrap_all_children_with_expanded/wrap_all_children_with_expanded.dart:10`: 4 | ``` 5 | return const Column( 6 | children: [ 7 | - Row( 8 | - children: [ 9 | - Flexible( 10 | - child: Text('Pyramid'), 11 | - ), 12 | - Spacer(), 13 | - Text('Lint'), 14 | - ], 15 | - ), 16 | - Row( 17 | - children: [ 18 | - Text('Is'), 19 | - Spacer(), 20 | - Text('Great'), 21 | - ], 22 | - ), 23 | + Expanded(child: Row( 24 | + children: [ 25 | + Flexible( 26 | + child: Text('Pyramid'), 27 | + ), 28 | + Spacer(), 29 | + Text('Lint'), 30 | + ], 31 | + )), 32 | + Expanded(child: Row( 33 | + children: [ 34 | + Text('Is'), 35 | + Spacer(), 36 | + Text('Great'), 37 | + ], 38 | + )), 39 | ], 40 | ); 41 | ``` 42 | --- 43 | Message: `Wrap all children with Expanded` 44 | Priority: 27 45 | Diff for file `test/assists/flutter/wrap_all_children_with_expanded/wrap_all_children_with_expanded.dart:16`: 46 | ``` 47 | ), 48 | Spacer(), 49 | - Text('Lint'), 50 | + Expanded(child: Text('Lint')), 51 | ], 52 | ), 53 | ``` 54 | --- 55 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/flutter/wrap_with_stack/wrap_with_stack.diff: -------------------------------------------------------------------------------- 1 | Message: `Wrap with Stack` 2 | Priority: 28 3 | Diff for file `test/assists/flutter/wrap_with_stack/wrap_with_stack.dart:8`: 4 | ``` 5 | @override 6 | Widget build(BuildContext context) { 7 | - return const Column( 8 | - children: [ 9 | - Text( 10 | - 'Pyramid', 11 | - style: TextStyle( 12 | - fontSize: 20, 13 | - fontWeight: FontWeight.bold, 14 | - ), 15 | - ), 16 | - Text('Lint'), 17 | - ], 18 | - ); 19 | + return Stack(children: [const Column( 20 | + children: [ 21 | + Text( 22 | + 'Pyramid', 23 | + style: TextStyle( 24 | + fontSize: 20, 25 | + fontWeight: FontWeight.bold, 26 | + ), 27 | + ), 28 | + Text('Lint'), 29 | + ], 30 | + ),],); 31 | } 32 | } 33 | ``` 34 | --- 35 | Message: `Wrap with Stack` 36 | Priority: 28 37 | Diff for file `test/assists/flutter/wrap_with_stack/wrap_with_stack.dart:10`: 38 | ``` 39 | return const Column( 40 | children: [ 41 | - Text( 42 | - 'Pyramid', 43 | - style: TextStyle( 44 | - fontSize: 20, 45 | - fontWeight: FontWeight.bold, 46 | - ), 47 | - ), 48 | + Stack(children: [Text( 49 | + 'Pyramid', 50 | + style: TextStyle( 51 | + fontSize: 20, 52 | + fontWeight: FontWeight.bold, 53 | + ), 54 | + ),],), 55 | Text('Lint'), 56 | ], 57 | ``` 58 | --- 59 | -------------------------------------------------------------------------------- /packages/pyramid_lint/lib/src/lints/dart/always_specify_parameter_names.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/error/error.dart'; 2 | import 'package:analyzer/error/listener.dart'; 3 | import 'package:custom_lint_builder/custom_lint_builder.dart'; 4 | 5 | import '../../pyramid_lint_rule.dart'; 6 | import '../../utils/constants.dart'; 7 | 8 | class AlwaysSpecifyParameterNames extends PyramidLintRule { 9 | AlwaysSpecifyParameterNames({required super.options}) 10 | : super( 11 | name: ruleName, 12 | problemMessage: 13 | 'Parameter names should always be specified to enhance code readability ' 14 | 'and enable IDEs to provide code completion suggestions.', 15 | correctionMessage: 'Consider declaring a descriptive parameter name.', 16 | url: url, 17 | errorSeverity: DiagnosticSeverity.INFO, 18 | ); 19 | 20 | static const ruleName = 'always_specify_parameter_names'; 21 | static const url = '$dartLintDocUrl/$ruleName'; 22 | 23 | factory AlwaysSpecifyParameterNames.fromConfigs(CustomLintConfigs configs) { 24 | final json = configs.rules[ruleName]?.json ?? {}; 25 | final options = PyramidLintRuleOptions.fromJson(json: json, paramsConverter: (_) => null); 26 | 27 | return AlwaysSpecifyParameterNames(options: options); 28 | } 29 | 30 | @override 31 | void run( 32 | CustomLintResolver resolver, 33 | DiagnosticReporter reporter, 34 | CustomLintContext context, 35 | ) { 36 | context.registry.addSimpleFormalParameter((node) { 37 | if (node.name == null) { 38 | reporter.atNode(node, code); 39 | } 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/pyramid_lint/lib/src/assists/dart/invert_boolean_expression.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/source/source_range.dart'; 2 | import 'package:custom_lint_builder/custom_lint_builder.dart'; 3 | 4 | import '../../utils/ast_node_extensions.dart'; 5 | import '../../utils/utils.dart'; 6 | 7 | class InvertBooleanExpression extends DartAssist { 8 | @override 9 | Future run( 10 | CustomLintResolver resolver, 11 | ChangeReporter reporter, 12 | CustomLintContext context, 13 | SourceRange target, 14 | ) async { 15 | context.registry.addBinaryExpression((node) { 16 | if (!node.sourceRange.covers(target)) return; 17 | 18 | final childrenBinaryExpressions = node.childrenBinaryExpressions; 19 | final isTargetInsideChildrenBinaryExpressions = childrenBinaryExpressions.any( 20 | (e) => e.sourceRange.intersects(target), 21 | ); 22 | if (isTargetInsideChildrenBinaryExpressions) return; 23 | 24 | final operator = node.operator.type; 25 | if (!operator.isEqualityOperator && !operator.isRelationalOperator) { 26 | return; 27 | } 28 | 29 | final invertedOperator = getInvertedOperator(operator); 30 | if (invertedOperator == null) return; 31 | 32 | final changeBuilder = reporter.createChangeBuilder( 33 | message: 'Invert boolean expression', 34 | priority: 80, 35 | ); 36 | 37 | changeBuilder.addDartFileEdit((builder) { 38 | builder.addSimpleReplacement( 39 | node.sourceRange, 40 | '${node.leftOperand} ${invertedOperator.lexeme} ${node.rightOperand}', 41 | ); 42 | }); 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/flutter/use_edge_insets_zero/use_edge_insets_zero_test.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: max_lines_for_function 2 | 3 | import 'package:analyzer/source/source_range.dart'; 4 | import 'package:pubspec_parse/pubspec_parse.dart'; 5 | import 'package:pyramid_lint/src/assists/flutter/use_edge_insets_zero.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import '../../../golden.dart'; 9 | 10 | void main() { 11 | testGolden( 12 | 'Use EdgeInsets.zero', 13 | 'assists/flutter/use_edge_insets_zero/use_edge_insets_zero.diff', 14 | sourcePath: 'test/assists/flutter/use_edge_insets_zero/use_edge_insets_zero.dart', 15 | (result) async { 16 | final assist = UseEdgeInsetsZero(); 17 | final pubspec = Pubspec('test', dependencies: {'flutter': SdkDependency('flutter')}); 18 | 19 | final changes = [ 20 | // EdgeInsets.all(0) 21 | // ^^ 22 | ...await assist.testRun(result, const SourceRange(147, 0), pubspec: pubspec), 23 | 24 | // EdgeInsets.fromLTRB(0, 0, 0, 0) 25 | // ^^ 26 | ...await assist.testRun(result, const SourceRange(177, 0), pubspec: pubspec), 27 | 28 | // EdgeInsets.only(left: 0, top: 0, right: 0, bottom: 0) 29 | // ^^ 30 | ...await assist.testRun(result, const SourceRange(236, 0), pubspec: pubspec), 31 | 32 | // EdgeInsets.symmetric(vertical: 0, horizontal: 0) 33 | // ^^ 34 | ...await assist.testRun(result, const SourceRange(302, 0), pubspec: pubspec), 35 | ]; 36 | 37 | expect(changes, hasLength(4)); 38 | 39 | return changes; 40 | }, 41 | ); 42 | } 43 | -------------------------------------------------------------------------------- /packages/pyramid_lint/lib/src/lints/dart/avoid_mutable_global_variables.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/error/error.dart'; 2 | import 'package:analyzer/error/listener.dart'; 3 | import 'package:custom_lint_builder/custom_lint_builder.dart'; 4 | 5 | import '../../pyramid_lint_rule.dart'; 6 | import '../../utils/constants.dart'; 7 | 8 | class AvoidMutableGlobalVariables extends PyramidLintRule { 9 | AvoidMutableGlobalVariables({required super.options}) 10 | : super( 11 | name: ruleName, 12 | problemMessage: 'Using mutable global variables is discouraged.', 13 | correctionMessage: 'Consider declaring the variable as final or const.', 14 | url: url, 15 | errorSeverity: DiagnosticSeverity.WARNING, 16 | ); 17 | 18 | static const ruleName = 'avoid_mutable_global_variables'; 19 | static const url = '$dartLintDocUrl/$ruleName'; 20 | 21 | factory AvoidMutableGlobalVariables.fromConfigs(CustomLintConfigs configs) { 22 | final json = configs.rules[ruleName]?.json ?? {}; 23 | final options = PyramidLintRuleOptions.fromJson(json: json, paramsConverter: (_) => null); 24 | 25 | return AvoidMutableGlobalVariables(options: options); 26 | } 27 | 28 | @override 29 | void run( 30 | CustomLintResolver resolver, 31 | DiagnosticReporter reporter, 32 | CustomLintContext context, 33 | ) { 34 | context.registry.addTopLevelVariableDeclaration((node) { 35 | final variables = node.variables.variables; 36 | final isConstOrFinal = variables.every((v) => v.isConst || v.isFinal); 37 | if (isConstOrFinal) return; 38 | 39 | reporter.atNode(node, code); 40 | }); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /docs/dart-lints/avoid_abbreviations_in_doc_comments.mdx: -------------------------------------------------------------------------------- 1 | # avoid_abbreviations_in_doc_comments 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Warning | ❌ | ✅ | 6 | 7 | ## Details 8 | 9 | **DO** avoid using abbreviations in doc comments as they can hinder readability and cause confusion for readers. 10 | 11 | see: [effective-dart](https://dart.dev/effective-dart/documentation#avoid-abbreviations-and-acronyms-unless-they-are-obvious) 12 | 13 | Included abbreviations: 14 | 15 | - e.g. 16 | - i.e. 17 | - etc. 18 | - et al. 19 | 20 | ```dart title="Bad" 21 | /// This is documentation. 22 | /// 23 | /// e.g. ... 24 | int function(){} 25 | ``` 26 | 27 | ```dart title="Good" 28 | /// This is documentation. 29 | /// 30 | /// For example ... 31 | int function(){} 32 | ``` 33 | 34 | ## Usage 35 | 36 | To enable the `avoid_abbreviations_in_doc_comments` rule, add `avoid_abbreviations_in_doc_comments` under custom_lint > rules in your `analysis_options.yaml` file: 37 | 38 | ```yaml 39 | custom_lint: 40 | rules: 41 | - avoid_abbreviations_in_doc_comments 42 | ``` 43 | 44 | ## Options 45 | 46 | | Option | Type | Description | Default Value | 47 | | --------------- | -------------- | ----------------------------------- |:-------------:| 48 | | `abbreviations` | `List` | The list of abbreviations to avoid. | `[]` | 49 | 50 | To configure the list of abbreviations, add a list of strings to the `abbreviations` option: 51 | 52 | ```yaml 53 | custom_lint: 54 | rules: 55 | - avoid_abbreviations_in_doc_comments: 56 | abbreviations: ['approx.'] 57 | ``` 58 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_const_constructor_declarations/prefer_const_constructor_declarations.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math show cos, sin; 2 | 3 | class Point { 4 | final double x; 5 | final double y; 6 | 7 | // expect_lint: prefer_const_constructor_declarations 8 | Point(this.x, this.y); 9 | 10 | // The lint will not be triggered because the redirecting constructor is not const. 11 | Point.origin() : this(0, 0); 12 | 13 | // expect_lint: prefer_const_constructor_declarations 14 | Point.origin2() : x = 0.0, y = 0.0; 15 | 16 | Point.fromPolarCoordinates({ 17 | required double radius, 18 | required double angle, 19 | }) : x = radius * math.cos(angle), 20 | y = radius * math.sin(angle); 21 | } 22 | 23 | class Point3D extends Point { 24 | final double z; 25 | 26 | // The lint will not be triggered because the super constructor is not const. 27 | Point3D(super.x, super.y, this.z); 28 | 29 | // The lint will not be triggered because the super constructor is not const. 30 | Point3D.origin() : z = 0, super.origin(); 31 | } 32 | 33 | class Coordinate { 34 | final double x; 35 | final double y; 36 | 37 | const Coordinate(this.x, this.y); 38 | 39 | const Coordinate.origin() : this(0, 0); 40 | } 41 | 42 | class Coordinate3D extends Coordinate { 43 | final double z; 44 | 45 | // expect_lint: prefer_const_constructor_declarations 46 | Coordinate3D(super.x, super.y, this.z); 47 | 48 | // expect_lint: prefer_const_constructor_declarations 49 | Coordinate3D.origin() : z = 0, super.origin(); 50 | } 51 | 52 | class A { 53 | // expect_lint: prefer_const_constructor_declarations 54 | A(); 55 | } 56 | -------------------------------------------------------------------------------- /packages/pyramid_lint/lib/src/lints/dart/prefer_async_await.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/error/error.dart'; 2 | import 'package:analyzer/error/listener.dart'; 3 | import 'package:custom_lint_builder/custom_lint_builder.dart'; 4 | 5 | import '../../pyramid_lint_rule.dart'; 6 | import '../../utils/constants.dart'; 7 | 8 | class PreferAsyncAwait extends PyramidLintRule { 9 | PreferAsyncAwait({required super.options}) 10 | : super( 11 | name: ruleName, 12 | problemMessage: 'Using Future.then() decreases readability.', 13 | correctionMessage: 'Consider using async/await instead.', 14 | url: url, 15 | errorSeverity: DiagnosticSeverity.INFO, 16 | ); 17 | 18 | static const ruleName = 'prefer_async_await'; 19 | static const url = '$dartLintDocUrl/$ruleName'; 20 | 21 | factory PreferAsyncAwait.fromConfigs(CustomLintConfigs configs) { 22 | final json = configs.rules[ruleName]?.json ?? {}; 23 | final options = PyramidLintRuleOptions.fromJson(json: json, paramsConverter: (_) => null); 24 | 25 | return PreferAsyncAwait(options: options); 26 | } 27 | 28 | @override 29 | void run( 30 | CustomLintResolver resolver, 31 | DiagnosticReporter reporter, 32 | CustomLintContext context, 33 | ) { 34 | context.registry.addMethodInvocation((node) { 35 | final target = node.realTarget; 36 | if (target == null) return; 37 | 38 | final targetType = target.staticType; 39 | if (targetType == null || !targetType.isDartAsyncFuture) return; 40 | 41 | final methodName = node.methodName.name; 42 | if (methodName != 'then') return; 43 | 44 | reporter.atNode(node, code); 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /docs/dart-lints/prefer_library_prefixes.mdx: -------------------------------------------------------------------------------- 1 | # prefer_library_prefixes 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ✅ | ✅ | 6 | 7 | ## Details 8 | 9 | **DO** use library prefixes to avoid name conflicts and increase code readability. 10 | 11 | Default libraries: 12 | 13 | - 'dart:developer' 14 | - 'dart:math' 15 | 16 | ```dart title="Bad" 17 | import 'dart:developer'; 18 | import 'dart:math'; 19 | 20 | print(log(e)); 21 | log('message'); 22 | ``` 23 | 24 | ```dart title="Good" 25 | import 'dart:developer' as developer; 26 | import 'dart:math' as math; 27 | 28 | print(math.log(math.e)); 29 | developer.log('message'); 30 | 31 | ``` 32 | 33 | ## Usage 34 | 35 | To enable the `prefer_library_prefixes` rule, add `prefer_library_prefixes` under custom_lint > rules in your `analysis_options.yaml` file: 36 | 37 | ```yaml 38 | custom_lint: 39 | rules: 40 | - prefer_library_prefixes 41 | ``` 42 | 43 | ## Options 44 | 45 | | Option | Type | Description | Default Value | 46 | | --------------------------- | -------------- | ------------------------------------- |:-------------:| 47 | | `include_default_libraries` | `bool` | Whether to include default libraries. | `true` | 48 | | `libraries` | `List` | A list of libraries to include. | `[]` | 49 | 50 | To configure the rule, add the optional `include_default_libraries` and `libraries` parameters: 51 | 52 | ```yaml 53 | custom_lint: 54 | rules: 55 | - prefer_library_prefixes: 56 | include_default_libraries: false 57 | libraries: ['package:http/http.dart'] 58 | ``` 59 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/dart/prefer_iterable_every/fix/prefer_iterable_every.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: unnecessary_lambdas, max_lines_for_function 2 | 3 | void fn(Iterable numbers) { 4 | // expect_lint: prefer_iterable_every 5 | numbers.where((n) => n.isEven).isEmpty; 6 | 7 | // expect_lint: prefer_iterable_every 8 | numbers.where((n) => n == 0).isEmpty; 9 | 10 | // expect_lint: prefer_iterable_every 11 | numbers.where((n) => n > 0).isEmpty; 12 | 13 | // expect_lint: prefer_iterable_every 14 | numbers.where((n) => n % 3 == 0).isEmpty; 15 | 16 | // expect_lint: prefer_iterable_every 17 | numbers.where((n) => isMultipleOfThree(n)).isEmpty; 18 | 19 | // expect_lint: prefer_iterable_every 20 | numbers.where(isMultipleOfThree).isEmpty; 21 | 22 | // expect_lint: prefer_iterable_every 23 | numbers.where((n) { 24 | return n.isEven; 25 | }).isEmpty; 26 | 27 | // expect_lint: prefer_iterable_every 28 | numbers.where((n) { 29 | return n == 0; 30 | }).isEmpty; 31 | 32 | // expect_lint: prefer_iterable_every 33 | numbers.where((n) { 34 | return n > 0; 35 | }).isEmpty; 36 | 37 | // expect_lint: prefer_iterable_every 38 | numbers.where((n) { 39 | return n % 3 == 0; 40 | }).isEmpty; 41 | 42 | // expect_lint: prefer_iterable_every 43 | numbers.where((n) { 44 | return isMultipleOfThree(n); 45 | }).isEmpty; 46 | 47 | // expect_lint: prefer_iterable_every 48 | numbers.where((n) { 49 | return !isMultipleOfThree(n); 50 | }).isEmpty; 51 | 52 | // expect_lint: prefer_iterable_every 53 | numbers.where((n) { 54 | return n % 3 == 0 && n % 7 == 0; 55 | }).isEmpty; 56 | } 57 | 58 | bool isMultipleOfThree(int number) => number % 3 == 0; 59 | -------------------------------------------------------------------------------- /packages/pyramid_lint/lib/src/lints/dart/no_self_comparisons.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:analyzer/error/error.dart'; 3 | import 'package:analyzer/error/listener.dart'; 4 | import 'package:custom_lint_builder/custom_lint_builder.dart'; 5 | 6 | import '../../pyramid_lint_rule.dart'; 7 | import '../../utils/constants.dart'; 8 | 9 | class NoSelfComparisons extends PyramidLintRule { 10 | NoSelfComparisons({required super.options}) 11 | : super( 12 | name: ruleName, 13 | problemMessage: 'Self comparison is usually a mistake.', 14 | correctionMessage: 'Consider changing the comparison to something else.', 15 | url: url, 16 | errorSeverity: DiagnosticSeverity.WARNING, 17 | ); 18 | 19 | static const ruleName = 'no_self_comparisons'; 20 | static const url = '$dartLintDocUrl/$ruleName'; 21 | 22 | factory NoSelfComparisons.fromConfigs(CustomLintConfigs configs) { 23 | final json = configs.rules[ruleName]?.json ?? {}; 24 | final options = PyramidLintRuleOptions.fromJson(json: json, paramsConverter: (_) => null); 25 | 26 | return NoSelfComparisons(options: options); 27 | } 28 | 29 | @override 30 | void run( 31 | CustomLintResolver resolver, 32 | DiagnosticReporter reporter, 33 | CustomLintContext context, 34 | ) { 35 | context.registry.addIfStatement((node) { 36 | final expression = node.expression; 37 | if (expression is! BinaryExpression) return; 38 | 39 | final left = expression.leftOperand.unParenthesized; 40 | final right = expression.rightOperand.unParenthesized; 41 | if (left.toSource() != right.toSource()) return; 42 | 43 | reporter.atNode(expression, code); 44 | }); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/pyramid_lint/lib/src/lints/dart/avoid_positional_fields_in_records.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/element/type.dart'; 2 | import 'package:analyzer/error/error.dart'; 3 | import 'package:analyzer/error/listener.dart'; 4 | import 'package:custom_lint_builder/custom_lint_builder.dart'; 5 | 6 | import '../../pyramid_lint_rule.dart'; 7 | import '../../utils/constants.dart'; 8 | 9 | class AvoidPositionalFieldsInRecords extends PyramidLintRule { 10 | AvoidPositionalFieldsInRecords({required super.options}) 11 | : super( 12 | name: ruleName, 13 | problemMessage: 'Using positional field getters decreases readability.', 14 | correctionMessage: 'Consider using named field getters instead.', 15 | url: url, 16 | errorSeverity: DiagnosticSeverity.INFO, 17 | ); 18 | 19 | static const ruleName = 'avoid_positional_fields_in_records'; 20 | static const url = '$dartLintDocUrl/$ruleName'; 21 | 22 | factory AvoidPositionalFieldsInRecords.fromConfigs(CustomLintConfigs configs) { 23 | final json = configs.rules[ruleName]?.json ?? {}; 24 | final options = PyramidLintRuleOptions.fromJson(json: json, paramsConverter: (_) => null); 25 | 26 | return AvoidPositionalFieldsInRecords(options: options); 27 | } 28 | 29 | @override 30 | void run( 31 | CustomLintResolver resolver, 32 | DiagnosticReporter reporter, 33 | CustomLintContext context, 34 | ) { 35 | context.registry.addPropertyAccess((node) { 36 | final targetType = node.realTarget.staticType; 37 | if (targetType is! RecordType) return; 38 | 39 | final propertyName = node.propertyName.name; 40 | if (!propertyName.startsWith(r'$')) return; 41 | 42 | reporter.atNode(node, code); 43 | }); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/pyramid_lint/lib/src/assists/flutter/wrap_with_layout_builder.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/source/source_range.dart'; 2 | import 'package:custom_lint_builder/custom_lint_builder.dart'; 3 | 4 | import '../../utils/ast_node_extensions.dart'; 5 | import '../../utils/pubspec_extension.dart'; 6 | import '../../utils/type_checker.dart'; 7 | import '../../utils/utils.dart'; 8 | 9 | class WrapWithLayoutBuilder extends DartAssist { 10 | @override 11 | Future run( 12 | CustomLintResolver resolver, 13 | ChangeReporter reporter, 14 | CustomLintContext context, 15 | SourceRange target, 16 | ) async { 17 | if (!context.pubspec.isFlutterProject) return; 18 | 19 | context.registry.addInstanceCreationExpression((node) { 20 | final sourceRange = node.keywordAndConstructorNameSourceRange; 21 | if (!sourceRange.covers(target)) return; 22 | 23 | final type = node.staticType; 24 | if (type == null || !widgetChecker.isSuperTypeOf(type)) return; 25 | 26 | if (layoutBuilderChecker.isExactlyType(type)) return; 27 | 28 | final parentWidget = findParentWidget(node); 29 | if (parentWidget != null) { 30 | final parentType = parentWidget.staticType; 31 | if (parentType != null && layoutBuilderChecker.isExactlyType(parentType)) { 32 | return; 33 | } 34 | } 35 | 36 | final changeBuilder = reporter.createChangeBuilder( 37 | message: 'Wrap with LayoutBuilder', 38 | priority: 29, 39 | ); 40 | 41 | changeBuilder.addDartFileEdit((builder) { 42 | builder.addSimpleInsertion(node.offset, 'LayoutBuilder(builder: (context, constraints) { return '); 43 | builder.addSimpleInsertion(node.end, '; },)'); 44 | }); 45 | }); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /docs/flutter-lints/prefer_dedicated_media_query_functions.mdx: -------------------------------------------------------------------------------- 1 | # prefer_dedicated_media_query_functions 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **PREFER** using dedicated MediaQuery functions instead of accessing the properties from MediaQuery.of(context) or MediaQuery.maybeOf(context). 10 | 11 | Using MediaQuery.of(context) or MediaQuery.maybeOf(context) to access below properties will cause unnecessary rebuilds. 12 | 13 | Properties: 14 | 15 | - accessibleNavigation 16 | - alwaysUse24HourFormat 17 | - boldText 18 | - devicePixelRatio 19 | - disableAnimations 20 | - displayFeatures 21 | - gestureSettings 22 | - highContrast 23 | - invertColors 24 | - navigationMode 25 | - onOffSwitchLabels 26 | - orientation 27 | - padding 28 | - platformBrightness 29 | - size 30 | - systemGestureInsets 31 | - textScaleFactor (deprecated after v3.12.0-2.0.pre) 32 | - textScaler 33 | - viewInsets 34 | - viewPadding 35 | 36 | ```dart title="Bad" 37 | final size = MediaQuery.of(context).size; 38 | final orientation = MediaQuery.maybeOf(context)?.orientation; 39 | 40 | final mediaQuery = MediaQuery.of(context); 41 | final platformBrightness = mediaQuery.platformBrightness; 42 | ``` 43 | 44 | Good 45 | 46 | ```dart title="Good" 47 | final size = MediaQuery.sizeOf(context); 48 | final orientation = MediaQuery.maybeOrientationOf(context); 49 | 50 | final platformBrightness = MediaQuery.platformBrightnessOf(context); 51 | ``` 52 | 53 | ## Usage 54 | 55 | To enable the `prefer_dedicated_media_query_functions` rule, add `prefer_dedicated_media_query_functions` under custom_lint > rules in your `analysis_options.yaml` file: 56 | 57 | ```yaml 58 | custom_lint: 59 | rules: 60 | - prefer_dedicated_media_query_functions 61 | ``` 62 | -------------------------------------------------------------------------------- /packages/pyramid_lint/lib/src/pyramid_lint_rule.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/error/error.dart' hide LintCode; 2 | import 'package:custom_lint_builder/custom_lint_builder.dart'; 3 | import 'package:meta/meta.dart' show immutable; 4 | 5 | import 'utils/typedef.dart'; 6 | 7 | @immutable 8 | abstract class PyramidLintRule extends DartLintRule { 9 | PyramidLintRule({ 10 | required String name, 11 | required String problemMessage, 12 | required String correctionMessage, 13 | required String url, 14 | required DiagnosticSeverity errorSeverity, 15 | required this.options, 16 | }) : super( 17 | code: LintCode( 18 | name: name, 19 | problemMessage: problemMessage, 20 | correctionMessage: correctionMessage, 21 | url: url, 22 | errorSeverity: options.severity ?? errorSeverity, 23 | ), 24 | ); 25 | 26 | final PyramidLintRuleOptions options; 27 | 28 | @override 29 | bool get enabledByDefault => false; 30 | } 31 | 32 | @immutable 33 | class PyramidLintRuleOptions { 34 | final T params; 35 | final DiagnosticSeverity? severity; 36 | 37 | const PyramidLintRuleOptions({ 38 | required this.params, 39 | this.severity, 40 | }); 41 | 42 | factory PyramidLintRuleOptions.fromJson({ 43 | required Json json, 44 | required T Function(Json json) paramsConverter, 45 | }) { 46 | final params = paramsConverter(json); 47 | final severity = switch (json['severity']) { 48 | 'info' => DiagnosticSeverity.INFO, 49 | 'warning' => DiagnosticSeverity.WARNING, 50 | 'error' => DiagnosticSeverity.ERROR, 51 | _ => null, 52 | }; 53 | 54 | return PyramidLintRuleOptions( 55 | params: params, 56 | severity: severity, 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/utils/version_constraint_extension_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:pub_semver/pub_semver.dart'; 2 | import 'package:pyramid_lint/src/utils/version_constraint_extension.dart'; 3 | import 'package:test/test.dart'; 4 | 5 | void main() { 6 | group('VersionConstraintExtension', () { 7 | test( 8 | 'meetMinimumRequiredVersion should return true if the version meets the minimum required version', 9 | () { 10 | final minVersion = Version.parse('3.0.0'); 11 | 12 | var version = VersionConstraint.parse('>=3.3.0 <4.0.0'); 13 | expect(version.meetMinimumRequiredVersion(minVersion), isTrue); 14 | 15 | version = VersionConstraint.parse('^3.3.0'); 16 | expect(version.meetMinimumRequiredVersion(minVersion), isTrue); 17 | 18 | version = VersionConstraint.parse('>=3.3.0'); 19 | expect(version.meetMinimumRequiredVersion(minVersion), isTrue); 20 | }, 21 | ); 22 | 23 | test( 24 | 'meetMinimumRequiredVersion should return false if the version does not meet the minimum required version', 25 | () { 26 | final minVersion = Version.parse('3.0.0'); 27 | 28 | var version = VersionConstraint.parse('>=2.10.0 <4.0.0'); 29 | expect(version.meetMinimumRequiredVersion(minVersion), isFalse); 30 | 31 | version = VersionConstraint.parse('^2.0.0'); 32 | expect(version.meetMinimumRequiredVersion(minVersion), isFalse); 33 | 34 | version = VersionConstraint.parse('<4.0.0'); 35 | expect(version.meetMinimumRequiredVersion(minVersion), isFalse); 36 | 37 | version = VersionConstraint.any; 38 | expect(version.meetMinimumRequiredVersion(minVersion), isFalse); 39 | 40 | version = VersionConstraint.empty; 41 | expect(version.meetMinimumRequiredVersion(minVersion), isFalse); 42 | }, 43 | ); 44 | }); 45 | } 46 | -------------------------------------------------------------------------------- /docs/dart-lints/boolean_prefixes.mdx: -------------------------------------------------------------------------------- 1 | # boolean_prefixes 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ❌ | ✅ | 6 | 7 | ## Details 8 | 9 | **DO** prefix boolean variables, functions and getters with specific verbs. 10 | 11 | see: [effective-dart](https://dart.dev/effective-dart/design#prefer-a-non-imperative-verb-phrase-for-a-boolean-property-or-variable) 12 | 13 | Default valid prefixes: 14 | 15 | - is 16 | - are 17 | - was 18 | - were 19 | - has 20 | - have 21 | - had 22 | - can 23 | - should 24 | - will 25 | - do 26 | - does 27 | - did 28 | 29 | ```dart title="Bad" 30 | class Point { 31 | final double x; 32 | final double y; 33 | 34 | const Point(this.x, this.y); 35 | 36 | bool get origin => x == 0 && y == 0; 37 | 38 | bool samePoint(Point other) => x == other.x && y == other.y; 39 | } 40 | ``` 41 | 42 | ```dart title="Good" 43 | class Point { 44 | final double x; 45 | final double y; 46 | 47 | const Point(this.x, this.y); 48 | 49 | bool get isOrigin => x == 0 && y == 0; 50 | 51 | bool isSamePoint(Point other) => x == other.x && y == other.y; 52 | } 53 | ``` 54 | 55 | ## Usage 56 | 57 | To enable the `boolean_prefixes` rule, add `boolean_prefixes` under custom_lint > rules in your `analysis_options.yaml` file: 58 | 59 | ```yaml 60 | custom_lint: 61 | rules: 62 | - boolean_prefixes 63 | ``` 64 | 65 | ## Options 66 | 67 | | Option | Type | Description | Default Value | 68 | | ---------------- | -------------- | ------------------------- |:-------------:| 69 | | `valid_prefixes` | `List` | A list of valid prefixes. | `[]` | 70 | 71 | To configure the list of valid prefixes, add a list of strings to the `valid_prefixes` option: 72 | 73 | ```yaml 74 | custom_lint: 75 | rules: 76 | - boolean_prefixes: 77 | valid_prefixes: ['is', 'has'] 78 | ``` 79 | -------------------------------------------------------------------------------- /packages/pyramid_lint/lib/src/lints/dart/no_duplicate_imports.dart: -------------------------------------------------------------------------------- 1 | import 'package:analyzer/dart/ast/ast.dart'; 2 | import 'package:analyzer/error/error.dart'; 3 | import 'package:analyzer/error/listener.dart'; 4 | import 'package:custom_lint_builder/custom_lint_builder.dart'; 5 | 6 | import '../../pyramid_lint_rule.dart'; 7 | import '../../utils/constants.dart'; 8 | import '../../utils/iterable_extension.dart'; 9 | 10 | class NoDuplicateImports extends PyramidLintRule { 11 | NoDuplicateImports({required super.options}) 12 | : super( 13 | name: ruleName, 14 | problemMessage: 'Duplicate imports can lead to confusion.', 15 | correctionMessage: 'Consider combining or removing the duplicate imports.', 16 | url: url, 17 | errorSeverity: DiagnosticSeverity.INFO, 18 | ); 19 | 20 | static const ruleName = 'no_duplicate_imports'; 21 | static const url = '$dartLintDocUrl/$ruleName'; 22 | 23 | factory NoDuplicateImports.fromConfigs(CustomLintConfigs configs) { 24 | final json = configs.rules[ruleName]?.json ?? {}; 25 | final options = PyramidLintRuleOptions.fromJson(json: json, paramsConverter: (_) => null); 26 | 27 | return NoDuplicateImports(options: options); 28 | } 29 | 30 | @override 31 | void run( 32 | CustomLintResolver resolver, 33 | DiagnosticReporter reporter, 34 | CustomLintContext context, 35 | ) { 36 | context.registry.addCompilationUnit((node) { 37 | final importDirectives = node.directives.whereType(); 38 | final duplicateUrls = importDirectives.map((e) => e.uri.stringValue).nonNulls.duplicates; 39 | 40 | for (final importDirective in importDirectives) { 41 | final url = importDirective.uri.stringValue; 42 | if (duplicateUrls.contains(url)) { 43 | reporter.atNode(importDirective.uri, code); 44 | } 45 | } 46 | }); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/assists/flutter/use_edge_insets_zero/use_edge_insets_zero.diff: -------------------------------------------------------------------------------- 1 | Message: `Replace with EdgeInsets.zero` 2 | Priority: 80 3 | Diff for file `test/assists/flutter/use_edge_insets_zero/use_edge_insets_zero.dart:5`: 4 | ``` 5 | import 'package:flutter/painting.dart'; 6 | 7 | - const edgeInsets1 = EdgeInsets.all(0); 8 | + const edgeInsets1 = EdgeInsets.zero; 9 | const edgeInsets2 = EdgeInsets.fromLTRB(0, 0, 0, 0); 10 | const edgeInsets4 = EdgeInsets.only(left: 0, top: 0, right: 0, bottom: 0); 11 | ``` 12 | --- 13 | Message: `Replace with EdgeInsets.zero` 14 | Priority: 80 15 | Diff for file `test/assists/flutter/use_edge_insets_zero/use_edge_insets_zero.dart:6`: 16 | ``` 17 | 18 | const edgeInsets1 = EdgeInsets.all(0); 19 | - const edgeInsets2 = EdgeInsets.fromLTRB(0, 0, 0, 0); 20 | + const edgeInsets2 = EdgeInsets.zero; 21 | const edgeInsets4 = EdgeInsets.only(left: 0, top: 0, right: 0, bottom: 0); 22 | const edgeInsets3 = EdgeInsets.symmetric(vertical: 0, horizontal: 0); 23 | ``` 24 | --- 25 | Message: `Replace with EdgeInsets.zero` 26 | Priority: 80 27 | Diff for file `test/assists/flutter/use_edge_insets_zero/use_edge_insets_zero.dart:7`: 28 | ``` 29 | const edgeInsets1 = EdgeInsets.all(0); 30 | const edgeInsets2 = EdgeInsets.fromLTRB(0, 0, 0, 0); 31 | - const edgeInsets4 = EdgeInsets.only(left: 0, top: 0, right: 0, bottom: 0); 32 | + const edgeInsets4 = EdgeInsets.zero; 33 | const edgeInsets3 = EdgeInsets.symmetric(vertical: 0, horizontal: 0); 34 | ``` 35 | --- 36 | Message: `Replace with EdgeInsets.zero` 37 | Priority: 80 38 | Diff for file `test/assists/flutter/use_edge_insets_zero/use_edge_insets_zero.dart:8`: 39 | ``` 40 | const edgeInsets2 = EdgeInsets.fromLTRB(0, 0, 0, 0); 41 | const edgeInsets4 = EdgeInsets.only(left: 0, top: 0, right: 0, bottom: 0); 42 | - const edgeInsets3 = EdgeInsets.symmetric(vertical: 0, horizontal: 0); 43 | + const edgeInsets3 = EdgeInsets.zero; 44 | ``` 45 | --- 46 | -------------------------------------------------------------------------------- /docs/flutter-lints/proper_edge_insets_constructors.mdx: -------------------------------------------------------------------------------- 1 | # proper_edge_insets_constructors 2 | 3 | | Severity | Quick Fix | Options | 4 | |:--------:|:---------:|:-------:| 5 | | Info | ✅ | ❌ | 6 | 7 | ## Details 8 | 9 | **PREFER** using correct EdgeInsets constructor. 10 | 11 | ```dart title="Bad" 12 | padding = const EdgeInsets.fromLTRB(8, 8, 8, 8); 13 | padding = const EdgeInsets.fromLTRB(8, 0, 8, 0); 14 | padding = const EdgeInsets.fromLTRB(8, 4, 8, 4); 15 | padding = const EdgeInsets.fromLTRB(8, 4, 8, 0); 16 | 17 | padding = const EdgeInsets.only(left: 8, top: 8, right: 8, bottom: 8); 18 | padding = const EdgeInsets.only(left: 8, top: 0, right: 8, bottom: 0); 19 | padding = const EdgeInsets.only(left: 8, top: 4, right: 8, bottom: 4); 20 | padding = const EdgeInsets.only(left: 2, top: 4, right: 6, bottom: 8); 21 | 22 | padding = const EdgeInsets.symmetric(horizontal: 0, vertical: 0); 23 | padding = const EdgeInsets.symmetric(horizontal: 8, vertical: 8); 24 | padding = const EdgeInsets.symmetric(horizontal: 8, vertical: 0); 25 | ``` 26 | 27 | ```dart title="Good" 28 | padding = const EdgeInsets.all(8); 29 | padding = const EdgeInsets.symmetric(horizontal: 8); 30 | padding = const EdgeInsets.symmetric(horizontal: 8, vertical: 4); 31 | padding = const EdgeInsets.only(left: 8, top: 4, right: 8); 32 | 33 | padding = const EdgeInsets.all(8); 34 | padding = const EdgeInsets.symmetric(horizontal: 8); 35 | padding = const EdgeInsets.symmetric(horizontal: 8, vertical: 4); 36 | padding = const EdgeInsets.fromLTRB(2, 4, 6, 8); 37 | 38 | padding = const EdgeInsets.all(8); 39 | padding = const EdgeInsets.symmetric(horizontal: 8); 40 | ``` 41 | 42 | ## Usage 43 | 44 | To enable the `proper_edge_insets_constructors` rule, add `proper_edge_insets_constructors` under custom_lint > rules in your `analysis_options.yaml` file: 45 | 46 | ```yaml 47 | custom_lint: 48 | rules: 49 | - proper_edge_insets_constructors 50 | ``` 51 | -------------------------------------------------------------------------------- /packages/pyramid_lint_test/test/lints/flutter/avoid_returning_widgets.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: avoid_public_members_in_states 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class A extends StatelessWidget { 6 | const A({super.key}); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return _buildWidget(); 11 | } 12 | 13 | // expect_lint: avoid_returning_widgets 14 | Widget _buildWidget() { 15 | return const Placeholder(); 16 | } 17 | } 18 | 19 | class B extends StatefulWidget { 20 | const B({super.key}); 21 | 22 | @override 23 | State createState() => _BState(); 24 | } 25 | 26 | class _BState extends State { 27 | // expect_lint: avoid_returning_widgets 28 | Widget get myWidget => const Placeholder(); 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | return Column( 33 | children: [ 34 | myWidget, 35 | _buildWidget(), 36 | ], 37 | ); 38 | } 39 | 40 | // expect_lint: avoid_returning_widgets 41 | Widget _buildWidget() => const Placeholder(); 42 | } 43 | 44 | // expect_lint: avoid_returning_widgets 45 | Widget buildWidget() => const Placeholder(); 46 | 47 | // expect_lint: avoid_returning_widgets 48 | Widget get myWidget => const Placeholder(); 49 | 50 | class MyInheritedWidget extends InheritedWidget { 51 | const MyInheritedWidget({ 52 | super.key, 53 | required super.child, 54 | }); 55 | 56 | // Static methods are ignored. 57 | static MyInheritedWidget? of(BuildContext context) { 58 | return context.dependOnInheritedWidgetOfExactType(); 59 | } 60 | 61 | @override 62 | bool updateShouldNotify(MyInheritedWidget oldWidget) => false; 63 | } 64 | 65 | Widget myMethod() { 66 | return const Placeholder(); 67 | } 68 | 69 | extension WidgetExtension on Widget { 70 | // Extension methods are ignored. 71 | Widget extensionMethod() { 72 | return const Placeholder(); 73 | } 74 | } 75 | --------------------------------------------------------------------------------