lint: correct some simple unnecessary_parenthesis cases found internally
Fixes https://github.com/dart-lang/linter/issues/4354
Cq-Include-Trybots: luci.dart.try:flutter-analyze-try,analyzer-win-release-try,pkg-win-release-try
Change-Id: Ie0138093467ff266d4f06c70f280660c4fc0783d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/376540
Commit-Queue: Samuel Rawlins <srawlins@google.com>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
diff --git a/pkg/analyzer/lib/src/lint/pub.dart b/pkg/analyzer/lib/src/lint/pub.dart
index 626c5a8..254fcfc 100644
--- a/pkg/analyzer/lib/src/lint/pub.dart
+++ b/pkg/analyzer/lib/src/lint/pub.dart
@@ -143,7 +143,7 @@
PSEntry(this.key, this.value);
@override
- String toString() => '${key != null ? ('$key: ') : ''}$value';
+ String toString() => '${key != null ? '$key: ' : ''}$value';
}
/// Representation of environment in `pubspec.yaml`.
diff --git a/pkg/linter/lib/src/extensions.dart b/pkg/linter/lib/src/extensions.dart
index 03cb544..1f75352 100644
--- a/pkg/linter/lib/src/extensions.dart
+++ b/pkg/linter/lib/src/extensions.dart
@@ -100,29 +100,29 @@
return null;
}
+ /// Whether the expression is null-aware, or if one of its recursive targets
+ /// is null-aware.
+ bool get containsNullAwareInvocationInChain {
+ var node = this;
+ if (node is PropertyAccess) {
+ if (node.isNullAware) return true;
+ return node.target.containsNullAwareInvocationInChain;
+ } else if (node is MethodInvocation) {
+ if (node.isNullAware) return true;
+ return node.target.containsNullAwareInvocationInChain;
+ } else if (node is IndexExpression) {
+ if (node.isNullAware) return true;
+ return node.target.containsNullAwareInvocationInChain;
+ }
+ return false;
+ }
+
bool get isFieldNameShortcut {
var node = this;
if (node is NullCheckPattern) node = node.parent;
if (node is NullAssertPattern) node = node.parent;
return node is PatternField && node.name != null && node.name?.name == null;
}
-
- /// Return `true` if the expression is null aware, or if one of its recursive
- /// targets is null aware.
- bool containsNullAwareInvocationInChain() {
- var node = this;
- if (node is PropertyAccess) {
- if (node.isNullAware) return true;
- return node.target.containsNullAwareInvocationInChain();
- } else if (node is MethodInvocation) {
- if (node.isNullAware) return true;
- return node.target.containsNullAwareInvocationInChain();
- } else if (node is IndexExpression) {
- if (node.isNullAware) return true;
- return node.target.containsNullAwareInvocationInChain();
- }
- return false;
- }
}
extension BlockExtension on Block {
diff --git a/pkg/linter/lib/src/rules/avoid_function_literals_in_foreach_calls.dart b/pkg/linter/lib/src/rules/avoid_function_literals_in_foreach_calls.dart
index 1731b23..13a2fd7 100644
--- a/pkg/linter/lib/src/rules/avoid_function_literals_in_foreach_calls.dart
+++ b/pkg/linter/lib/src/rules/avoid_function_literals_in_foreach_calls.dart
@@ -67,7 +67,7 @@
node.argumentList.arguments.isNotEmpty &&
node.argumentList.arguments.first is FunctionExpression &&
_isIterable(target.staticType) &&
- !node.containsNullAwareInvocationInChain() &&
+ !node.containsNullAwareInvocationInChain &&
!_hasMethodChaining(node) &&
!_isInsideCascade(node)) {
rule.reportLint(node.function);
diff --git a/pkg/linter/lib/src/rules/unnecessary_lambdas.dart b/pkg/linter/lib/src/rules/unnecessary_lambdas.dart
index 9bfb7b7..33cdec7 100644
--- a/pkg/linter/lib/src/rules/unnecessary_lambdas.dart
+++ b/pkg/linter/lib/src/rules/unnecessary_lambdas.dart
@@ -204,7 +204,7 @@
}
var checker = _FinalExpressionChecker(parameters);
- if (!node.containsNullAwareInvocationInChain() &&
+ if (!node.containsNullAwareInvocationInChain &&
checker.isFinalNode(node.target) &&
node.methodName.staticElement.isFinal &&
node.typeArguments == null) {
diff --git a/pkg/linter/lib/src/rules/unnecessary_parenthesis.dart b/pkg/linter/lib/src/rules/unnecessary_parenthesis.dart
index 38fd7f8..ec6bbac 100644
--- a/pkg/linter/lib/src/rules/unnecessary_parenthesis.dart
+++ b/pkg/linter/lib/src/rules/unnecessary_parenthesis.dart
@@ -104,8 +104,8 @@
// `(List<int>).toString()` is OK.
if (expression is TypeLiteral) return;
- if (expression is SimpleIdentifier ||
- expression.containsNullAwareInvocationInChain()) {
+ if (expression.isOneToken ||
+ expression.containsNullAwareInvocationInChain) {
if (parent is PropertyAccess) {
var name = parent.propertyName.name;
if (name == 'hashCode' || name == 'runtimeType') {
@@ -233,9 +233,10 @@
// inside one of the following nodes, the readability is not affected.
if (parent is! AssignmentExpression &&
parent is! ConstructorFieldInitializer &&
- parent is! VariableDeclaration &&
parent is! ExpressionFunctionBody &&
+ parent is! RecordLiteral &&
parent is! ReturnStatement &&
+ parent is! VariableDeclaration &&
parent is! YieldStatement &&
!node.isArgument) {
return;
@@ -350,4 +351,15 @@
bool get isArgument =>
parent is ArgumentList ||
(parent is NamedExpression && parent?.parent is ArgumentList);
+
+ /// Whether this expression is a sigle token.
+ ///
+ /// This excludes type literals because they often need to be parenthesized.
+ bool get isOneToken =>
+ this is SimpleIdentifier ||
+ this is StringLiteral ||
+ this is IntegerLiteral ||
+ this is DoubleLiteral ||
+ this is NullLiteral ||
+ this is BooleanLiteral;
}
diff --git a/pkg/linter/test/rules/unnecessary_parenthesis_test.dart b/pkg/linter/test/rules/unnecessary_parenthesis_test.dart
index bf5eb07..cce1ae4 100644
--- a/pkg/linter/test/rules/unnecessary_parenthesis_test.dart
+++ b/pkg/linter/test/rules/unnecessary_parenthesis_test.dart
@@ -57,6 +57,17 @@
''');
}
+ test_binaryExpressionInside_constructorFieldInitializer() async {
+ await assertDiagnostics(r'''
+class C {
+ bool f;
+ C() : f = (true && false);
+}
+''', [
+ lint(32, 15),
+ ]);
+ }
+
test_binaryExpressionInside_namedArgument() async {
await assertDiagnostics(r'''
void f({required int p}) {
@@ -83,6 +94,16 @@
''');
}
+ test_binaryExpressionInside_recordLiteral() async {
+ await assertDiagnostics(r'''
+Record f() {
+ return (1, (2 + 2));
+}
+''', [
+ lint(26, 7),
+ ]);
+ }
+
test_binaryExpressionInside_returnExpression() async {
await assertDiagnostics(r'''
bool f() {
@@ -93,6 +114,26 @@
]);
}
+ /// https://github.com/dart-lang/linter/issues/4041
+ test_cascadeAssignmentInside_nullAware() async {
+ await assertNoDiagnostics(r'''
+class A {
+ var b = false;
+ void m() {}
+ set setter(int i) {}
+}
+
+void f(A? a) {
+ (a?..b = true)?.m();
+ (a?..b = true)?.setter = 1;
+}
+
+void g(List<int>? list) {
+ (list?..[0] = 1)?.length;
+}
+''');
+ }
+
test_conditionalExpressionInside_argument() async {
await assertDiagnostics(r'''
void f(int p) {
@@ -141,26 +182,6 @@
''');
}
- test_constructorFieldInitializer_binaryInside() async {
- await assertDiagnostics(r'''
-class C {
- bool f;
- C() : f = (true && false);
-}
-''', [
- lint(32, 15),
- ]);
- }
-
- test_constructorFieldInitializer_equalityInside() async {
- await assertNoDiagnostics(r'''
-class C {
- bool f;
- C() : f = (1 == 2);
-}
-''');
- }
-
test_constructorFieldInitializer_functionExpressionInAssignment() async {
await assertNoDiagnostics(r'''
class C {
@@ -171,29 +192,6 @@
''');
}
- /// https://github.com/dart-lang/linter/issues/1395
- test_constructorFieldInitializer_functionExpressionInCascade() async {
- await assertNoDiagnostics(r'''
-class C {
- Object f;
-
- C() : f = (C()..f = () => 42);
-}
-''');
- }
-
- /// https://github.com/dart-lang/linter/issues/1395
- test_constructorFieldInitializer_functionExpressionInCascade2() async {
- await assertNoDiagnostics(r'''
-class C {
- dynamic f;
-
- C()
- : f = (C()..f = (C()..f = () => 42));
-}
-''');
- }
-
test_constructorFieldInitializer_functionExpressionInNullAware() async {
await assertNoDiagnostics(r'''
class C {
@@ -215,6 +213,15 @@
''');
}
+ test_equalityInside_constructorFieldInitializer() async {
+ await assertNoDiagnostics(r'''
+class C {
+ bool f;
+ C() : f = (1 == 2);
+}
+''');
+ }
+
test_equalityInside_expressionBody() async {
await assertDiagnostics(r'''
bool f() => (1 == 1);
@@ -223,15 +230,7 @@
]);
}
- test_inListLiteral() async {
- await assertDiagnostics(r'''
-final items = [1, (DateTime.now())];
-''', [
- lint(18, 16),
- ]);
- }
-
- test_isExpressionInside_targetOfMethodInvocation() async {
+ test_expressionInside_targetOfMethodInvocation() async {
await assertNoDiagnostics(r'''
void f(Object o) {
(o is num).toString();
@@ -239,6 +238,37 @@
''');
}
+ /// https://github.com/dart-lang/linter/issues/1395
+ test_functionExpressionInCascade2Inside_constructorFieldInitializer() async {
+ await assertNoDiagnostics(r'''
+class C {
+ dynamic f;
+
+ C()
+ : f = (C()..f = (C()..f = () => 42));
+}
+''');
+ }
+
+ /// https://github.com/dart-lang/linter/issues/1395
+ test_functionExpressionInCascadeInside_constructorFieldInitializer() async {
+ await assertNoDiagnostics(r'''
+class C {
+ Object f;
+
+ C() : f = (C()..f = () => 42);
+}
+''');
+ }
+
+ test_listLiteral() async {
+ await assertDiagnostics(r'''
+final items = [1, (DateTime.now())];
+''', [
+ lint(18, 16),
+ ]);
+ }
+
test_notEqualInside_returnExpression() async {
await assertNoDiagnostics(r'''
bool f() {
@@ -247,24 +277,15 @@
''');
}
- /// https://github.com/dart-lang/linter/issues/4041
- test_nullAware_cascadeAssignment() async {
- await assertNoDiagnostics(r'''
-class A {
- var b = false;
- void m() {}
- set setter(int i) {}
+ @FailingTest(issue: 'https://github.com/dart-lang/linter/issues/4062')
+ test_parenthesizedPattern_nonPatternOutside() async {
+ await assertDiagnostics(r'''
+void f(num n) {
+ if (1 case (int())) {}
}
-
-void f(A? a) {
- (a?..b = true)?.m();
- (a?..b = true)?.setter = 1;
-}
-
-void g(List<int>? list) {
- (list?..[0] = 1)?.length;
-}
-''');
+''', [
+ lint(20, 7),
+ ]);
}
test_positionalArgument() async {
@@ -293,7 +314,17 @@
''');
}
- test_record_assignment() async {
+ test_propertyAccessInside_recordLiteral() async {
+ await assertDiagnostics(r'''
+Record f() {
+ return (1.isEven, (2.isEven));
+}
+''', [
+ lint(33, 10),
+ ]);
+ }
+
+ test_recordInside_assignment() async {
await assertDiagnostics(r'''
void f() {
(int,) r = ((3,));
@@ -303,7 +334,7 @@
]);
}
- test_record_namedParam() async {
+ test_recordInside_namedParam() async {
await assertDiagnostics(r'''
void f() {
g(i: ((3,)));
@@ -315,7 +346,7 @@
]);
}
- test_record_param() async {
+ test_recordInside_param() async {
await assertDiagnostics(r'''
void f() {
g(((3,)));
@@ -340,7 +371,7 @@
]);
}
- test_singleElementRecordWithNoTrailingComma_namedParam() async {
+ test_singleElementRecordWithNoTrailingCommaInside_namedArgument() async {
await assertDiagnostics(r'''
void f() {
g(i: (3));
@@ -357,7 +388,7 @@
}
/// https://github.com/dart-lang/linter/issues/4876
- test_singleElementRecordWithNoTrailingComma_param() async {
+ test_singleElementRecordWithNoTrailingCommaInside_positionalArgument() async {
await assertDiagnostics(r'''
void f() {
g((3));
@@ -389,15 +420,17 @@
''');
}
- test_switchExpression_expressionStatement() async {
- await assertNoDiagnostics(r'''
-void f(Object? x) {
- (switch (x) { _ => 0 });
+ test_stringLiteralInside() async {
+ await assertDiagnostics(r'''
+void f() {
+ '' + ('');
}
-''');
+''', [
+ lint(18, 4),
+ ]);
}
- test_switchExpression_invocationArgument() async {
+ test_switchExpressionInside_argument() async {
await assertDiagnostics(r'''
void f(Object? x) {
print((switch (x) { _ => 0 }));
@@ -407,7 +440,15 @@
]);
}
- test_switchExpression_variableDeclaration() async {
+ test_switchExpressionInside_expressionStatement() async {
+ await assertNoDiagnostics(r'''
+void f(Object? x) {
+ (switch (x) { _ => 0 });
+}
+''');
+ }
+
+ test_switchExpressionInside_variableDeclaration() async {
await assertDiagnostics(r'''
void f(Object? x) {
final v = (switch (x) { _ => 0 });