From 3bf33b667c447d16dc90a38d8ea73517d7267ce2 Mon Sep 17 00:00:00 2001 From: Konstantin Shcheglov Date: Wed, 27 Mar 2024 00:22:59 +0000 Subject: [PATCH] Augment. Issue 55295. Report AUGMENTATION_WITHOUT_DECLARATION. Bug: https://github.com/dart-lang/sdk/issues/55295 Change-Id: I50811e1ccd7a2be0e71b18eefa9ecb76ac1aef73 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/359961 Commit-Queue: Konstantin Shcheglov Reviewed-by: Brian Wilkerson --- .../services/correction/error_fix_status.yaml | 2 + pkg/analyzer/lib/src/error/codes.g.dart | 8 + .../lib/src/error/error_code_values.g.dart | 1 + .../lib/src/generated/error_verifier.dart | 68 +++++- pkg/analyzer/messages.yaml | 3 + ...augmentation_without_declaration_test.dart | 223 ++++++++++++++++++ .../test/src/diagnostics/test_all.dart | 3 + 7 files changed, 298 insertions(+), 10 deletions(-) create mode 100644 pkg/analyzer/test/src/diagnostics/augmentation_without_declaration_test.dart diff --git a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml index a623bd3a982..9a82dea0305 100644 --- a/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml +++ b/pkg/analysis_server/lib/src/services/correction/error_fix_status.yaml @@ -209,6 +209,8 @@ CompileTimeErrorCode.ASSIGNMENT_TO_TYPE: status: noFix CompileTimeErrorCode.ASYNC_FOR_IN_WRONG_CONTEXT: status: hasFix +CompileTimeErrorCode.AUGMENTATION_WITHOUT_DECLARATION: + status: noFix CompileTimeErrorCode.AUGMENTATION_WITHOUT_IMPORT: status: needsFix notes: |- diff --git a/pkg/analyzer/lib/src/error/codes.g.dart b/pkg/analyzer/lib/src/error/codes.g.dart index ecd5cafa8db..88fa894ca63 100644 --- a/pkg/analyzer/lib/src/error/codes.g.dart +++ b/pkg/analyzer/lib/src/error/codes.g.dart @@ -214,6 +214,14 @@ class CompileTimeErrorCode extends AnalyzerErrorCode { hasPublishedDocs: true, ); + static const CompileTimeErrorCode AUGMENTATION_WITHOUT_DECLARATION = + CompileTimeErrorCode( + 'AUGMENTATION_WITHOUT_DECLARATION', + "The declaration being augmented doesn't exist.", + correctionMessage: + "Try changing the augmentation to match an existing declaration.", + ); + static const CompileTimeErrorCode AUGMENTATION_WITHOUT_IMPORT = CompileTimeErrorCode( 'AUGMENTATION_WITHOUT_IMPORT', diff --git a/pkg/analyzer/lib/src/error/error_code_values.g.dart b/pkg/analyzer/lib/src/error/error_code_values.g.dart index 8b6b5409e63..48d933a5c81 100644 --- a/pkg/analyzer/lib/src/error/error_code_values.g.dart +++ b/pkg/analyzer/lib/src/error/error_code_values.g.dart @@ -64,6 +64,7 @@ const List errorCodeValues = [ CompileTimeErrorCode.ASSIGNMENT_TO_METHOD, CompileTimeErrorCode.ASSIGNMENT_TO_TYPE, CompileTimeErrorCode.ASYNC_FOR_IN_WRONG_CONTEXT, + CompileTimeErrorCode.AUGMENTATION_WITHOUT_DECLARATION, CompileTimeErrorCode.AUGMENTATION_WITHOUT_IMPORT, CompileTimeErrorCode.AUGMENTATION_WITHOUT_LIBRARY, CompileTimeErrorCode.AWAIT_IN_LATE_LOCAL_VARIABLE_INITIALIZER, diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart index be8f3f120e2..f25edd15e6a 100644 --- a/pkg/analyzer/lib/src/generated/error_verifier.dart +++ b/pkg/analyzer/lib/src/generated/error_verifier.dart @@ -439,6 +439,12 @@ class ErrorVerifier extends RecursiveAstVisitor void visitClassDeclaration(covariant ClassDeclarationImpl node) { try { final element = node.declaredElement!; + + _checkAugmentations( + augmentKeyword: node.augmentKeyword, + element: element, + ); + final augmented = element.augmented; if (augmented == null) { return; @@ -570,6 +576,10 @@ class ErrorVerifier extends RecursiveAstVisitor } _checkForUndefinedConstructorInInitializerImplicit(node); _checkForReturnInGenerativeConstructor(node); + _checkAugmentations( + augmentKeyword: node.augmentKeyword, + element: element, + ); _reportMacroDiagnostics(element); super.visitConstructorDeclaration(node); }); @@ -781,9 +791,13 @@ class ErrorVerifier extends RecursiveAstVisitor _checkForNonFinalFieldInEnum(node); for (final field in fields.variables) { - if (field.declaredElement case final FieldElementImpl element) { - _reportMacroDiagnostics(element); - } + var element = field.declaredElement; + element as FieldElementImpl; + _checkAugmentations( + augmentKeyword: node.augmentKeyword, + element: element, + ); + _reportMacroDiagnostics(element); } super.visitFieldDeclaration(node); @@ -867,6 +881,10 @@ class ErrorVerifier extends RecursiveAstVisitor _returnTypeVerifier.verifyReturnType(returnType); _checkForMainFunction1(node.name, node.declaredElement!); _checkForMainFunction2(node); + _checkAugmentations( + augmentKeyword: node.augmentKeyword, + element: element, + ); _reportMacroDiagnostics(element); super.visitFunctionDeclaration(node); }); @@ -1068,6 +1086,10 @@ class ErrorVerifier extends RecursiveAstVisitor _checkForTypeAnnotationDeferredClass(returnType); _returnTypeVerifier.verifyReturnType(returnType); _checkForWrongTypeParameterVarianceInMethod(node); + _checkAugmentations( + augmentKeyword: node.augmentKeyword, + element: element, + ); _reportMacroDiagnostics(element); super.visitMethodDeclaration(node); }); @@ -1097,6 +1119,12 @@ class ErrorVerifier extends RecursiveAstVisitor // TODO(scheglov): Verify for all mixin errors. try { final element = node.declaredElement!; + + _checkAugmentations( + augmentKeyword: node.augmentKeyword, + element: element, + ); + final augmented = element.augmented; if (augmented == null) { return; @@ -1425,14 +1453,14 @@ class ErrorVerifier extends RecursiveAstVisitor _checkForNotInitializedNonNullableVariable(node.variables, true); for (var variable in node.variables.variables) { - _checkForMainFunction1(variable.name, variable.declaredElement!); - } - - for (final variable in node.variables.variables) { var element = variable.declaredElement; - if (element is TopLevelVariableElementImpl) { - _reportMacroDiagnostics(element); - } + element as TopLevelVariableElementImpl; + _checkForMainFunction1(variable.name, element); + _checkAugmentations( + augmentKeyword: node.augmentKeyword, + element: element, + ); + _reportMacroDiagnostics(element); } super.visitTopLevelVariableDeclaration(node); @@ -1507,6 +1535,26 @@ class ErrorVerifier extends RecursiveAstVisitor _isInLateLocalVariable.removeLast(); } + void _checkAugmentations({ + required Token? augmentKeyword, + required T element, + }) { + if (augmentKeyword == null) { + return; + } + + if (element is AugmentableElement) { + var augmentationTarget = element.augmentationTarget; + if (augmentationTarget == null) { + errorReporter.atToken( + augmentKeyword, + CompileTimeErrorCode.AUGMENTATION_WITHOUT_DECLARATION, + ); + return; + } + } + } + /// Checks the class for problems with the superclass, mixins, or implemented /// interfaces. /// diff --git a/pkg/analyzer/messages.yaml b/pkg/analyzer/messages.yaml index 35943cc4810..3441e262d85 100644 --- a/pkg/analyzer/messages.yaml +++ b/pkg/analyzer/messages.yaml @@ -1109,6 +1109,9 @@ CompileTimeErrorCode: } } ``` + AUGMENTATION_WITHOUT_DECLARATION: + problemMessage: The declaration being augmented doesn't exist. + correctionMessage: Try changing the augmentation to match an existing declaration. AUGMENTATION_WITHOUT_IMPORT: problemMessage: The library does not import this augmentation. correctionMessage: Try updating the augmented library to import this augmentation. diff --git a/pkg/analyzer/test/src/diagnostics/augmentation_without_declaration_test.dart b/pkg/analyzer/test/src/diagnostics/augmentation_without_declaration_test.dart new file mode 100644 index 00000000000..f60f0db93cf --- /dev/null +++ b/pkg/analyzer/test/src/diagnostics/augmentation_without_declaration_test.dart @@ -0,0 +1,223 @@ +// Copyright (c) 2024, 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/src/error/codes.dart'; +import 'package:test_reflective_loader/test_reflective_loader.dart'; + +import '../dart/resolution/context_collection_resolution.dart'; + +main() { + defineReflectiveSuite(() { + defineReflectiveTests(AugmentationWithoutDeclarationTest); + }); +} + +@reflectiveTest +class AugmentationWithoutDeclarationTest extends PubPackageResolutionTest { + test_class() async { + newFile('$testPackageLibPath/a.dart', r''' +import augment 'test.dart'; +'''); + + await assertErrorsInCode(r''' +library augment 'a.dart'; + +augment class A {} +''', [ + error(CompileTimeErrorCode.AUGMENTATION_WITHOUT_DECLARATION, 27, 7), + ]); + } + + test_class_constructor() async { + newFile('$testPackageLibPath/a.dart', r''' +import augment 'test.dart'; + +class A {} +'''); + + await assertErrorsInCode(r''' +library augment 'a.dart'; + +augment class A { + augment A.named(); +} +''', [ + error(CompileTimeErrorCode.AUGMENTATION_WITHOUT_DECLARATION, 47, 7), + ]); + } + + test_class_field() async { + newFile('$testPackageLibPath/a.dart', r''' +import augment 'test.dart'; + +class A {} +'''); + + await assertErrorsInCode(r''' +library augment 'a.dart'; + +augment class A { + augment int foo = 0; +} +''', [ + error(CompileTimeErrorCode.AUGMENTATION_WITHOUT_DECLARATION, 47, 7), + ]); + } + + test_class_getter() async { + newFile('$testPackageLibPath/a.dart', r''' +import augment 'test.dart'; + +class A {} +'''); + + await assertErrorsInCode(r''' +library augment 'a.dart'; + +augment class A { + augment int get foo => 0; +} +''', [ + error(CompileTimeErrorCode.AUGMENTATION_WITHOUT_DECLARATION, 47, 7), + ]); + } + + test_class_method() async { + newFile('$testPackageLibPath/a.dart', r''' +import augment 'test.dart'; + +class A {} +'''); + + await assertErrorsInCode(r''' +library augment 'a.dart'; + +augment class A { + augment void foo() {} +} +''', [ + error(CompileTimeErrorCode.AUGMENTATION_WITHOUT_DECLARATION, 47, 7), + ]); + } + + test_class_method_valid() async { + newFile('$testPackageLibPath/a.dart', r''' +import augment 'test.dart'; + +class A { + void foo() {} +} +'''); + + await assertNoErrorsInCode(r''' +library augment 'a.dart'; + +augment class A { + augment void foo() {} +} +'''); + } + + test_class_setter() async { + newFile('$testPackageLibPath/a.dart', r''' +import augment 'test.dart'; + +class A {} +'''); + + await assertErrorsInCode(r''' +library augment 'a.dart'; + +augment class A { + augment set foo(int _) {} +} +''', [ + error(CompileTimeErrorCode.AUGMENTATION_WITHOUT_DECLARATION, 47, 7), + ]); + } + + test_mixin() async { + newFile('$testPackageLibPath/a.dart', r''' +import augment 'test.dart'; +'''); + + await assertErrorsInCode(r''' +library augment 'a.dart'; + +augment mixin A {} +''', [ + error(CompileTimeErrorCode.AUGMENTATION_WITHOUT_DECLARATION, 27, 7), + ]); + } + + test_topLevel_function() async { + newFile('$testPackageLibPath/a.dart', r''' +import augment 'test.dart'; +'''); + + await assertErrorsInCode(r''' +library augment 'a.dart'; + +augment void foo() {} +''', [ + error(CompileTimeErrorCode.AUGMENTATION_WITHOUT_DECLARATION, 27, 7), + ]); + } + + test_topLevel_function_valid() async { + newFile('$testPackageLibPath/a.dart', r''' +import augment 'test.dart'; + +void foo() {} +'''); + + await assertNoErrorsInCode(r''' +library augment 'a.dart'; + +augment void foo() {} +'''); + } + + test_topLevel_getter() async { + newFile('$testPackageLibPath/a.dart', r''' +import augment 'test.dart'; +'''); + + await assertErrorsInCode(r''' +library augment 'a.dart'; + +augment int get foo => 0; +''', [ + error(CompileTimeErrorCode.AUGMENTATION_WITHOUT_DECLARATION, 27, 7), + ]); + } + + test_topLevel_setter() async { + newFile('$testPackageLibPath/a.dart', r''' +import augment 'test.dart'; +'''); + + await assertErrorsInCode(r''' +library augment 'a.dart'; + +augment set foo(int _) {} +''', [ + error(CompileTimeErrorCode.AUGMENTATION_WITHOUT_DECLARATION, 27, 7), + ]); + } + + test_topLevel_variable() async { + newFile('$testPackageLibPath/a.dart', r''' +import augment 'test.dart'; +'''); + + await assertErrorsInCode(r''' +library augment 'a.dart'; + +augment int foo = 0; +''', [ + error(CompileTimeErrorCode.AUGMENTATION_WITHOUT_DECLARATION, 27, 7), + ]); + } +} diff --git a/pkg/analyzer/test/src/diagnostics/test_all.dart b/pkg/analyzer/test/src/diagnostics/test_all.dart index ca4a7fa9692..e2b677a29b9 100644 --- a/pkg/analyzer/test/src/diagnostics/test_all.dart +++ b/pkg/analyzer/test/src/diagnostics/test_all.dart @@ -38,6 +38,8 @@ import 'assignment_to_type_test.dart' as assignment_to_type; import 'async_for_in_wrong_context_test.dart' as async_for_in_wrong_context; import 'async_keyword_used_as_identifier_test.dart' as async_keyword_used_as_identifier; +import 'augmentation_without_declaration_test.dart' + as augmentation_without_declaration; import 'await_in_late_local_variable_initializer_test.dart' as await_in_late_local_variable_initializer; import 'await_in_wrong_context_test.dart' as await_in_wrong_context; @@ -929,6 +931,7 @@ main() { assignment_to_type.main(); async_for_in_wrong_context.main(); async_keyword_used_as_identifier.main(); + augmentation_without_declaration.main(); await_in_late_local_variable_initializer.main(); await_in_wrong_context.main(); await_of_incompatible_type.main();