Augment. Report AUGMENTATION_EXTENDS_CLAUSE_ALREADY_PRESENT

Change-Id: I5590b68366a12129e42e19e26ac3ec4fa8330149
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/372221
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Konstantin Shcheglov 2024-06-21 15:55:11 +00:00 committed by Commit Queue
parent 7d649303c2
commit 35cae3a8b8
11 changed files with 162 additions and 11 deletions

View file

@ -203,6 +203,8 @@ CompileTimeErrorCode.ASSIGNMENT_TO_TYPE:
status: noFix
CompileTimeErrorCode.ASYNC_FOR_IN_WRONG_CONTEXT:
status: hasFix
CompileTimeErrorCode.AUGMENTATION_EXTENDS_CLAUSE_ALREADY_PRESENT:
status: needsEvaluation
CompileTimeErrorCode.AUGMENTATION_MODIFIER_EXTRA:
status: hasFix
CompileTimeErrorCode.AUGMENTATION_MODIFIER_MISSING:

View file

@ -95,7 +95,7 @@ import 'package:meta/meta.dart';
// TODO(scheglov): Clean up the list of implicitly analyzed files.
class AnalysisDriver {
/// The version of data format, should be incremented on every format change.
static const int DATA_VERSION = 367;
static const int DATA_VERSION = 368;
/// The number of exception contexts allowed to write. Once this field is
/// zero, we stop writing any new exception contexts in this process.

View file

@ -317,6 +317,14 @@ class ClassElementImpl extends ClassOrMixinElementImpl
super.fields = fields;
}
bool get hasExtendsClause {
return hasModifier(Modifier.HAS_EXTENDS_CLAUSE);
}
set hasExtendsClause(bool value) {
setModifier(Modifier.HAS_EXTENDS_CLAUSE, value);
}
bool get hasGenerativeConstConstructor {
return constructors.any((c) => !c.isFactory && c.isConst);
}
@ -5463,6 +5471,9 @@ enum Modifier {
/// Indicates that the pseudo-modifier 'get' was applied to the element.
GETTER,
/// Indicates that this class has an explicit `extends` clause.
HAS_EXTENDS_CLAUSE,
/// A flag used for libraries indicating that the variable has an explicit
/// initializer.
HAS_INITIALIZER,

View file

@ -214,6 +214,17 @@ class CompileTimeErrorCode extends AnalyzerErrorCode {
hasPublishedDocs: true,
);
static const CompileTimeErrorCode
AUGMENTATION_EXTENDS_CLAUSE_ALREADY_PRESENT = CompileTimeErrorCode(
'AUGMENTATION_EXTENDS_CLAUSE_ALREADY_PRESENT',
"The augmentation has an 'extends' clause, but an augmentation target "
"already includes an 'extends' clause and it isn't allowed to be "
"repeated or changed.",
correctionMessage:
"Try removing the 'extends' clause, either here or in the augmentation "
"target.",
);
/// Parameters:
/// 0: the lexeme of the modifier.
static const CompileTimeErrorCode AUGMENTATION_MODIFIER_EXTRA =

View file

@ -63,6 +63,7 @@ const List<ErrorCode> errorCodeValues = [
CompileTimeErrorCode.ASSIGNMENT_TO_METHOD,
CompileTimeErrorCode.ASSIGNMENT_TO_TYPE,
CompileTimeErrorCode.ASYNC_FOR_IN_WRONG_CONTEXT,
CompileTimeErrorCode.AUGMENTATION_EXTENDS_CLAUSE_ALREADY_PRESENT,
CompileTimeErrorCode.AUGMENTATION_MODIFIER_EXTRA,
CompileTimeErrorCode.AUGMENTATION_MODIFIER_MISSING,
CompileTimeErrorCode.AUGMENTATION_OF_DIFFERENT_DECLARATION_KIND,

View file

@ -459,6 +459,11 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
augmentationElement: element,
);
_checkClassAugmentationTargetAlreadyHasExtendsClause(
node: node,
augmentationTarget: element.augmentationTarget,
);
_isInNativeClass = node.nativeClause != null;
var augmented = element.augmented;
@ -1704,6 +1709,36 @@ class ErrorVerifier extends RecursiveAstVisitor<void>
);
}
void _checkClassAugmentationTargetAlreadyHasExtendsClause({
required ClassDeclarationImpl node,
required ClassElementImpl? augmentationTarget,
}) {
var extendsClause = node.extendsClause;
if (extendsClause == null) {
return;
}
while (augmentationTarget != null) {
if (augmentationTarget.hasExtendsClause) {
errorReporter.atToken(
extendsClause.extendsKeyword,
CompileTimeErrorCode.AUGMENTATION_EXTENDS_CLAUSE_ALREADY_PRESENT,
contextMessages: [
DiagnosticMessageImpl(
filePath: augmentationTarget.source.fullName,
offset: augmentationTarget.nameOffset,
length: augmentationTarget.nameLength,
message: 'The extends clause is included here.',
url: null,
),
],
);
return;
}
augmentationTarget = augmentationTarget.augmentationTarget;
}
}
/// Checks the class for problems with the superclass, mixins, or implemented
/// interfaces.
///

