diff --git a/pkg/linter/lib/src/rules/unnecessary_parenthesis.dart b/pkg/linter/lib/src/rules/unnecessary_parenthesis.dart index 95116899170..34e4724aa99 100644 --- a/pkg/linter/lib/src/rules/unnecessary_parenthesis.dart +++ b/pkg/linter/lib/src/rules/unnecessary_parenthesis.dart @@ -6,6 +6,7 @@ import 'package:analyzer/dart/ast/ast.dart'; import 'package:analyzer/dart/ast/token.dart'; import 'package:analyzer/dart/ast/visitor.dart'; import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/dart/element/type.dart'; import '../analyzer.dart'; import '../extensions.dart'; @@ -92,6 +93,26 @@ class _Visitor extends SimpleAstVisitor { var parent = node.parent; // case const (a + b): if (parent is ConstantPattern) return; + + // Don't over-report on records missing trailing commas. + // (int,) r = (3); + if (parent is VariableDeclaration && + parent.declaredElement?.type is RecordType) { + if (node.expression is! RecordLiteral) return; + } + // g((3)); => g((int,) i) { } + if (parent is ArgumentList) { + var element = node.staticParameterElement; + if (element?.type is RecordType && node.expression is! RecordLiteral) { + return; + } + } + // g(i: (3)); => g({required (int,) i}) { } + if (parent is NamedExpression && + parent.staticParameterElement?.type is RecordType) { + if (node.expression is! RecordLiteral) return; + } + var expression = node.expression; if (expression is SimpleIdentifier || expression.containsNullAwareInvocationInChain()) { diff --git a/pkg/linter/test/rules/unnecessary_parenthesis_test.dart b/pkg/linter/test/rules/unnecessary_parenthesis_test.dart index 5601bce9bce..0db77292a6f 100644 --- a/pkg/linter/test/rules/unnecessary_parenthesis_test.dart +++ b/pkg/linter/test/rules/unnecessary_parenthesis_test.dart @@ -51,6 +51,84 @@ void g(List? list) { '''); } + test_record_assignment() async { + await assertDiagnostics(r''' +void f() { + (int,) r = ((3,)); +} +''', [ + lint(24, 6), + ]); + } + + test_record_namedParam() async { + await assertDiagnostics(r''' +void f() { + g(i: ((3,))); +} +g({required (int,) i}) { } + +''', [ + lint(18, 6), + ]); + } + + test_record_param() async { + await assertDiagnostics(r''' +void f() { + g(((3,))); +} +g((int,) i) { } + +''', [ + lint(15, 6), + ]); + } + + test_singleElementRecordWithNoTrailingComma_assignment() async { + await assertDiagnostics(r''' +void f() { + (int,) r = (3); +} +''', [ + error( + CompileTimeErrorCode.RECORD_LITERAL_ONE_POSITIONAL_NO_TRAILING_COMMA, + 24, + 3), + ]); + } + + test_singleElementRecordWithNoTrailingComma_namedParam() async { + await assertDiagnostics(r''' +f() { + g(i: (3)); +} + +g({required (int,) i}) { } +''', [ + error( + CompileTimeErrorCode.RECORD_LITERAL_ONE_POSITIONAL_NO_TRAILING_COMMA, + 13, + 3), + ]); + } + + /// https://github.com/dart-lang/linter/issues/4876 + test_singleElementRecordWithNoTrailingComma_param() async { + await assertDiagnostics(r''' +f() { + g((3)); +} + +g((int,) i) { } +''', [ + error( + CompileTimeErrorCode.RECORD_LITERAL_ONE_POSITIONAL_NO_TRAILING_COMMA, + 14, + 3), + ]); + } + test_switchExpression_expressionStatement() async { await assertNoDiagnostics(r''' void f(Object? x) {