Skip to content

Commit

Permalink
Add always_provide_flag_property_parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
Pante committed Dec 11, 2024
1 parent ebddcd3 commit c5c61e1
Show file tree
Hide file tree
Showing 10 changed files with 251 additions and 72 deletions.
20 changes: 10 additions & 10 deletions forui/example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -236,10 +236,10 @@ packages:
dependency: transitive
description:
name: flutter_svg
sha256: "936d9c1c010d3e234d1672574636f3352b4941ca3decaddd3cafaeb9ad49c471"
sha256: "54900a1a1243f3c4a5506d853a2b5c2dbc38d5f27e52a52618a8054401431123"
url: "https://pub.dev"
source: hosted
version: "2.0.15"
version: "2.0.16"
flutter_test:
dependency: "direct dev"
description: flutter
Expand Down Expand Up @@ -501,18 +501,18 @@ packages:
dependency: transitive
description:
name: path_provider_android
sha256: c464428172cb986b758c6d1724c603097febb8fb855aa265aeecc9280c294d4a
sha256: "8c4967f8b7cb46dc914e178daa29813d83ae502e0529d7b0478330616a691ef7"
url: "https://pub.dev"
source: hosted
version: "2.2.12"
version: "2.2.14"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
version: "2.4.1"
path_provider_linux:
dependency: transitive
description:
Expand Down Expand Up @@ -714,10 +714,10 @@ packages:
dependency: transitive
description:
name: vector_graphics_compiler
sha256: ab9ff38fc771e9ee1139320adbe3d18a60327370c218c60752068ebee4b49ab1
sha256: "1b4b9e706a10294258727674a340ae0d6e64a7231980f9f9a3d12e4b42407aad"
url: "https://pub.dev"
source: hosted
version: "1.1.15"
version: "1.1.16"
vector_math:
dependency: transitive
description:
Expand Down Expand Up @@ -786,10 +786,10 @@ packages:
dependency: transitive
description:
name: win32
sha256: "84ba388638ed7a8cb3445a320c8273136ab2631cd5f2c57888335504ddab1bc2"
sha256: "8b338d4486ab3fbc0ba0db9f9b4f5239b6697fcee427939a40e720cbb9ee0a69"
url: "https://pub.dev"
source: hosted
version: "5.8.0"
version: "5.9.0"
xdg_directories:
dependency: transitive
description:
Expand Down
2 changes: 1 addition & 1 deletion forui/lib/src/widgets/switch.dart
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ final class FSwitchStateStyle with Diagnosticable implements FFormFieldStyle {
});

