Skip to content

Commit

Permalink
feat: Improve prefer_sliver_prefix to prefer_to_include_sliver_in_name (
Browse files Browse the repository at this point in the history
#58)

* feat: Improve prefer_sliver_prefix to prefer_to_include_sliver_in_name

* chore: use early return
  • Loading branch information
k-nkmr authored Sep 24, 2024
1 parent c2e3ccb commit 9e9ceb5
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 97 deletions.
6 changes: 3 additions & 3 deletions packages/altive_lints/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ There are also Altive-made rules by custom_lint.
- [avoid\_single\_child](#avoid_single_child)
- [prefer\_clock\_now](#prefer_clock_now)
- [prefer\_dedicated\_media\_query\_methods](#prefer_dedicated_media_query_methods)
- [prefer\_sliver\_prefix](#prefer_sliver_prefix)
- [prefer\_to\_include\_sliver\_in\_name](#prefer_to_include_sliver_in_name)
- [prefer\_space\_between\_elements](#prefer_space_between_elements)
- [Lint rules adopted by altive\_lints and why](#lint-rules-adopted-by-altive_lints-and-why)
- [public\_member\_api\_docs](#public_member_api_docs)
Expand Down Expand Up @@ -252,9 +252,9 @@ var size = MediaQuery.sizeOf(context);
var padding = MediaQuery.viewInsetsOf(context);
```

### prefer_sliver_prefix
### prefer_to_include_sliver_in_name

Prefer to prefix the class name of a Widget that returns a Sliver type Widget with “Sliver”.
Prefer to include ‘Sliver’ in the class name or named constructor of a widget that returns a Sliver-type widget.

This makes it easy for the user to know at a glance that it is a Sliver type Widget, and improves readability and consistency.

Expand Down
4 changes: 2 additions & 2 deletions packages/altive_lints/lib/altive_lints.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import 'src/lints/avoid_shrink_wrap_in_list_view.dart';
import 'src/lints/avoid_single_child.dart';
import 'src/lints/prefer_clock_now.dart';
import 'src/lints/prefer_dedicated_media_query_methods.dart';
import 'src/lints/prefer_sliver_prefix.dart';
import 'src/lints/prefer_space_between_elements.dart';
import 'src/lints/prefer_to_include_sliver_in_name.dart';

/// Returns the Altive Plugin instance.
PluginBase createPlugin() => _AltivePlugin();
Expand All @@ -24,6 +24,6 @@ class _AltivePlugin extends PluginBase {
const PreferClockNow(),
const PreferDedicatedMediaQueryMethods(),
const PreferSpaceBetweenElements(),
const PreferSliverPrefix(),
const PreferToIncludeSliverInName(),
];
}
76 changes: 0 additions & 76 deletions packages/altive_lints/lib/src/lints/prefer_sliver_prefix.dart

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/error/listener.dart';
import 'package:collection/collection.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';

/// A `prefer_to_include_sliver_in_name` rule that ensures widgets returning
/// a Sliver-type widget include "Sliver" in their class names.
///
/// This naming convention improves code readability and consistency
/// by clearly indicating the widget's functionality
/// and return type through its name.
///
/// The rule also applies if "Sliver" is present in the named constructor,
/// allowing flexibility in how the convention is followed.
///
/// ### Example
///
/// #### BAD:
///
/// ```dart
/// class MyCustomList extends StatelessWidget {
/// @override
/// Widget build(BuildContext context) {
/// return SliverList(...); // LINT
/// }
/// }
/// ```
///
/// #### GOOD:
///
/// ```dart
/// class SliverMyCustomList extends StatelessWidget {
/// @override
/// Widget build(BuildContext context) {
/// return SliverList(...);
/// }
/// }
/// ```
class PreferToIncludeSliverInName extends DartLintRule {
/// Creates a new instance of [PreferToIncludeSliverInName].
const PreferToIncludeSliverInName() : super(code: _code);

static const _code = LintCode(
name: 'prefer_to_include_sliver_in_name',
problemMessage: 'Widgets returning Sliver should include "Sliver" '
'in the class name or named constructor.',
correctionMessage:
'Consider adding "Sliver" to the class name or a named constructor.',
);

@override
void run(
CustomLintResolver resolver,
ErrorReporter reporter,
CustomLintContext context,
) {
context.registry.addClassDeclaration((node) {
final methodBody = node.members
.whereType<MethodDeclaration>()
.firstWhereOrNull((method) => method.name.lexeme == 'build')
?.body;

if (methodBody is! BlockFunctionBody) {
return;
}

final returnStatements =
methodBody.block.statements.whereType<ReturnStatement>();
final returnsSliverWidget = returnStatements.any(
(returnStatement) {
final returnType = returnStatement.expression?.staticType;
final typeName = returnType?.getDisplayString();
return typeName?.startsWith('Sliver') ?? false;
},
);

if (!returnsSliverWidget) {
return;
}

final className = node.name.lexeme;

if (className.contains('Sliver')) {
return;
}

final constructorNames = node.members
.whereType<ConstructorDeclaration>()
.map((constructor) => constructor.name?.lexeme)
.nonNulls;

final hasSliverInConstructor = constructorNames.any(
(constructorName) => constructorName.toLowerCase().contains('sliver'),
);

if (hasSliverInConstructor) {
return;
}

reporter.atNode(node, _code);
});
}
}
16 changes: 0 additions & 16 deletions packages/altive_lints/lint_test/lints/prefer_sliver_prefix.dart

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import 'package:flutter/material.dart';

// expect_lint: prefer_to_include_sliver_in_name
class MyWidget extends StatelessWidget {
const MyWidget({super.key});

@override
Widget build(BuildContext context) {
return SliverList.builder(
itemCount: 10,
itemBuilder: (context, index) {
return const Placeholder();
},
);
}
}

// expect_lint: prefer_to_include_sliver_in_name
class MyWidget2 extends StatelessWidget {
const MyWidget2({super.key, this.maxCount = 10});

const MyWidget2.small({super.key, this.maxCount = 5});

final int maxCount;

@override
Widget build(BuildContext context) {
return SliverList.builder(
itemCount: maxCount,
itemBuilder: (context, index) {
return const Placeholder();
},
);
}
}

class MySliverWidget extends StatelessWidget {
const MySliverWidget({super.key});

@override
Widget build(BuildContext context) {
return SliverList.builder(
itemCount: 10,
itemBuilder: (context, index) {
return const Placeholder();
},
);
}
}

class MyWidget3 extends StatelessWidget {
const MyWidget3({super.key});

const MyWidget3.sliver({super.key});

@override
Widget build(BuildContext context) {
return SliverList.builder(
itemCount: 10,
itemBuilder: (context, index) {
return const Placeholder();
},
);
}
}

0 comments on commit 9e9ceb5

Please sign in to comment.