View file

@ -102,6 +102,7 @@ class ElementBuilder extends ThrowingAstVisitor<void> {
element.isAbstract = true;
element.isSealed = true;
}
element.hasExtendsClause = node.extendsClause != null;
element.metadata = _buildAnnotations(node.metadata);
_setCodeRange(element, node);
_setDocumentation(element, node);

View file

@ -7,22 +7,24 @@ import 'package:analyzer/src/summary2/data_reader.dart';
import 'package:analyzer/src/summary2/data_writer.dart';
class ClassElementFlags {
static const int _isAbstract = 1 << 0;
static const int _isAugmentation = 1 << 1;
static const int _isBase = 1 << 2;
static const int _isFinal = 1 << 3;
static const int _isInterface = 1 << 4;
static const int _isMacro = 1 << 5;
static const int _isMixinApplication = 1 << 6;
static const int _isMixinClass = 1 << 7;
static const int _isSealed = 1 << 8;
static const int _isSimplyBounded = 1 << 9;
static const int _hasExtendsClause = 1 << 0;
static const int _isAbstract = 1 << 1;
static const int _isAugmentation = 1 << 2;
static const int _isBase = 1 << 3;
static const int _isFinal = 1 << 4;
static const int _isInterface = 1 << 5;
static const int _isMacro = 1 << 6;
static const int _isMixinApplication = 1 << 7;
static const int _isMixinClass = 1 << 8;
static const int _isSealed = 1 << 9;
static const int _isSimplyBounded = 1 << 10;
static void read(
SummaryDataReader reader,
ClassElementImpl element,
) {
var byte = reader.readUInt30();
element.hasExtendsClause = (byte & _hasExtendsClause) != 0;
element.isAbstract = (byte & _isAbstract) != 0;
element.isAugmentation = (byte & _isAugmentation) != 0;
element.isBase = (byte & _isBase) != 0;
@ -40,6 +42,7 @@ class ClassElementFlags {
ClassElementImpl element,
) {
var result = 0;
result |= element.hasExtendsClause ? _hasExtendsClause : 0;
result |= element.isAbstract ? _isAbstract : 0;
result |= element.isAugmentation ? _isAugmentation : 0;
result |= element.isBase ? _isBase : 0;

View file

@ -1131,6 +1131,9 @@ CompileTimeErrorCode:
}
}
```
AUGMENTATION_EXTENDS_CLAUSE_ALREADY_PRESENT:
problemMessage: The augmentation has an 'extends' clause, but an augmentation target already includes an 'extends' clause and it isn't allowed to be repeated or changed.
correctionMessage: Try removing the 'extends' clause, either here or in the augmentation target.
AUGMENTATION_MODIFIER_EXTRA:
problemMessage: The augmentation has the '{0}' modifier that the declaration doesn't have.
correctionMessage: Try removing the '{0}' modifier, or adding it to the declaration.

View file

@ -0,0 +1,81 @@
// 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(AugmentationExtendsClauseAlreadyPresentTest);
});
}
@reflectiveTest
class AugmentationExtendsClauseAlreadyPresentTest
extends PubPackageResolutionTest {
test_alreadyPresent() async {
var a = newFile('$testPackageLibPath/a.dart', r'''
import augment 'test.dart';
class A {}
class B extends A {};
''');
await assertErrorsInCode(r'''
augment library 'a.dart';
augment class B extends A {}
''', [
error(CompileTimeErrorCode.AUGMENTATION_EXTENDS_CLAUSE_ALREADY_PRESENT,
43, 7,
contextMessages: [message(a, 47, 1)]),
]);
}
test_alreadyPresent2() async {
var a = newFile('$testPackageLibPath/a.dart', r'''
import augment 'b.dart';
import augment 'test.dart';
class A {}
class B extends A {};
''');
newFile('$testPackageLibPath/b.dart', r'''
augment library 'a.dart';
augment class B {}
''');
await assertErrorsInCode(r'''
augment library 'a.dart';
augment class B extends A {}
''', [
error(CompileTimeErrorCode.AUGMENTATION_EXTENDS_CLAUSE_ALREADY_PRESENT,
43, 7,
contextMessages: [message(a, 72, 1)]),
]);
}
test_notPresent() async {
newFile('$testPackageLibPath/a.dart', r'''
import augment 'test.dart';
class A {}
class B {};
''');
await assertNoErrorsInCode(r'''
augment library 'a.dart';
augment class B extends A {}
''');
}
}

View file

@ -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_extends_clause_already_present_test.dart'
as augmentation_extends_clause_already_present;
import 'augmentation_modifier_extra_test.dart' as augmentation_modifier_extra;
import 'augmentation_modifier_missing_test.dart'
as augmentation_modifier_missing;
@ -943,6 +945,7 @@ main() {
assignment_to_type.main();
async_for_in_wrong_context.main();
async_keyword_used_as_identifier.main();
augmentation_extends_clause_already_present.main();
augmentation_modifier_extra.main();
augmentation_modifier_missing.main();
augmentation_of_different_declaration_kind.main();