@override
FFormFieldStyle copyWith({
FSwitchStateStyle copyWith({
Color? checkedColor,
Color? uncheckedColor,
Color? thumbColor,
Expand Down
6 changes: 4 additions & 2 deletions forui_internal_lints/lib/forui_internal_lints.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import 'package:custom_lint_builder/custom_lint_builder.dart';
import 'package:forui_internal_lints/src/always_call_super_dispose_last.dart';
import 'package:forui_internal_lints/src/always_provide_flag_property_parameter.dart';
import 'package:forui_internal_lints/src/avoid_record_diagnostics_properties.dart';

import 'package:forui_internal_lints/src/diagnosticable_styles.dart';
import 'package:forui_internal_lints/src/style_api.dart';
import 'package:forui_internal_lints/src/prefer_specific_diagnostics_properties.dart';
import 'package:forui_internal_lints/src/prefix_public_types.dart';

Expand All @@ -13,8 +14,9 @@ class _ForuiLinter extends PluginBase {
@override
List<LintRule> getLintRules(CustomLintConfigs configs) => const [
AlwaysCallSuperDisposeLast(),
AlwaysProvideFlagPropertyArgument(),
AvoidRecordDiagnosticsProperties(),
DiagnosticableStylesRule(),
StyleApiRule(),
PreferSpecificDiagnosticsProperties(),
PrefixPublicTypesRule(),
];
Expand Down
18 changes: 10 additions & 8 deletions forui_internal_lints/lib/src/always_call_super_dispose_last.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ class AlwaysCallSuperDisposeLast extends DartLintRule {

if (node.body case BlockFunctionBody(block: Block(:final statements)) when statements.isNotEmpty) {
for (final statement in statements.reversed.skip(1)) {
if (statement case ExpressionStatement(:final MethodInvocation expression) when expression.toSource() == 'super.dispose()') {
if (statement case ExpressionStatement(:final MethodInvocation expression)
when expression.toSource() == 'super.dispose()') {
reporter.atNode(expression, _code);
}
}
Expand All @@ -59,13 +60,14 @@ class _Visitor extends SimpleElementVisitor<bool> {
bool? visitMixinElement(MixinElement type) => _visitInterface(type);

bool _visitInterface(InterfaceElement interface) {
if (self != interface &&
interface.methods.any((method) =>
!method.isStatic &&
method.hasMustCallSuper &&
method.returnType is VoidType &&
method.name == 'dispose' &&
method.parameters.isEmpty)) {
bool signature(MethodElement method) =>
!method.isStatic &&
method.hasMustCallSuper &&
method.returnType is VoidType &&
method.name == 'dispose' &&
method.parameters.isEmpty;

if (self != interface && interface.methods.any(signature)) {
return true;
}

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

const _code = LintCode(
name: 'always_provide_flag_property_parameter',
problemMessage: 'Provide `ifTrue` and/or `ifFalse` parameter',
);

const _flagProperty = TypeChecker.fromName('FlagProperty', packageName: 'flutter');

/// A lint rule that ensures flag property provides `ifTrue` and/or `ifFalse` parameter.
class AlwaysProvideFlagPropertyArgument extends DartLintRule {
/// Creates a new [AlwaysProvideFlagPropertyArgument].
const AlwaysProvideFlagPropertyArgument() : super(code: _code);

@override
void run(CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context) {
context.registry.addInstanceCreationExpression((node) {
if (node.staticType case final type when type == null || !_flagProperty.isExactlyType(type)) {
return;
}

if (node.argumentList.length < 2) {
return;
}

if (node.argumentList.arguments
.whereType<NamedExpression>()
.any((expression) => expression.element?.name == 'ifTrue' || expression.element?.name == 'ifFalse')) {
return;
}

reporter.atNode(node, _code);
});
}
}
38 changes: 0 additions & 38 deletions forui_internal_lints/lib/src/diagnosticable_styles.dart

This file was deleted.

81 changes: 81 additions & 0 deletions forui_internal_lints/lib/src/style_api.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import 'package:analyzer/error/listener.dart';
import 'package:custom_lint_builder/custom_lint_builder.dart';

const _code = LintCode(
name: 'style_api',
problemMessage: 'Style(s) should conform to the Diagnosticable interface.',
);

const _suffixes = {'Style', 'Styles'};

const _checker = TypeChecker.fromName('Diagnosticable', packageName: 'flutter');

/// A lint rule that checks if a class ending with 'Style' or 'Styles' conforms to the required interface.
class StyleApiRule extends DartLintRule {
/// Creates a new [StyleApiRule].
const StyleApiRule() : super(code: _code);

@override
void run(CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context) {
context.registry.addClassDeclaration((node) {
final declared = node.declaredElement;
if (declared == null) {
return;
}

if (declared.name case final name when _suffixes.every((s) => !name.endsWith(s))) {
return;
}

if (declared.isConstructable && !_checker.isAssignableFrom(declared)) {
reporter.atElement(
declared,
LintCode(
name: 'style_api',
problemMessage: '${declared.name}, should be assignable to Diagnosticable.',
),
);
}

if (!declared.isConstructable) {
return;
}

final copyWith = declared.getMethod('copyWith');
if (copyWith == null ||
copyWith.isStatic ||
copyWith.returnType != declared.thisType ||
copyWith.parameters.isEmpty) {
reporter.atElement(
declared,
LintCode(
name: 'style_api',
problemMessage: '${declared.name} does not provide a valid copyWith(...) method.',
),
);
}

final equality = declared.getMethod('==');
if (equality == null) {
reporter.atElement(
declared,
LintCode(
name: 'style_api',
problemMessage: '${declared.name} does not provide a valid == operator.',
),
);
}

final hashCode = declared.getGetter('hashCode');
if (hashCode == null) {
reporter.atElement(
declared,
LintCode(
name: 'style_api',
problemMessage: '${declared.name} does not provide a valid hashCode getter.',
),
);
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import 'package:flutter/foundation.dart';

final goodBoth = FlagProperty('good', value: true, ifTrue: 'good', ifFalse: 'bad');

final goodTrue = FlagProperty('good', value: true, ifTrue: 'good');

final goodFalse = FlagProperty('good', value: false, ifFalse: 'bad');

// expect_lint: always_provide_flag_property_parameter
final bad = FlagProperty('bad', value: false);


13 changes: 0 additions & 13 deletions forui_internal_lints/testbed/lib/src/diagnosticable_styles.dart

This file was deleted.

Loading

0 comments on commit c5c61e1

Please sign in to comment.