Skip to content
This repository has been archived by the owner on Nov 20, 2024. It is now read-only.

Commit

Permalink
extension type support for prefer_constructors_over_static_methods (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
pq authored Aug 11, 2023
1 parent 78251ed commit facc499
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 37 deletions.
42 changes: 24 additions & 18 deletions lib/src/rules/prefer_constructors_over_static_methods.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ class Point {
```
''';

bool _hasNewInvocation(DartType returnType, FunctionBody body) =>
_BodyVisitor(returnType).containsInstanceCreation(body);

// todo(pq): temporary; remove after renamed class is in the SDK
// ignore: non_constant_identifier_names
LintRule PreferConstructorsInsteadOfStaticMethods() =>
PreferConstructorsOverStaticMethods();

bool _hasNewInvocation(DartType returnType, FunctionBody body) =>
_BodyVisitor(returnType).containsInstanceCreation(body);

class PreferConstructorsOverStaticMethods extends LintRule {
static const LintCode code = LintCode(
'prefer_constructors_over_static_methods',
Expand Down Expand Up @@ -101,23 +101,29 @@ class _Visitor extends SimpleAstVisitor<void> {

@override
void visitMethodDeclaration(MethodDeclaration node) {
if (!node.isStatic) return;
if (node.typeParameters != null) return;
var returnType = node.returnType?.type;
var parent = node.parent;
if (node.isStatic &&
parent is ClassDeclaration &&
returnType is InterfaceType &&
parent.typeParameters == null &&
node.typeParameters == null) {
var declaredElement = parent.declaredElement;
if (declaredElement != null) {
var interfaceType = declaredElement.thisType;
if (!context.typeSystem.isAssignableTo(returnType, interfaceType)) {
return;
}
if (_hasNewInvocation(returnType, node.body)) {
rule.reportLintForToken(node.name);
}
if (returnType is! InterfaceType) return;

var interfaceType = node.parent.typeToCheckOrNull();
if (interfaceType != null) {
if (!context.typeSystem.isAssignableTo(returnType, interfaceType)) {
return;
}
if (_hasNewInvocation(returnType, node.body)) {
rule.reportLintForToken(node.name);
}
}
}
}

extension on AstNode? {
InterfaceType? typeToCheckOrNull() => switch (this) {
ExtensionTypeDeclaration e =>
e.typeParameters == null ? e.declaredElement?.thisType : null,
ClassDeclaration c =>
c.typeParameters == null ? c.declaredElement?.thisType : null,
_ => null
};
}
53 changes: 34 additions & 19 deletions test/rules/prefer_constructors_over_static_methods_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,23 @@ main() {

@reflectiveTest
class PreferConstructorsOverStaticMethodsTest extends LintRuleTest {
@override
List<String> get experiments => ['inline-class'];

@override
String get lintRule => 'prefer_constructors_over_static_methods';

test_extensionMethod() async {
await assertNoDiagnostics(r'''
class A {
A.named();
}
extension E on A {
static A foo() => A.named();
}
''');
}

test_factoryConstructor() async {
await assertNoDiagnostics(r'''
class A {
Expand Down Expand Up @@ -50,31 +64,23 @@ class A {
]);
}

test_staticMethod_generic() async {
await assertNoDiagnostics(r'''
class A {
A.named();
static A generic<T>() => A.named();
}
''');
}

test_staticMethod_returnsInstantiatedInstance() async {
await assertNoDiagnostics(r'''
class A<T> {
A.named();
static A<int> staticM() => A.named();
test_staticMethod_expressionBody_extensionType() async {
// Since the check logic is shared one test should be sufficient to verify
// extension types are supported.
await assertDiagnostics(r'''
extension type E(int i) {
static E make(int i) => E(i);
}
''');
''', [
lint(37, 4),
]);
}

test_extensionMethod() async {
test_staticMethod_generic() async {
await assertNoDiagnostics(r'''
class A {
A.named();
}
extension E on A {
static A foo() => A.named();
static A generic<T>() => A.named();
}
''');
}
Expand Down Expand Up @@ -104,6 +110,15 @@ class A {
''');
}

test_staticMethod_returnsInstantiatedInstance() async {
await assertNoDiagnostics(r'''
class A<T> {
A.named();
static A<int> staticM() => A.named();
}
''');
}

test_staticMethod_returnsNullable() async {
await assertNoDiagnostics(r'''
class A {
Expand Down

0 comments on commit facc499

Please sign in to comment.