From 81497032615d212e071bd4cbb1734413230422cd Mon Sep 17 00:00:00 2001 From: Islam-Shaaban-Ibrahim Date: Tue, 14 Apr 2026 11:04:26 +0200 Subject: [PATCH 1/5] Add analysis_server_plugin dependency --- pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pubspec.yaml b/pubspec.yaml index 80a1415e..58322307 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,6 +13,7 @@ environment: dependencies: analyzer: ^8.4.0 collection: ^1.19.0 + analysis_server_plugin: ^0.3.3 custom_lint_builder: ^0.8.1 glob: ^2.1.3 path: ^1.9.1 From b9ef33ada92054775a307f810f090cc567eb6e27 Mon Sep 17 00:00:00 2001 From: Islam-Shaaban-Ibrahim Date: Tue, 14 Apr 2026 16:34:32 +0200 Subject: [PATCH 2/5] Migrate avoid_global_state rule and tests --- lib/solid_lints.dart | 79 ++---------- .../avoid_global_state_rule.dart | 119 +++++++++++------- lint_test/.gitignore | 45 +++++++ lint_test/.metadata | 30 +++++ lint_test/README.md | 3 + lint_test/avoid_global_state_test.dart | 66 +++++++--- lint_test/lib/main.dart | 20 +++ lint_test/pubspec.yaml | 8 +- pubspec.yaml | 4 +- 9 files changed, 238 insertions(+), 136 deletions(-) create mode 100644 lint_test/.gitignore create mode 100644 lint_test/.metadata create mode 100644 lint_test/README.md create mode 100644 lint_test/lib/main.dart diff --git a/lib/solid_lints.dart b/lib/solid_lints.dart index 4fc66f58..813837bf 100644 --- a/lib/solid_lints.dart +++ b/lib/solid_lints.dart @@ -1,77 +1,18 @@ -library solid_metrics; - -import 'package:custom_lint_builder/custom_lint_builder.dart'; +import 'package:analysis_server_plugin/plugin.dart'; +import 'package:analysis_server_plugin/registry.dart'; import 'package:solid_lints/src/lints/avoid_debug_print_in_release/avoid_debug_print_in_release_rule.dart'; -import 'package:solid_lints/src/lints/avoid_final_with_getter/avoid_final_with_getter_rule.dart'; import 'package:solid_lints/src/lints/avoid_global_state/avoid_global_state_rule.dart'; -import 'package:solid_lints/src/lints/avoid_late_keyword/avoid_late_keyword_rule.dart'; -import 'package:solid_lints/src/lints/avoid_non_null_assertion/avoid_non_null_assertion_rule.dart'; -import 'package:solid_lints/src/lints/avoid_returning_widgets/avoid_returning_widgets_rule.dart'; -import 'package:solid_lints/src/lints/avoid_unnecessary_return_variable/avoid_unnecessary_return_variable_rule.dart'; -import 'package:solid_lints/src/lints/avoid_unnecessary_setstate/avoid_unnecessary_set_state_rule.dart'; -import 'package:solid_lints/src/lints/avoid_unnecessary_type_assertions/avoid_unnecessary_type_assertions_rule.dart'; -import 'package:solid_lints/src/lints/avoid_unnecessary_type_casts/avoid_unnecessary_type_casts_rule.dart'; -import 'package:solid_lints/src/lints/avoid_unrelated_type_assertions/avoid_unrelated_type_assertions_rule.dart'; -import 'package:solid_lints/src/lints/avoid_unused_parameters/avoid_unused_parameters_rule.dart'; -import 'package:solid_lints/src/lints/avoid_using_api/avoid_using_api_rule.dart'; -import 'package:solid_lints/src/lints/cyclomatic_complexity/cyclomatic_complexity_rule.dart'; -import 'package:solid_lints/src/lints/double_literal_format/double_literal_format_rule.dart'; -import 'package:solid_lints/src/lints/function_lines_of_code/function_lines_of_code_rule.dart'; -import 'package:solid_lints/src/lints/member_ordering/member_ordering_rule.dart'; -import 'package:solid_lints/src/lints/named_parameters_ordering/named_parameters_ordering_rule.dart'; -import 'package:solid_lints/src/lints/newline_before_return/newline_before_return_rule.dart'; -import 'package:solid_lints/src/lints/no_empty_block/no_empty_block_rule.dart'; -import 'package:solid_lints/src/lints/no_equal_then_else/no_equal_then_else_rule.dart'; -import 'package:solid_lints/src/lints/no_magic_number/no_magic_number_rule.dart'; -import 'package:solid_lints/src/lints/number_of_parameters/number_of_parameters_rule.dart'; -import 'package:solid_lints/src/lints/prefer_conditional_expressions/prefer_conditional_expressions_rule.dart'; -import 'package:solid_lints/src/lints/prefer_early_return/prefer_early_return_rule.dart'; -import 'package:solid_lints/src/lints/prefer_first/prefer_first_rule.dart'; -import 'package:solid_lints/src/lints/prefer_last/prefer_last_rule.dart'; -import 'package:solid_lints/src/lints/prefer_match_file_name/prefer_match_file_name_rule.dart'; -import 'package:solid_lints/src/lints/proper_super_calls/proper_super_calls_rule.dart'; -import 'package:solid_lints/src/models/solid_lint_rule.dart'; -/// Creates a plugin for our custom linter -PluginBase createPlugin() => _SolidLints(); +final plugin = SolidLintsPlugin(); -/// Initialize custom solid lints -class _SolidLints extends PluginBase { +class SolidLintsPlugin extends Plugin { @override - List getLintRules(CustomLintConfigs configs) { - final List supportedRules = [ - CyclomaticComplexityRule.createRule(configs), - NumberOfParametersRule.createRule(configs), - FunctionLinesOfCodeRule.createRule(configs), - AvoidNonNullAssertionRule.createRule(configs), - AvoidLateKeywordRule.createRule(configs), - AvoidGlobalStateRule.createRule(configs), - AvoidReturningWidgetsRule.createRule(configs), - DoubleLiteralFormatRule.createRule(configs), - AvoidUnnecessaryTypeAssertions.createRule(configs), - AvoidUnnecessarySetStateRule.createRule(configs), - AvoidUnnecessaryTypeCastsRule.createRule(configs), - AvoidUnrelatedTypeAssertionsRule.createRule(configs), - AvoidUnusedParametersRule.createRule(configs), - AvoidUsingApiRule.createRule(configs), - NewlineBeforeReturnRule.createRule(configs), - NoEmptyBlockRule.createRule(configs), - NoEqualThenElseRule.createRule(configs), - MemberOrderingRule.createRule(configs), - NoMagicNumberRule.createRule(configs), - PreferConditionalExpressionsRule.createRule(configs), - PreferFirstRule.createRule(configs), - PreferLastRule.createRule(configs), - PreferMatchFileNameRule.createRule(configs), - ProperSuperCallsRule.createRule(configs), - AvoidDebugPrintInReleaseRule.createRule(configs), - PreferEarlyReturnRule.createRule(configs), - AvoidFinalWithGetterRule.createRule(configs), - NamedParametersOrderingRule.createRule(configs), - AvoidUnnecessaryReturnVariableRule.createRule(configs), - ]; + String get name => 'solid_lints'; - // Return only enabled rules - return supportedRules.where((r) => r.enabled).toList(); + @override + void register(PluginRegistry registry) { + registry.registerLintRule( + AvoidGlobalStateRule(), + ); } } 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 f8211320..ae037ff1 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 @@ -1,8 +1,9 @@ +import 'package:analyzer/analysis_rule/analysis_rule.dart'; +import 'package:analyzer/analysis_rule/rule_context.dart'; +import 'package:analyzer/analysis_rule/rule_visitor_registry.dart'; import 'package:analyzer/dart/ast/ast.dart'; -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/models/solid_lint_rule.dart'; +import 'package:analyzer/dart/ast/visitor.dart'; +import 'package:analyzer/error/error.dart'; /// Avoid top-level and static mutable variables. /// @@ -23,7 +24,6 @@ import 'package:solid_lints/src/models/solid_lint_rule.dart'; /// } /// ``` /// -/// /// #### GOOD: /// /// ```dart @@ -35,49 +35,84 @@ import 'package:solid_lints/src/models/solid_lint_rule.dart'; /// static final int globalFinal = 1; /// } /// ``` -class AvoidGlobalStateRule extends SolidLintRule { - /// This lint rule represents - /// the error whether we use global state. - static const lintName = 'avoid_global_state'; - - AvoidGlobalStateRule._(super.config); - - /// Creates a new instance of [AvoidGlobalStateRule] - /// based on the lint configuration. - factory AvoidGlobalStateRule.createRule(CustomLintConfigs configs) { - final rule = RuleConfig( - configs: configs, - name: lintName, - problemMessage: (_) => 'Avoid variables that can be globally mutated.', - ); - - return AvoidGlobalStateRule._(rule); - } +class AvoidGlobalStateRule extends AnalysisRule { + /// Lint name used for suppression and reporting. + static const String lintName = 'avoid_global_state'; + + /// Lint code used for suppression and reporting. + static const LintCode code = LintCode( + lintName, + 'Avoid variables that can be globally mutated.', + correctionMessage: + 'Prefer using final/const or a state management solution.', + ); + + /// Creates an instance of [AvoidGlobalStateRule]. + AvoidGlobalStateRule() + : super( + name: lintName, + description: + 'Avoid top-level or static mutable variables to reduce shared mutable state.', + ); + + @override + LintCode get diagnosticCode => code; @override - void run( - CustomLintResolver resolver, - DiagnosticReporter reporter, - CustomLintContext context, + void registerNodeProcessors( + RuleVisitorRegistry registry, + RuleContext context, ) { - context.registry.addTopLevelVariableDeclaration( - (node) => node.variables.variables - .where((variable) => variable.isPublicMutable) - .forEach((node) => reporter.atNode(node, code)), - ); - context.registry.addFieldDeclaration((node) { - if (!node.isStatic) return; - node.fields.variables - .where((variable) => variable.isPublicMutable) - .forEach((node) => reporter.atNode(node, code)); - }); + final visitor = _Visitor(this); + + registry.addTopLevelVariableDeclaration(this, visitor); + registry.addFieldDeclaration(this, visitor); } } -extension on VariableDeclaration { - bool get isMutable => !isFinal && !isConst; +class _Visitor extends SimpleAstVisitor { + final AvoidGlobalStateRule rule; - bool get isPrivate => declaredFragment?.element.isPrivate ?? false; + _Visitor(this.rule); - bool get isPublicMutable => isMutable && !isPrivate; + @override + void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { + for (final variable in node.variables.variables) { + if (_isPublicMutable(variable)) { + rule.reportAtNode(variable); + } + } + } + + @override + void visitFieldDeclaration(FieldDeclaration node) { + if (!node.isStatic) return; + + for (final variable in node.fields.variables) { + if (_isPublicMutable(variable)) { + rule.reportAtNode(variable); + } + } + } + + /// Returns true if the variable is mutable and not private. + bool _isPublicMutable(VariableDeclaration variable) { + return _isMutable(variable) && !_isPrivate(variable); + } + + /// A variable is mutable if it is not final or const. + bool _isMutable(VariableDeclaration variable) { + final element = variable.declaredFragment?.element; + + final isFinal = element?.isFinal ?? false; + final isConst = element?.isConst ?? false; + + return !isFinal && !isConst; + } + + /// A variable is private if its element is private. + bool _isPrivate(VariableDeclaration variable) { + final element = variable.declaredFragment?.element; + return element?.isPrivate ?? false; + } } diff --git a/lint_test/.gitignore b/lint_test/.gitignore new file mode 100644 index 00000000..3820a95c --- /dev/null +++ b/lint_test/.gitignore @@ -0,0 +1,45 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ +/coverage/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/lint_test/.metadata b/lint_test/.metadata new file mode 100644 index 00000000..0bb2266c --- /dev/null +++ b/lint_test/.metadata @@ -0,0 +1,30 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "05db9689081f091050f01aed79f04dce0c750154" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 05db9689081f091050f01aed79f04dce0c750154 + base_revision: 05db9689081f091050f01aed79f04dce0c750154 + - platform: web + create_revision: 05db9689081f091050f01aed79f04dce0c750154 + base_revision: 05db9689081f091050f01aed79f04dce0c750154 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/lint_test/README.md b/lint_test/README.md new file mode 100644 index 00000000..9002c39f --- /dev/null +++ b/lint_test/README.md @@ -0,0 +1,3 @@ +# solid_lints_test + +A new Flutter project. diff --git a/lint_test/avoid_global_state_test.dart b/lint_test/avoid_global_state_test.dart index 37028d42..05bb041b 100644 --- a/lint_test/avoid_global_state_test.dart +++ b/lint_test/avoid_global_state_test.dart @@ -1,30 +1,56 @@ -// ignore_for_file: type_annotate_public_apis, prefer_match_file_name, unused_local_variable +import 'package:analyzer_testing/analysis_rule/analysis_rule.dart'; +import 'package:solid_lints/src/lints/avoid_global_state/avoid_global_state_rule.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; -/// Check global mutable variable fail -/// `avoid_global_state` +@reflectiveTest +class AvoidGlobalStateRuleTest extends AnalysisRuleTest { + @override + void setUp() { + rule = AvoidGlobalStateRule(); -// expect_lint: avoid_global_state -var globalMutable = 0; - -final globalFinal = 1; + super.setUp(); + } -const globalConst = 1; + void test_reports_mutable_top_level_variable() async { + await assertDiagnostics( + r''' +var globalMutable = 0; +''', + [lint(4, 17)], + ); + } + void test_reports_mutable_static_field() async { + await assertDiagnostics( + r''' class Test { - static final int globalFinal = 1; - - // expect_lint: avoid_global_state - static int globalMutable = 0; - - final int memberFinal = 1; - - int memberMutable = 0; + static int staticMutable = 0; +} +''', + [lint(26, 17)], + ); + } - void m() { - int localMutable = 0; + void test_does_not_report_immutable_or_private_cases() async { + await assertNoDiagnostics(r''' +final globalFinal = 1; +const globalConst = 1; - final localFinal = 1; +var _privateTopLevel = 0; - const localConst = 2; +class Test { + static final int staticFinal = 1; + static const int staticConst = 2; + static int _staticPrivate = 0; + final int instanceFinal = 1; + int instanceMutable = 0; +} +'''); } } + +void main() { + defineReflectiveSuite(() { + defineReflectiveTests(AvoidGlobalStateRuleTest); + }); +} diff --git a/lint_test/lib/main.dart b/lint_test/lib/main.dart new file mode 100644 index 00000000..a7256585 --- /dev/null +++ b/lint_test/lib/main.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; + +void main() { + runApp(const MainApp()); +} + +class MainApp extends StatelessWidget { + const MainApp({super.key}); + + @override + Widget build(BuildContext context) { + return const MaterialApp( + home: Scaffold( + body: Center( + child: Text('Hello World!'), + ), + ), + ); + } +} diff --git a/lint_test/pubspec.yaml b/lint_test/pubspec.yaml index cb37cd0b..2879bd20 100644 --- a/lint_test/pubspec.yaml +++ b/lint_test/pubspec.yaml @@ -1,9 +1,9 @@ name: solid_lints_test -description: A starting point for Dart libraries or applications. +description: Test project for solid_lints rules publish_to: none environment: - sdk: '>=3.0.0 <4.0.0' + sdk: ">=3.5.0 <4.0.0" dependencies: flutter: @@ -12,4 +12,6 @@ dependencies: dev_dependencies: solid_lints: path: ../ - test: ^1.20.1 + analyzer_testing: ^0.1.9 + test_reflective_loader: ^0.3.0 + test: ^1.25.0 diff --git a/pubspec.yaml b/pubspec.yaml index 58322307..a8eefc85 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,10 +11,9 @@ environment: sdk: ">=3.5.0 <4.0.0" dependencies: - analyzer: ^8.4.0 + analyzer: ^10.0.1 collection: ^1.19.0 analysis_server_plugin: ^0.3.3 - custom_lint_builder: ^0.8.1 glob: ^2.1.3 path: ^1.9.1 yaml: ^3.1.3 @@ -25,3 +24,4 @@ dependencies: dev_dependencies: args: ^2.6.0 + From 322278be29eb2ff66ea4cae6663c0c15c9e67fa7 Mon Sep 17 00:00:00 2001 From: Islam-Shaaban-Ibrahim Date: Wed, 15 Apr 2026 05:47:03 +0200 Subject: [PATCH 3/5] applied code review suggestions --- lib/{solid_lints.dart => main.dart} | 3 +- .../avoid_global_state_rule.dart | 55 +------------------ .../avoid_global_state_rule_visitor.dart | 51 +++++++++++++++++ lint_test/lib/main.dart | 20 ------- lint_test/pubspec.yaml | 4 +- pubspec.yaml | 3 + .../avoid_global_state_rule_test.dart | 38 ++++++++++--- 7 files changed, 89 insertions(+), 85 deletions(-) rename lib/{solid_lints.dart => main.dart} (80%) create mode 100644 lib/src/lints/avoid_global_state/avoid_global_state_rule_visitor.dart delete mode 100644 lint_test/lib/main.dart rename lint_test/avoid_global_state_test.dart => test/avoid_global_state_rule_test.dart (65%) diff --git a/lib/solid_lints.dart b/lib/main.dart similarity index 80% rename from lib/solid_lints.dart rename to lib/main.dart index 813837bf..07d19258 100644 --- a/lib/solid_lints.dart +++ b/lib/main.dart @@ -1,10 +1,11 @@ import 'package:analysis_server_plugin/plugin.dart'; import 'package:analysis_server_plugin/registry.dart'; -import 'package:solid_lints/src/lints/avoid_debug_print_in_release/avoid_debug_print_in_release_rule.dart'; import 'package:solid_lints/src/lints/avoid_global_state/avoid_global_state_rule.dart'; +/// create plugin final plugin = SolidLintsPlugin(); +/// create plugin class class SolidLintsPlugin extends Plugin { @override String get name => 'solid_lints'; 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 ae037ff1..c7b82fed 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 @@ -1,9 +1,8 @@ import 'package:analyzer/analysis_rule/analysis_rule.dart'; import 'package:analyzer/analysis_rule/rule_context.dart'; import 'package:analyzer/analysis_rule/rule_visitor_registry.dart'; -import 'package:analyzer/dart/ast/ast.dart'; -import 'package:analyzer/dart/ast/visitor.dart'; import 'package:analyzer/error/error.dart'; +import 'package:solid_lints/src/lints/avoid_global_state/avoid_global_state_rule_visitor.dart'; /// Avoid top-level and static mutable variables. /// @@ -51,8 +50,7 @@ class AvoidGlobalStateRule extends AnalysisRule { AvoidGlobalStateRule() : super( name: lintName, - description: - 'Avoid top-level or static mutable variables to reduce shared mutable state.', + description: 'Avoid top-level or static mutable variables ', ); @override @@ -63,56 +61,9 @@ class AvoidGlobalStateRule extends AnalysisRule { RuleVisitorRegistry registry, RuleContext context, ) { - final visitor = _Visitor(this); + final visitor = AvoidGlobalStateRuleVisitor(this); registry.addTopLevelVariableDeclaration(this, visitor); registry.addFieldDeclaration(this, visitor); } } - -class _Visitor extends SimpleAstVisitor { - final AvoidGlobalStateRule rule; - - _Visitor(this.rule); - - @override - void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { - for (final variable in node.variables.variables) { - if (_isPublicMutable(variable)) { - rule.reportAtNode(variable); - } - } - } - - @override - void visitFieldDeclaration(FieldDeclaration node) { - if (!node.isStatic) return; - - for (final variable in node.fields.variables) { - if (_isPublicMutable(variable)) { - rule.reportAtNode(variable); - } - } - } - - /// Returns true if the variable is mutable and not private. - bool _isPublicMutable(VariableDeclaration variable) { - return _isMutable(variable) && !_isPrivate(variable); - } - - /// A variable is mutable if it is not final or const. - bool _isMutable(VariableDeclaration variable) { - final element = variable.declaredFragment?.element; - - final isFinal = element?.isFinal ?? false; - final isConst = element?.isConst ?? false; - - return !isFinal && !isConst; - } - - /// A variable is private if its element is private. - bool _isPrivate(VariableDeclaration variable) { - final element = variable.declaredFragment?.element; - return element?.isPrivate ?? false; - } -} diff --git a/lib/src/lints/avoid_global_state/avoid_global_state_rule_visitor.dart b/lib/src/lints/avoid_global_state/avoid_global_state_rule_visitor.dart new file mode 100644 index 00000000..916c839c --- /dev/null +++ b/lib/src/lints/avoid_global_state/avoid_global_state_rule_visitor.dart @@ -0,0 +1,51 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/ast/visitor.dart'; +import 'package:solid_lints/src/lints/avoid_global_state/avoid_global_state_rule.dart'; + +/// Visitor for [AvoidGlobalStateRule]. +class AvoidGlobalStateRuleVisitor extends SimpleAstVisitor { + /// The rule this visitor is associated with. + final AvoidGlobalStateRule rule; + + /// Creates an instance of [AvoidGlobalStateRuleVisitor]. + AvoidGlobalStateRuleVisitor(this.rule); + + @override + void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) { + for (final variable in node.variables.variables) { + if (_isPublicMutable(variable)) { + rule.reportAtNode(variable); + } + } + } + + @override + void visitFieldDeclaration(FieldDeclaration node) { + if (!node.isStatic) return; + + for (final variable in node.fields.variables) { + if (_isPublicMutable(variable)) { + rule.reportAtNode(variable); + } + } + } + + /// Returns true if the variable is mutable and not private. + bool _isPublicMutable(VariableDeclaration variable) { + return _isMutable(variable) && !_isPrivate(variable); + } + + /// A variable is mutable if it is not final or const. + bool _isMutable(VariableDeclaration variable) { + final parent = variable.parent; + return parent is VariableDeclarationList && + !parent.isFinal && + !parent.isConst; + } + + /// A variable is private if its element is private. + bool _isPrivate(VariableDeclaration variable) { + final element = variable.declaredFragment?.element; + return element?.isPrivate ?? false; + } +} diff --git a/lint_test/lib/main.dart b/lint_test/lib/main.dart deleted file mode 100644 index a7256585..00000000 --- a/lint_test/lib/main.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:flutter/material.dart'; - -void main() { - runApp(const MainApp()); -} - -class MainApp extends StatelessWidget { - const MainApp({super.key}); - - @override - Widget build(BuildContext context) { - return const MaterialApp( - home: Scaffold( - body: Center( - child: Text('Hello World!'), - ), - ), - ); - } -} diff --git a/lint_test/pubspec.yaml b/lint_test/pubspec.yaml index 2879bd20..18201052 100644 --- a/lint_test/pubspec.yaml +++ b/lint_test/pubspec.yaml @@ -12,6 +12,4 @@ dependencies: dev_dependencies: solid_lints: path: ../ - analyzer_testing: ^0.1.9 - test_reflective_loader: ^0.3.0 - test: ^1.25.0 + diff --git a/pubspec.yaml b/pubspec.yaml index a8eefc85..45317bec 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -24,4 +24,7 @@ dependencies: dev_dependencies: args: ^2.6.0 + analyzer_testing: ^0.1.9 + test_reflective_loader: ^0.3.0 + test: ^1.25.0 diff --git a/lint_test/avoid_global_state_test.dart b/test/avoid_global_state_rule_test.dart similarity index 65% rename from lint_test/avoid_global_state_test.dart rename to test/avoid_global_state_rule_test.dart index 05bb041b..020f4344 100644 --- a/lint_test/avoid_global_state_test.dart +++ b/test/avoid_global_state_rule_test.dart @@ -2,12 +2,17 @@ import 'package:analyzer_testing/analysis_rule/analysis_rule.dart'; import 'package:solid_lints/src/lints/avoid_global_state/avoid_global_state_rule.dart'; import 'package:test_reflective_loader/test_reflective_loader.dart'; +void main() { + defineReflectiveSuite(() { + defineReflectiveTests(AvoidGlobalStateRuleTest); + }); +} + @reflectiveTest class AvoidGlobalStateRuleTest extends AnalysisRuleTest { @override void setUp() { rule = AvoidGlobalStateRule(); - super.setUp(); } @@ -31,26 +36,41 @@ class Test { ); } - void test_does_not_report_immutable_or_private_cases() async { + void test_does_not_report_global_immutable_variables() async { await assertNoDiagnostics(r''' final globalFinal = 1; const globalConst = 1; +'''); + } + void test_does_not_report_global_private_variables() async { + await assertNoDiagnostics(r''' var _privateTopLevel = 0; +'''); + } + void test_does_not_report_class_level_immutable_variables() async { + await assertNoDiagnostics(r''' class Test { static final int staticFinal = 1; static const int staticConst = 2; - static int _staticPrivate = 0; - final int instanceFinal = 1; - int instanceMutable = 0; } '''); } + + void test_does_not_report_class_level_private_variables() async { + await assertNoDiagnostics(r''' +class Test { + static int _staticPrivate = 0; } +'''); + } -void main() { - defineReflectiveSuite(() { - defineReflectiveTests(AvoidGlobalStateRuleTest); - }); + void test_does_not_report_local_method_variables() async { + await assertNoDiagnostics(r''' +void m() { + int localMutable = 0; +} +'''); + } } From 62d79b6dfb5744c4c553bb28b800f587fddda60c Mon Sep 17 00:00:00 2001 From: Islam-Shaaban-Ibrahim Date: Wed, 15 Apr 2026 12:34:56 +0200 Subject: [PATCH 4/5] applied suggestions from code review --- lib/main.dart | 10 ++++++++-- .../avoid_global_state/avoid_global_state_rule.dart | 2 +- .../avoid_global_state_rule_visitor.dart | 0 lint_test/pubspec.yaml | 2 +- pubspec.yaml | 2 -- test/avoid_global_state_rule_test.dart | 2 +- 6 files changed, 11 insertions(+), 7 deletions(-) rename lib/src/lints/avoid_global_state/{ => visitors}/avoid_global_state_rule_visitor.dart (100%) diff --git a/lib/main.dart b/lib/main.dart index 07d19258..257ef524 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,10 +2,16 @@ import 'package:analysis_server_plugin/plugin.dart'; import 'package:analysis_server_plugin/registry.dart'; import 'package:solid_lints/src/lints/avoid_global_state/avoid_global_state_rule.dart'; -/// create plugin +/// The entry point for the Solid Lints analyser server plugin. +/// +/// This plugin integrates custom lint rules into the Dart analysis server, +/// allowing them to run during static analysis. final plugin = SolidLintsPlugin(); -/// create plugin class +/// An analysis server plugin that provides Solid Lints rules. +/// +/// This plugin registers custom lint rules and enables them to be executed +/// by the Dart analyzer during code analysis. class SolidLintsPlugin extends Plugin { @override String get name => 'solid_lints'; 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 c7b82fed..1dc3f391 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 @@ -2,7 +2,7 @@ import 'package:analyzer/analysis_rule/analysis_rule.dart'; import 'package:analyzer/analysis_rule/rule_context.dart'; import 'package:analyzer/analysis_rule/rule_visitor_registry.dart'; import 'package:analyzer/error/error.dart'; -import 'package:solid_lints/src/lints/avoid_global_state/avoid_global_state_rule_visitor.dart'; +import 'package:solid_lints/src/lints/avoid_global_state/visitors/avoid_global_state_rule_visitor.dart'; /// Avoid top-level and static mutable variables. /// diff --git a/lib/src/lints/avoid_global_state/avoid_global_state_rule_visitor.dart b/lib/src/lints/avoid_global_state/visitors/avoid_global_state_rule_visitor.dart similarity index 100% rename from lib/src/lints/avoid_global_state/avoid_global_state_rule_visitor.dart rename to lib/src/lints/avoid_global_state/visitors/avoid_global_state_rule_visitor.dart diff --git a/lint_test/pubspec.yaml b/lint_test/pubspec.yaml index 18201052..10e64475 100644 --- a/lint_test/pubspec.yaml +++ b/lint_test/pubspec.yaml @@ -12,4 +12,4 @@ dependencies: dev_dependencies: solid_lints: path: ../ - + test: ^1.20.1 diff --git a/pubspec.yaml b/pubspec.yaml index 45317bec..a7c16536 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -26,5 +26,3 @@ dev_dependencies: args: ^2.6.0 analyzer_testing: ^0.1.9 test_reflective_loader: ^0.3.0 - test: ^1.25.0 - diff --git a/test/avoid_global_state_rule_test.dart b/test/avoid_global_state_rule_test.dart index 020f4344..29616804 100644 --- a/test/avoid_global_state_rule_test.dart +++ b/test/avoid_global_state_rule_test.dart @@ -66,7 +66,7 @@ class Test { '''); } - void test_does_not_report_local_method_variables() async { + void test_does_not_report_local_variables() async { await assertNoDiagnostics(r''' void m() { int localMutable = 0; From aae01673c3e31b29afdfd4a708fa127800c22299 Mon Sep 17 00:00:00 2001 From: Islam-Shaaban-Ibrahim Date: Wed, 15 Apr 2026 12:51:15 +0200 Subject: [PATCH 5/5] edited main documentation and changed avoid_global_state visitor naming --- lib/main.dart | 2 +- .../lints/avoid_global_state/avoid_global_state_rule.dart | 4 ++-- ...te_rule_visitor.dart => avoid_global_state_visitor.dart} | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) rename lib/src/lints/avoid_global_state/visitors/{avoid_global_state_rule_visitor.dart => avoid_global_state_visitor.dart} (89%) diff --git a/lib/main.dart b/lib/main.dart index 257ef524..707269e3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,7 +8,7 @@ import 'package:solid_lints/src/lints/avoid_global_state/avoid_global_state_rule /// allowing them to run during static analysis. final plugin = SolidLintsPlugin(); -/// An analysis server plugin that provides Solid Lints rules. +/// An analysis server plugin that provides Solid lint rules. /// /// This plugin registers custom lint rules and enables them to be executed /// by the Dart analyzer during code analysis. 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 1dc3f391..91a38cc4 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 @@ -2,7 +2,7 @@ import 'package:analyzer/analysis_rule/analysis_rule.dart'; import 'package:analyzer/analysis_rule/rule_context.dart'; import 'package:analyzer/analysis_rule/rule_visitor_registry.dart'; import 'package:analyzer/error/error.dart'; -import 'package:solid_lints/src/lints/avoid_global_state/visitors/avoid_global_state_rule_visitor.dart'; +import 'package:solid_lints/src/lints/avoid_global_state/visitors/avoid_global_state_visitor.dart'; /// Avoid top-level and static mutable variables. /// @@ -61,7 +61,7 @@ class AvoidGlobalStateRule extends AnalysisRule { RuleVisitorRegistry registry, RuleContext context, ) { - final visitor = AvoidGlobalStateRuleVisitor(this); + final visitor = AvoidGlobalStateVisitor(this); registry.addTopLevelVariableDeclaration(this, visitor); registry.addFieldDeclaration(this, visitor); diff --git a/lib/src/lints/avoid_global_state/visitors/avoid_global_state_rule_visitor.dart b/lib/src/lints/avoid_global_state/visitors/avoid_global_state_visitor.dart similarity index 89% rename from lib/src/lints/avoid_global_state/visitors/avoid_global_state_rule_visitor.dart rename to lib/src/lints/avoid_global_state/visitors/avoid_global_state_visitor.dart index 916c839c..1f5a1876 100644 --- a/lib/src/lints/avoid_global_state/visitors/avoid_global_state_rule_visitor.dart +++ b/lib/src/lints/avoid_global_state/visitors/avoid_global_state_visitor.dart @@ -3,12 +3,12 @@ import 'package:analyzer/dart/ast/visitor.dart'; import 'package:solid_lints/src/lints/avoid_global_state/avoid_global_state_rule.dart'; /// Visitor for [AvoidGlobalStateRule]. -class AvoidGlobalStateRuleVisitor extends SimpleAstVisitor { +class AvoidGlobalStateVisitor extends SimpleAstVisitor { /// The rule this visitor is associated with. final AvoidGlobalStateRule rule; - /// Creates an instance of [AvoidGlobalStateRuleVisitor]. - AvoidGlobalStateRuleVisitor(this.rule); + /// Creates an instance of [AvoidGlobalStateVisitor]. + AvoidGlobalStateVisitor(this.rule); @override void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {