From 3659b3e5854c7eb6e9eadf9697d40689f0600a3d Mon Sep 17 00:00:00 2001 From: Brian Wilkerson Date: Tue, 24 Oct 2023 23:27:59 +0000 Subject: [PATCH] Recover from exension type declarations with no representation field Change-Id: I2fdd0c281ba8a8458248d00b91a0cb1abfed02da Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/331749 Commit-Queue: Brian Wilkerson Reviewed-by: Konstantin Shcheglov --- .../extension_type_declaration_test.dart | 1 - pkg/analyzer/lib/src/fasta/ast_builder.dart | 35 +++++++++++++------ .../lib/src/test_utilities/find_node.dart | 4 +++ .../src/dart/parser/extension_type_test.dart | 27 ++++++++++++++ pkg/analyzer/test/verify_docs_test.dart | 13 ++++++- 5 files changed, 68 insertions(+), 12 deletions(-) diff --git a/pkg/analysis_server/test/services/completion/dart/location/extension_type_declaration_test.dart b/pkg/analysis_server/test/services/completion/dart/location/extension_type_declaration_test.dart index f27222a61248..b3e0f00f0ef4 100644 --- a/pkg/analysis_server/test/services/completion/dart/location/extension_type_declaration_test.dart +++ b/pkg/analysis_server/test/services/completion/dart/location/extension_type_declaration_test.dart @@ -52,7 +52,6 @@ suggestions '''); } - @FailingTest(reason: 'The AstBuilder drops the incomplete extension type') Future test_afterType_beforeEof() async { await computeSuggestions(''' extension type ^ diff --git a/pkg/analyzer/lib/src/fasta/ast_builder.dart b/pkg/analyzer/lib/src/fasta/ast_builder.dart index 957cf3d6d732..fa0f5581a7c6 100644 --- a/pkg/analyzer/lib/src/fasta/ast_builder.dart +++ b/pkg/analyzer/lib/src/fasta/ast_builder.dart @@ -1634,23 +1634,38 @@ class AstBuilder extends StackListener { Token typeKeyword, Token endToken) { final implementsClause = pop(NullValues.IdentifierList) as ImplementsClauseImpl?; - final representation = pop(const NullValue()) + var representation = pop(const NullValue()) as RepresentationDeclarationImpl?; final constKeyword = pop() as Token?; if (enableInlineClass) { final builder = _classLikeBuilder as _ExtensionTypeDeclarationBuilder; - if (representation != null) { - // TODO(scheglov): Handle missing primary constructor. - declarations.add( - builder.build( - typeKeyword: typeKeyword, - constKeyword: constKeyword, - representation: representation, - implementsClause: implementsClause, - ), + if (representation == null) { + var leftParenthesis = parser.rewriter.insertParens(builder.name, true); + var typeName = leftParenthesis.next!; + var rightParenthesis = leftParenthesis.endGroup!; + var fieldName = parser.rewriter.insertSyntheticIdentifier(typeName); + representation = RepresentationDeclarationImpl( + constructorName: null, + leftParenthesis: leftParenthesis, + fieldMetadata: [], + fieldType: NamedTypeImpl( + importPrefix: null, + name2: typeName, + question: null, + typeArguments: null), + fieldName: fieldName, + rightParenthesis: rightParenthesis, ); } + declarations.add( + builder.build( + typeKeyword: typeKeyword, + constKeyword: constKeyword, + representation: representation, + implementsClause: implementsClause, + ), + ); } else { _reportFeatureNotEnabled( feature: ExperimentalFeatures.inline_class, diff --git a/pkg/analyzer/lib/src/test_utilities/find_node.dart b/pkg/analyzer/lib/src/test_utilities/find_node.dart index a9e3bcd15108..c7e6a4f533aa 100644 --- a/pkg/analyzer/lib/src/test_utilities/find_node.dart +++ b/pkg/analyzer/lib/src/test_utilities/find_node.dart @@ -391,6 +391,10 @@ class FindNode { return _node(search, (n) => n is ExtensionOverride); } + ExtensionTypeDeclaration extensionTypeDeclaration(String search) { + return _node(search, (n) => n is ExtensionTypeDeclaration); + } + FieldDeclaration fieldDeclaration(String search) { return _node(search, (n) => n is FieldDeclaration); } diff --git a/pkg/analyzer/test/src/dart/parser/extension_type_test.dart b/pkg/analyzer/test/src/dart/parser/extension_type_test.dart index f8f1f8933b99..f972a71cc0b6 100644 --- a/pkg/analyzer/test/src/dart/parser/extension_type_test.dart +++ b/pkg/analyzer/test/src/dart/parser/extension_type_test.dart @@ -778,6 +778,33 @@ ExtensionTypeDeclaration '''); } + void test_primaryConstructor_missing() { + var parseResult = parseStringWithErrors(r''' +extension type E {} +'''); + parseResult.assertErrors( + [error(ParserErrorCode.MISSING_PRIMARY_CONSTRUCTOR, 15, 1)]); + + final node = parseResult.findNode.extensionTypeDeclaration('E'); + assertParsedNodeText( + node, + r''' +ExtensionTypeDeclaration + extensionKeyword: extension @0 + typeKeyword: type @10 + name: E @15 + representation: RepresentationDeclaration + leftParenthesis: ( @17 + fieldType: NamedType + name: @17 + fieldName: @17 + rightParenthesis: ) @17 + leftBracket: { @17 + rightBracket: } @18 +''', + withOffsets: true); + } + test_primaryConstructor_named() { final parseResult = parseStringWithErrors(r''' extension type A.named(int it) {} diff --git a/pkg/analyzer/test/verify_docs_test.dart b/pkg/analyzer/test/verify_docs_test.dart index d62b01be9fe9..1b98a60a0d08 100644 --- a/pkg/analyzer/test/verify_docs_test.dart +++ b/pkg/analyzer/test/verify_docs_test.dart @@ -43,6 +43,15 @@ class SnippetTester { SnippetTester._( this.provider, this.docFolder, this.snippetDirPath, this.snippetPath); + /// Return `true` if the given error is a diagnostic produced by a lint that + /// is allowed to occur in documentation. + bool isAllowedLint(AnalysisError error) { + var errorCode = error.errorCode; + return errorCode is LintCode && + errorCode.name == 'non_constant_identifier_names' && + error.message.contains("'test_"); + } + Future verify() async { await verifyFolder(docFolder); } @@ -128,8 +137,10 @@ $snippet if (results is ErrorsResult) { Iterable errors = results.errors.where((error) { ErrorCode errorCode = error.errorCode; + // TODO(brianwilkerson) return errorCode != WarningCode.UNUSED_IMPORT && - errorCode != HintCode.UNUSED_LOCAL_VARIABLE; + errorCode != HintCode.UNUSED_LOCAL_VARIABLE && + !isAllowedLint(error); }); if (errors.isNotEmpty) { String filePath =