From 4829499b3fa71a436577bfd9e4e9af98b9bb8956 Mon Sep 17 00:00:00 2001 From: milkyway-12 Date: Mon, 8 Sep 2025 14:18:40 +0300 Subject: [PATCH 1/2] Fix #192: Add analyzer.plugins setup to documentation and test configuration --- README.md | 4 ++++ lib/analysis_options_test.yaml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/README.md b/README.md index 02d62c85..a26b963d 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,10 @@ Add an `analysis_options.yaml` file under the `test/` directory, and include the ```yaml include: package:solid_lints/analysis_options_test.yaml + +analyzer: + plugins: + - custom_lint ``` Then you can see suggestions in your IDE or you can run checks manually: diff --git a/lib/analysis_options_test.yaml b/lib/analysis_options_test.yaml index 0acf73b8..b98534ae 100644 --- a/lib/analysis_options_test.yaml +++ b/lib/analysis_options_test.yaml @@ -1,5 +1,9 @@ include: package:solid_lints/analysis_options.yaml +analyzer: + plugins: + - custom_lint + custom_lint: rules: # Tests usually organized in one large main() function making this rule not applicable. From 050676a0b5c097679b0d6af873ae78ceb32f766b Mon Sep 17 00:00:00 2001 From: milkyway-12 Date: Tue, 9 Sep 2025 09:12:03 +0300 Subject: [PATCH 2/2] Add hierarchical analysis_options.yaml support for test files --- .../avoid_debug_print_in_release_rule.dart | 2 +- .../avoid_final_with_getter_rule.dart | 2 +- .../avoid_global_state_rule.dart | 2 +- .../avoid_late_keyword_rule.dart | 2 +- .../avoid_non_null_assertion_rule.dart | 2 +- .../avoid_returning_widgets_rule.dart | 2 +- .../avoid_unnecessary_set_state_rule.dart | 2 +- ...void_unnecessary_type_assertions_rule.dart | 2 +- .../avoid_unnecessary_type_casts_rule.dart | 2 +- .../avoid_unrelated_type_assertions_rule.dart | 2 +- .../avoid_unused_parameters_rule.dart | 2 +- .../avoid_using_api/avoid_using_api_rule.dart | 2 +- .../cyclomatic_complexity_rule.dart | 2 +- .../double_literal_format_rule.dart | 2 +- .../function_lines_of_code_rule.dart | 2 +- .../member_ordering/member_ordering_rule.dart | 2 +- .../named_parameters_ordering_rule.dart | 2 +- .../newline_before_return_rule.dart | 2 +- .../no_empty_block/no_empty_block_rule.dart | 2 +- .../no_equal_then_else_rule.dart | 2 +- .../no_magic_number/no_magic_number_rule.dart | 2 +- .../number_of_parameters_rule.dart | 2 +- .../prefer_conditional_expressions_rule.dart | 2 +- .../prefer_early_return_rule.dart | 2 +- .../lints/prefer_first/prefer_first_rule.dart | 2 +- .../lints/prefer_last/prefer_last_rule.dart | 2 +- .../prefer_match_file_name_rule.dart | 2 +- .../proper_super_calls_rule.dart | 2 +- lib/src/models/solid_lint_rule.dart | 75 ++++++++++++++++++- lib/src/utils/path_utils.dart | 52 +++++++++++++ 30 files changed, 154 insertions(+), 29 deletions(-) diff --git a/lib/src/lints/avoid_debug_print_in_release/avoid_debug_print_in_release_rule.dart b/lib/src/lints/avoid_debug_print_in_release/avoid_debug_print_in_release_rule.dart index fc951135..c0ca24e6 100644 --- a/lib/src/lints/avoid_debug_print_in_release/avoid_debug_print_in_release_rule.dart +++ b/lib/src/lints/avoid_debug_print_in_release/avoid_debug_print_in_release_rule.dart @@ -59,7 +59,7 @@ your `debugPrint` call in a `!kReleaseMode` check.""", } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/avoid_final_with_getter/avoid_final_with_getter_rule.dart b/lib/src/lints/avoid_final_with_getter/avoid_final_with_getter_rule.dart index fa844a96..f5e99fe4 100644 --- a/lib/src/lints/avoid_final_with_getter/avoid_final_with_getter_rule.dart +++ b/lib/src/lints/avoid_final_with_getter/avoid_final_with_getter_rule.dart @@ -54,7 +54,7 @@ class AvoidFinalWithGetterRule extends SolidLintRule { } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/avoid_global_state/avoid_global_state_rule.dart b/lib/src/lints/avoid_global_state/avoid_global_state_rule.dart index 8c62c0ec..a4ae6cc2 100644 --- a/lib/src/lints/avoid_global_state/avoid_global_state_rule.dart +++ b/lib/src/lints/avoid_global_state/avoid_global_state_rule.dart @@ -55,7 +55,7 @@ class AvoidGlobalStateRule extends SolidLintRule { } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/avoid_late_keyword/avoid_late_keyword_rule.dart b/lib/src/lints/avoid_late_keyword/avoid_late_keyword_rule.dart index e42c7fd6..75ed4380 100644 --- a/lib/src/lints/avoid_late_keyword/avoid_late_keyword_rule.dart +++ b/lib/src/lints/avoid_late_keyword/avoid_late_keyword_rule.dart @@ -69,7 +69,7 @@ class AvoidLateKeywordRule extends SolidLintRule { } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/avoid_non_null_assertion/avoid_non_null_assertion_rule.dart b/lib/src/lints/avoid_non_null_assertion/avoid_non_null_assertion_rule.dart index e7c93285..974c0e8c 100644 --- a/lib/src/lints/avoid_non_null_assertion/avoid_non_null_assertion_rule.dart +++ b/lib/src/lints/avoid_non_null_assertion/avoid_non_null_assertion_rule.dart @@ -58,7 +58,7 @@ class AvoidNonNullAssertionRule extends SolidLintRule { } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/avoid_returning_widgets/avoid_returning_widgets_rule.dart b/lib/src/lints/avoid_returning_widgets/avoid_returning_widgets_rule.dart index 0e5d6b43..b75a0b96 100644 --- a/lib/src/lints/avoid_returning_widgets/avoid_returning_widgets_rule.dart +++ b/lib/src/lints/avoid_returning_widgets/avoid_returning_widgets_rule.dart @@ -75,7 +75,7 @@ class AvoidReturningWidgetsRule } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/avoid_unnecessary_setstate/avoid_unnecessary_set_state_rule.dart b/lib/src/lints/avoid_unnecessary_setstate/avoid_unnecessary_set_state_rule.dart index 37d7113f..c30d9b7a 100644 --- a/lib/src/lints/avoid_unnecessary_setstate/avoid_unnecessary_set_state_rule.dart +++ b/lib/src/lints/avoid_unnecessary_setstate/avoid_unnecessary_set_state_rule.dart @@ -75,7 +75,7 @@ class AvoidUnnecessarySetStateRule extends SolidLintRule { } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/avoid_unnecessary_type_assertions/avoid_unnecessary_type_assertions_rule.dart b/lib/src/lints/avoid_unnecessary_type_assertions/avoid_unnecessary_type_assertions_rule.dart index 826a44aa..f1849cfc 100644 --- a/lib/src/lints/avoid_unnecessary_type_assertions/avoid_unnecessary_type_assertions_rule.dart +++ b/lib/src/lints/avoid_unnecessary_type_assertions/avoid_unnecessary_type_assertions_rule.dart @@ -70,7 +70,7 @@ class AvoidUnnecessaryTypeAssertions extends SolidLintRule { } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/avoid_unnecessary_type_casts/avoid_unnecessary_type_casts_rule.dart b/lib/src/lints/avoid_unnecessary_type_casts/avoid_unnecessary_type_casts_rule.dart index d90d1629..d8bf4f7c 100644 --- a/lib/src/lints/avoid_unnecessary_type_casts/avoid_unnecessary_type_casts_rule.dart +++ b/lib/src/lints/avoid_unnecessary_type_casts/avoid_unnecessary_type_casts_rule.dart @@ -31,7 +31,7 @@ class AvoidUnnecessaryTypeCastsRule extends SolidLintRule { } @override - void run( + void runRule( CustomLintResolver resolver, error_listener.ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule.dart b/lib/src/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule.dart index c1745eed..d1789ba7 100644 --- a/lib/src/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule.dart +++ b/lib/src/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule.dart @@ -29,7 +29,7 @@ class AvoidUnrelatedTypeAssertionsRule extends SolidLintRule { } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/avoid_unused_parameters/avoid_unused_parameters_rule.dart b/lib/src/lints/avoid_unused_parameters/avoid_unused_parameters_rule.dart index c80ef2b7..514884bd 100644 --- a/lib/src/lints/avoid_unused_parameters/avoid_unused_parameters_rule.dart +++ b/lib/src/lints/avoid_unused_parameters/avoid_unused_parameters_rule.dart @@ -88,7 +88,7 @@ class AvoidUnusedParametersRule extends SolidLintRule { } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/avoid_using_api/avoid_using_api_rule.dart b/lib/src/lints/avoid_using_api/avoid_using_api_rule.dart index 2e96d221..d6b4477e 100644 --- a/lib/src/lints/avoid_using_api/avoid_using_api_rule.dart +++ b/lib/src/lints/avoid_using_api/avoid_using_api_rule.dart @@ -87,7 +87,7 @@ class AvoidUsingApiRule extends SolidLintRule { } @override - Future run( + Future runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/cyclomatic_complexity/cyclomatic_complexity_rule.dart b/lib/src/lints/cyclomatic_complexity/cyclomatic_complexity_rule.dart index a0a30fae..24c23445 100644 --- a/lib/src/lints/cyclomatic_complexity/cyclomatic_complexity_rule.dart +++ b/lib/src/lints/cyclomatic_complexity/cyclomatic_complexity_rule.dart @@ -46,7 +46,7 @@ class CyclomaticComplexityRule } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/double_literal_format/double_literal_format_rule.dart b/lib/src/lints/double_literal_format/double_literal_format_rule.dart index 8a8f7d00..975260ee 100644 --- a/lib/src/lints/double_literal_format/double_literal_format_rule.dart +++ b/lib/src/lints/double_literal_format/double_literal_format_rule.dart @@ -72,7 +72,7 @@ class DoubleLiteralFormatRule extends SolidLintRule { } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/function_lines_of_code/function_lines_of_code_rule.dart b/lib/src/lints/function_lines_of_code/function_lines_of_code_rule.dart index e77a3590..0b11492a 100644 --- a/lib/src/lints/function_lines_of_code/function_lines_of_code_rule.dart +++ b/lib/src/lints/function_lines_of_code/function_lines_of_code_rule.dart @@ -43,7 +43,7 @@ class FunctionLinesOfCodeRule } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/member_ordering/member_ordering_rule.dart b/lib/src/lints/member_ordering/member_ordering_rule.dart index fe59b027..56a9cafd 100644 --- a/lib/src/lints/member_ordering/member_ordering_rule.dart +++ b/lib/src/lints/member_ordering/member_ordering_rule.dart @@ -108,7 +108,7 @@ class MemberOrderingRule extends SolidLintRule { } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/named_parameters_ordering/named_parameters_ordering_rule.dart b/lib/src/lints/named_parameters_ordering/named_parameters_ordering_rule.dart index 013c19cd..59ead9aa 100644 --- a/lib/src/lints/named_parameters_ordering/named_parameters_ordering_rule.dart +++ b/lib/src/lints/named_parameters_ordering/named_parameters_ordering_rule.dart @@ -123,7 +123,7 @@ class NamedParametersOrderingRule } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/newline_before_return/newline_before_return_rule.dart b/lib/src/lints/newline_before_return/newline_before_return_rule.dart index 1c0fd4fa..ef9c1cef 100644 --- a/lib/src/lints/newline_before_return/newline_before_return_rule.dart +++ b/lib/src/lints/newline_before_return/newline_before_return_rule.dart @@ -92,7 +92,7 @@ class NewlineBeforeReturnRule extends SolidLintRule { } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/no_empty_block/no_empty_block_rule.dart b/lib/src/lints/no_empty_block/no_empty_block_rule.dart index 546e0eac..4213ae3e 100644 --- a/lib/src/lints/no_empty_block/no_empty_block_rule.dart +++ b/lib/src/lints/no_empty_block/no_empty_block_rule.dart @@ -72,7 +72,7 @@ class NoEmptyBlockRule extends SolidLintRule { } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/no_equal_then_else/no_equal_then_else_rule.dart b/lib/src/lints/no_equal_then_else/no_equal_then_else_rule.dart index f9eddd7f..aee6cb80 100644 --- a/lib/src/lints/no_equal_then_else/no_equal_then_else_rule.dart +++ b/lib/src/lints/no_equal_then_else/no_equal_then_else_rule.dart @@ -61,7 +61,7 @@ class NoEqualThenElseRule extends SolidLintRule { } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/no_magic_number/no_magic_number_rule.dart b/lib/src/lints/no_magic_number/no_magic_number_rule.dart index 2eda1787..baf89fc6 100644 --- a/lib/src/lints/no_magic_number/no_magic_number_rule.dart +++ b/lib/src/lints/no_magic_number/no_magic_number_rule.dart @@ -155,7 +155,7 @@ class NoMagicNumberRule extends SolidLintRule { } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/number_of_parameters/number_of_parameters_rule.dart b/lib/src/lints/number_of_parameters/number_of_parameters_rule.dart index f03a9c7a..c270649a 100644 --- a/lib/src/lints/number_of_parameters/number_of_parameters_rule.dart +++ b/lib/src/lints/number_of_parameters/number_of_parameters_rule.dart @@ -59,7 +59,7 @@ class NumberOfParametersRule } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/prefer_conditional_expressions/prefer_conditional_expressions_rule.dart b/lib/src/lints/prefer_conditional_expressions/prefer_conditional_expressions_rule.dart index 499d0e35..e14f2ba9 100644 --- a/lib/src/lints/prefer_conditional_expressions/prefer_conditional_expressions_rule.dart +++ b/lib/src/lints/prefer_conditional_expressions/prefer_conditional_expressions_rule.dart @@ -79,7 +79,7 @@ class PreferConditionalExpressionsRule } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/prefer_early_return/prefer_early_return_rule.dart b/lib/src/lints/prefer_early_return/prefer_early_return_rule.dart index 2c43c2c2..cee4b680 100644 --- a/lib/src/lints/prefer_early_return/prefer_early_return_rule.dart +++ b/lib/src/lints/prefer_early_return/prefer_early_return_rule.dart @@ -51,7 +51,7 @@ class PreferEarlyReturnRule extends SolidLintRule { } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/prefer_first/prefer_first_rule.dart b/lib/src/lints/prefer_first/prefer_first_rule.dart index 1779780d..2c29ded9 100644 --- a/lib/src/lints/prefer_first/prefer_first_rule.dart +++ b/lib/src/lints/prefer_first/prefer_first_rule.dart @@ -47,7 +47,7 @@ class PreferFirstRule extends SolidLintRule { } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/prefer_last/prefer_last_rule.dart b/lib/src/lints/prefer_last/prefer_last_rule.dart index b7c2beca..9265fe70 100644 --- a/lib/src/lints/prefer_last/prefer_last_rule.dart +++ b/lib/src/lints/prefer_last/prefer_last_rule.dart @@ -47,7 +47,7 @@ class PreferLastRule extends SolidLintRule { } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/prefer_match_file_name/prefer_match_file_name_rule.dart b/lib/src/lints/prefer_match_file_name/prefer_match_file_name_rule.dart index 66955581..de5d76f8 100644 --- a/lib/src/lints/prefer_match_file_name/prefer_match_file_name_rule.dart +++ b/lib/src/lints/prefer_match_file_name/prefer_match_file_name_rule.dart @@ -73,7 +73,7 @@ class PreferMatchFileNameRule } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/lints/proper_super_calls/proper_super_calls_rule.dart b/lib/src/lints/proper_super_calls/proper_super_calls_rule.dart index 0fa3b94d..ed95b87f 100644 --- a/lib/src/lints/proper_super_calls/proper_super_calls_rule.dart +++ b/lib/src/lints/proper_super_calls/proper_super_calls_rule.dart @@ -81,7 +81,7 @@ class ProperSuperCallsRule extends SolidLintRule { } @override - void run( + void runRule( CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context, diff --git a/lib/src/models/solid_lint_rule.dart b/lib/src/models/solid_lint_rule.dart index a536eb4f..35f43528 100644 --- a/lib/src/models/solid_lint_rule.dart +++ b/lib/src/models/solid_lint_rule.dart @@ -1,5 +1,10 @@ +import 'dart:io'; + +import 'package:analyzer/error/listener.dart'; import 'package:custom_lint_builder/custom_lint_builder.dart'; import 'package:solid_lints/src/models/rule_config.dart'; +import 'package:solid_lints/src/utils/path_utils.dart'; +import 'package:yaml/yaml.dart'; /// A base class for emitting information about /// issues with user's `.dart` files. @@ -11,6 +16,74 @@ abstract class SolidLintRule extends DartLintRule { /// defined custom parameters. final RuleConfig config; - /// A flag which indicates whether this rule was enabled by the user. + /// A flag which indicates whether this rule was enabled by the user (globally). bool get enabled => config.enabled; + + @override + void run(CustomLintResolver resolver, + ErrorReporter reporter, + CustomLintContext context,) { + if (!isEnabledForFile(resolver.path)) return; + runRule(resolver, reporter, context); + } + + /// Override this method instead of run() in your rule implementations + void runRule(CustomLintResolver resolver, + ErrorReporter reporter, + CustomLintContext context,); + + /// Determines if the rule should run for the given file. + bool isEnabledForFile(String filePath) { + if (!enabled) return false; + if (_isTestFile(filePath) && _isRuleDisabledInLocalConfig(filePath)) { + return false; + } + return true; + } + + /// Checking if the file is a test file. + bool _isTestFile(String filePath) { + return filePath.contains('/test/') || + filePath.contains(r'\test\') || + filePath.endsWith('_test.dart'); + } + + /// Check if rule is disabled in local analysis_options.yaml config. + bool _isRuleDisabledInLocalConfig(String filePath) { + final configPath = findAnalysisOptionsForFile(filePath); + if (configPath == null) return false; + + try { + final file = File(configPath); + if (!file.existsSync()) return false; + + final content = file.readAsStringSync(); + final yaml = loadYaml(content) as Map?; + + if (yaml == null) return false; + + final customLint = yaml['custom_lint'] as Map?; + if (customLint == null) return false; + + final rules = customLint['rules'] as List?; + if (rules == null) return false; + + /// Searching rule in rules list + for (final rule in rules) { + if (rule is String && rule == config.name) { + return false; + } else if (rule is Map) { + final key = rule.keys.first as String; + if (key == config.name) { + final value = rule.values.first; + return value == false; + } + } + } + + return false; + } catch (e) { + return false; + } + } } diff --git a/lib/src/utils/path_utils.dart b/lib/src/utils/path_utils.dart index 1973c57d..51f5f6ee 100644 --- a/lib/src/utils/path_utils.dart +++ b/lib/src/utils/path_utils.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:glob/glob.dart'; import 'package:path/path.dart' as p; @@ -45,3 +47,53 @@ String relativePath(String path, [String? root]) { final relative = p.posix.relative(uriNormlizedPath, from: uriNormlizedRoot); return relative; } + +/// Finds the appropriate analysis options file for the given file path +/// Supports hierarchical search for nested analysis_options.yaml files +String? findAnalysisOptionsForFile(String filePath) { + final directory = p.dirname(filePath); + return _findSpecificAnalysisOptions(directory, 'analysis_options.yaml'); +} + +/// Recursively searches for a specific analysis options file +String? _findSpecificAnalysisOptions(String dirPath, String fileName) { + final analysisOptions = p.join(dirPath, fileName); + if (File(analysisOptions).existsSync()) { + return analysisOptions; + } + + final parent = p.dirname(dirPath); + + /// Reached filesystem root + if (parent == dirPath) return null; + + if (!_isWithinProject(parent)) return null; + + return _findSpecificAnalysisOptions(parent, fileName); +} + +/// Checking if we are still in project +bool _isWithinProject(String dirPath) { + try { + final dir = Directory(dirPath); + + /// Using custom_lint logic to find project directory + final projectDir = _findProjectDirectory(dir); + return projectDir != null; + } catch (e) { + return false; + } +} + +/// Finds the project directory (contains pubspec.yaml) +Directory? _findProjectDirectory(Directory directory) { + if (File(p.join(directory.path, 'pubspec.yaml')).existsSync()) { + return directory; + } + + if (directory.parent.uri == directory.uri) { + return null; + } + + return _findProjectDirectory(directory.parent); +}