Skip to content

Commit

Permalink
add unnecessary_null_aware_operator_on_extension_on_nullable (dart-la…
Browse files Browse the repository at this point in the history
…ng/linter#3392)

* add unnecessary_null_aware_operator_on_extension_on_nullable

* Address review comment
  • Loading branch information
a14n authored May 11, 2022
1 parent d1387b6 commit ba6c1b8
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 0 deletions.
1 change: 1 addition & 0 deletions example/all.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ linter:
- unnecessary_late
- unnecessary_new
- unnecessary_null_aware_assignments
- unnecessary_null_aware_operator_on_extension_on_nullable
- unnecessary_null_checks
- unnecessary_null_in_if_null_operators
- unnecessary_nullable_for_final_variable_declarations
Expand Down
2 changes: 2 additions & 0 deletions lib/src/rules.dart
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ import 'rules/unnecessary_lambdas.dart';
import 'rules/unnecessary_late.dart';
import 'rules/unnecessary_new.dart';
import 'rules/unnecessary_null_aware_assignments.dart';
import 'rules/unnecessary_null_aware_operator_on_extension_on_nullable.dart';
import 'rules/unnecessary_null_checks.dart';
import 'rules/unnecessary_null_in_if_null_operators.dart';
import 'rules/unnecessary_nullable_for_final_variable_declarations.dart';
Expand Down Expand Up @@ -391,6 +392,7 @@ void registerLintRules({bool inTestMode = false}) {
..register(UnnecessaryLambdas())
..register(UnnecessaryLate())
..register(UnnecessaryNullableForFinalVariableDeclarations())
..register(UnnecessaryNullAwareOperatorOnExtensionOnNullable())
..register(UnnecessaryNullChecks())
..register(UnnecessaryOverrides())
..register(UnnecessaryParenthesis())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';

import '../analyzer.dart';

const _desc =
r'Unnecessary null aware operator on extension on a nullable type';

const _details = r'''
Avoid null aware operators for members defined in an extension on a nullable type.
**BAD:**
```dart
extension E on int? {
int m() => 1;
}
f(int? i) => i?.m();
```
**GOOD:**
```dart
extension E on int? {
int m() => 1;
}
f(int? i) => i.m();
```
''';

class UnnecessaryNullAwareOperatorOnExtensionOnNullable extends LintRule {
UnnecessaryNullAwareOperatorOnExtensionOnNullable()
: super(
name: 'unnecessary_null_aware_operator_on_extension_on_nullable',
description: _desc,
details: _details,
group: Group.style,
);

@override
void registerNodeProcessors(
NodeLintRegistry registry, LinterContext context) {
var visitor = _Visitor(this, context);
registry.addIndexExpression(this, visitor);
registry.addMethodInvocation(this, visitor);
registry.addPropertyAccess(this, visitor);
}
}

class _Visitor extends SimpleAstVisitor<void> {
_Visitor(this.rule, this.context);

final LintRule rule;
final LinterContext context;

@override
void visitIndexExpression(IndexExpression node) {
if (node.isNullAware &&
_isExtensionOnNullableType(node.inSetterContext()
? (node.thisOrAncestorOfType<AssignmentExpression>())
?.writeElement
?.enclosingElement
: node.staticElement?.enclosingElement)) {
rule.reportLintForToken(node.question);
}
}

@override
void visitMethodInvocation(MethodInvocation node) {
if (node.isNullAware &&
_isExtensionOnNullableType(
node.methodName.staticElement?.enclosingElement)) {
rule.reportLintForToken(node.operator);
}
}

@override
void visitPropertyAccess(PropertyAccess node) {
if (node.isNullAware) {
var realParent = node.thisOrAncestorMatching(
(p) => p != node && p is! ParenthesizedExpression);
if (_isExtensionOnNullableType(realParent is AssignmentExpression
? realParent.writeElement?.enclosingElement
: node.propertyName.staticElement?.enclosingElement)) {
rule.reportLintForToken(node.operator);
}
}
}

bool _isExtensionOnNullableType(Element? enclosingElement) =>
enclosingElement is ExtensionElement &&
context.typeSystem.isNullable(enclosingElement.extendedType);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// test w/ `dart test -N unnecessary_null_aware_operator_on_extension_on_nullable`

extension E on int? {
int get foo => 1;
void set foo(int v) {}
String operator [](int i) => '';
void operator []=(int i, String v) {}
int m() => 1;
}

f(int? i) {
i?.foo; // LINT
i.foo; // OK
E(i)?.foo; // LINT
E(i).foo; // OK

i?.foo = 1; // LINT
i.foo = 1; // OK
E(i)?.foo = 1; // LINT
E(i).foo = 1; // OK

i?[0]; // LINT
i[0]; // OK
E(i)?[0]; // LINT
E(i)[0]; // OK

i?[0] = ''; // LINT
i[0] = ''; // OK
E(i)?[0] = ''; // LINT
E(i)[0] = ''; // OK

i?.m(); // LINT
i.m(); // OK
E(i)?.m(); // LINT
E(i).m(); // OK
}

0 comments on commit ba6c1b8

Please sign in to comment.