Skip to content

Commit

Permalink
Implement proper-super-calls (#77)
Browse files Browse the repository at this point in the history
* Fix avoid-late if initialized

* Update lint_test/avoid_late_keyword_test.dart

Co-authored-by: Yurii Prykhodko <[email protected]>

* Apply suggestions from code review

Co-authored-by: Yurii Prykhodko <[email protected]>

* Custom avoid-late

* Fix naming

* Apply suggestions from code review

Co-authored-by: Yurii Prykhodko <[email protected]>

* Avoid late simplified

* Update lib/lints/avoid_late_keyword/models/avoid_late_keyword_parameters.dart

Co-authored-by: Yurii Prykhodko <[email protected]>

* Avoid-late ignored_types

* Avoid-late ignored_types formatted

* Update lib/lints/avoid_late_keyword/models/avoid_late_keyword_parameters.dart

Co-authored-by: Yurii Prykhodko <[email protected]>

* Avoid-late ignored_types fix

* Avoid-late ignored_types Fix

* Avoid-late allow_initialized testcases

* Update lint_test/avoid_late_keyword_allow_initialized_test/pubspec.yaml

Co-authored-by: Yurii Prykhodko <[email protected]>

* Update lib/lints/avoid_late_keyword/models/avoid_late_keyword_parameters.dart

Co-authored-by: Yurii Prykhodko <[email protected]>

* Allow subclasses for avoid-late whitelist

* Fix naming

* Short-circuit of there's no ignored types

* Short-circuit earlier

* Update lib/lints/avoid_late_keyword/avoid_late_keyword_rule.dart

Co-authored-by: Yurii Prykhodko <[email protected]>

* Avoid-late ignored_types tests

* Avoid-late add testcases

* Proper-super-calls impl

* Proper-super-calls format

* Apply suggestions from code review

Co-authored-by: Yurii Prykhodko <[email protected]>

* Proper-super-calls refactoring

* Update lib/lints/proper_super_calls/proper_super_calls_rule.dart

Co-authored-by: Yurii Prykhodko <[email protected]>

* Proper-super-keyword annotation check

* Proper-super-keyword format

* Proper-super-calls cleanup

---------

Co-authored-by: Yurii Prykhodko <[email protected]>
Co-authored-by: Yurii Prykhodko <[email protected]>
  • Loading branch information
3 people authored Nov 17, 2023
1 parent 6aae62c commit 414a14e
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 1 deletion.
134 changes: 134 additions & 0 deletions lib/lints/proper_super_calls/proper_super_calls_rule.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
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/models/rule_config.dart';
import 'package:solid_lints/models/solid_lint_rule.dart';

/// Checks that `super` calls in the initState and
/// dispose methods are called in the correct order.
class ProperSuperCallsRule extends SolidLintRule {
/// The [LintCode] of this lint rule that represents
/// the error whether the initState and dispose methods
/// are called in the incorrect order
static const lintName = 'proper_super_calls';
static const _initState = 'initState';
static const _dispose = 'dispose';
static const _override = 'override';

/// The [LintCode] of this lint rule that represents
/// the error whether super.initState() should be called first
static const _superInitStateCode = LintCode(
name: lintName,
problemMessage: "super.initState() should be first",
);

/// The [LintCode] of this lint rule that represents
/// the error whether super.dispose() should be called last
static const _superDisposeCode = LintCode(
name: lintName,
problemMessage: "super.dispose() should be last",
);

ProperSuperCallsRule._(super.config);

/// Creates a new instance of [ProperSuperCallsRule]
/// based on the lint configuration.
factory ProperSuperCallsRule.createRule(CustomLintConfigs configs) {
final rule = RuleConfig(
name: lintName,
configs: configs,
problemMessage: (_) => 'Proper super calls issue',
);

return ProperSuperCallsRule._(rule);
}

@override
void run(
CustomLintResolver resolver,
ErrorReporter reporter,
CustomLintContext context,
) {
context.registry.addMethodDeclaration(
(node) {
final methodName = node.name.toString();

if (methodName == _initState || methodName == _dispose) {
final statements = (node.body as BlockFunctionBody).block.statements;

_checkSuperCalls(
node,
methodName,
statements,
reporter,
);
}
},
);
}

/// This method report an error whether `super.initState()`
/// or `super.dispose()` are called incorrect
void _checkSuperCalls(
MethodDeclaration node,
String methodName,
List<Statement> statements,
ErrorReporter reporter,
) {
final hasOverrideAnnotation =
node.metadata.any((annotation) => annotation.name.name == _override);

if (!hasOverrideAnnotation) return;
if (methodName == _initState && !_isSuperInitStateCalledFirst(statements)) {
reporter.reportErrorForNode(
_superInitStateCode,
node,
);
}
if (methodName == _dispose && !_isSuperDisposeCalledLast(statements)) {
reporter.reportErrorForNode(
_superDisposeCode,
node,
);
}
}

/// Returns `true` if `super.initState()` is called before other code in the
/// `initState` method, `false` otherwise.
bool _isSuperInitStateCalledFirst(List<Statement> statements) {
if (statements.isEmpty) return false;
final firstStatement = statements.first;

if (firstStatement is ExpressionStatement) {
final expression = firstStatement.expression;

final isSuperInitStateCalledFirst = expression is MethodInvocation &&
expression.target is SuperExpression &&
expression.methodName.toString() == _initState;

return isSuperInitStateCalledFirst;
}

return false;
}

/// Returns `true` if `super.dispose()` is called at the end of the `dispose`
/// method, `false` otherwise.
bool _isSuperDisposeCalledLast(List<Statement> statements) {
if (statements.isEmpty) return false;
final lastStatement = statements.last;

if (lastStatement is ExpressionStatement) {
final expression = lastStatement.expression;

final lastStatementIsSuperDispose = expression is MethodInvocation &&
expression.target is SuperExpression &&
expression.methodName.toString() == _dispose;

return lastStatementIsSuperDispose;
}

return false;
}
}
2 changes: 2 additions & 0 deletions lib/solid_lints.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import 'package:solid_lints/lints/prefer_conditional_expressions/prefer_conditio
import 'package:solid_lints/lints/prefer_first/prefer_first_rule.dart';
import 'package:solid_lints/lints/prefer_last/prefer_last_rule.dart';
import 'package:solid_lints/lints/prefer_match_file_name/prefer_match_file_name_rule.dart';
import 'package:solid_lints/lints/proper_super_calls/proper_super_calls_rule.dart';

import 'package:solid_lints/models/solid_lint_rule.dart';

Expand Down Expand Up @@ -56,6 +57,7 @@ class _SolidLints extends PluginBase {
PreferFirstRule.createRule(configs),
PreferLastRule.createRule(configs),
PreferMatchFileNameRule.createRule(configs),
ProperSuperCallsRule.createRule(configs),
];

// Return only enabled rules
Expand Down
1 change: 1 addition & 0 deletions lint_test/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,4 @@ custom_lint:
- prefer_first
- prefer_last
- prefer_match_file_name
- proper_super_calls
2 changes: 1 addition & 1 deletion lint_test/member_ordering_test.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// ignore_for_file: unused_field, prefer_match_file_name
// ignore_for_file: unused_field, prefer_match_file_name, proper_super_calls
// ignore_for_file: unused_element
// ignore_for_file: no_empty_block

Expand Down
77 changes: 77 additions & 0 deletions lint_test/proper_super_calls_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// ignore_for_file: prefer_match_file_name, no_empty_block

class Widget {}

abstract class State<T> {
build();
dispose() {}
initState() {}
}

abstract class StatefulWidget extends Widget {
State createState();

StatefulWidget();
}

/// Check "check super" keyword fail
///
/// `proper_super_calls`
class ProperSuperCallsTest1 extends StatefulWidget {
@override
State<ProperSuperCallsTest1> createState() => _ProperSuperCallsTest1State();

ProperSuperCallsTest1();
}

class _ProperSuperCallsTest1State extends State<ProperSuperCallsTest1> {
@override
Widget build() {
return Widget();
}

// expect_lint: proper_super_calls
@override
void initState() {
print('');
super.initState();
}

// expect_lint: proper_super_calls
@override
void dispose() {
super.dispose();
print('');
}
}

class ProperSuperCallsTest2 extends StatefulWidget {
@override
State<ProperSuperCallsTest2> createState() => _ProperSuperCallsTest2State();

ProperSuperCallsTest2();
}

class _ProperSuperCallsTest2State extends State<ProperSuperCallsTest2> {
@override
Widget build() {
return Widget();
}

@override
void initState() {
super.initState();
print('');
}

@override
void dispose() {
print('');
super.dispose();
}
}

class MyClass {
dispose() {}
initState() {}
}

0 comments on commit 414a14e

Please sign in to comment.