[analyzer] Add sealed modifier to ClassOrMixinElementImpl and source visitor.

Add sealed modifier to element builder summaries and to_source_visitor.
Enable sealed_class experiement in tests to test the changes.

Change-Id: I2929a5a38360c73abfcdb26d1e9b786058ea9335
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/272351
Commit-Queue: Kallen Tu <kallentu@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
Kallen Tu 2022-11-30 22:58:58 +00:00 committed by Commit Queue
parent c870932e45
commit 1d9257ad02
9 changed files with 121 additions and 8 deletions

View file

@ -85,7 +85,7 @@ import 'package:analyzer/src/util/performance/operation_performance.dart';
/// TODO(scheglov) Clean up the list of implicitly analyzed files.
class AnalysisDriver implements AnalysisDriverGeneric {
/// The version of data format, should be incremented on every format change.
static const int DATA_VERSION = 250;
static const int DATA_VERSION = 251;
/// 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

@ -188,6 +188,7 @@ class ToSourceVisitor implements AstVisitor<void> {
_visitToken(node.augmentKeyword, suffix: ' ');
_visitToken(node.abstractKeyword, suffix: ' ');
_visitToken(node.macroKeyword, suffix: ' ');
_visitToken(node.sealedKeyword, suffix: ' ');
sink.write('class ');
_visitToken(node.name);
_visitNode(node.typeParameters);
@ -205,6 +206,7 @@ class ToSourceVisitor implements AstVisitor<void> {
_visitToken(node.augmentKeyword, suffix: ' ');
_visitToken(node.abstractKeyword, suffix: ' ');
_visitToken(node.macroKeyword, suffix: ' ');
_visitToken(node.sealedKeyword, suffix: ' ');
sink.write('class ');
_visitToken(node.name);
_visitNode(node.typeParameters);

View file

@ -998,6 +998,14 @@ abstract class ClassOrMixinElementImpl extends AbstractClassElementImpl {
return _interfaces;
}
bool get isSealed {
return hasModifier(Modifier.SEALED);
}
set isSealed(bool isSealed) {
setModifier(Modifier.SEALED, isSealed);
}
@override
bool get isSimplyBounded {
return hasModifier(Modifier.SIMPLY_BOUNDED);
@ -4839,24 +4847,27 @@ class Modifier implements Comparable<Modifier> {
static const Modifier PROMOTABLE = Modifier('IS_PROMOTABLE', 20);
/// Indicates that the modifier 'sealed' was applied to the element.
static const Modifier SEALED = Modifier('SEALED', 21);
/// Indicates that the pseudo-modifier 'set' was applied to the element.
static const Modifier SETTER = Modifier('SETTER', 21);
static const Modifier SETTER = Modifier('SETTER', 22);
/// See [TypeParameterizedElement.isSimplyBounded].
static const Modifier SIMPLY_BOUNDED = Modifier('SIMPLY_BOUNDED', 22);
static const Modifier SIMPLY_BOUNDED = Modifier('SIMPLY_BOUNDED', 23);
/// Indicates that the modifier 'static' was applied to the element.
static const Modifier STATIC = Modifier('STATIC', 23);
static const Modifier STATIC = Modifier('STATIC', 24);
/// Indicates that the element does not appear in the source code but was
/// implicitly created. For example, if a class does not define any
/// constructors, an implicit zero-argument constructor will be created and it
/// will be marked as being synthetic.
static const Modifier SYNTHETIC = Modifier('SYNTHETIC', 24);
static const Modifier SYNTHETIC = Modifier('SYNTHETIC', 25);
/// Indicates that the element was appended to this enclosing element to
/// simulate temporary the effect of applying augmentation.
static const Modifier TEMP_AUGMENTATION = Modifier('TEMP_AUGMENTATION', 25);
static const Modifier TEMP_AUGMENTATION = Modifier('TEMP_AUGMENTATION', 26);
static const List<Modifier> values = [
ABSTRACT,
@ -4879,6 +4890,7 @@ class Modifier implements Comparable<Modifier> {
MACRO,
MIXIN_APPLICATION,
PROMOTABLE,
SEALED,
SETTER,
STATIC,
SIMPLY_BOUNDED,

View file

@ -91,6 +91,7 @@ class ElementBuilder extends ThrowingAstVisitor<void> {
var element = ClassElementImpl(name, nameToken.offset);
element.isAbstract = node.abstractKeyword != null;
element.isMacro = node.macroKeyword != null;
element.isSealed = node.sealedKeyword != null;
element.metadata = _buildAnnotations(node.metadata);
_setCodeRange(element, node);
_setDocumentation(element, node);
@ -124,6 +125,7 @@ class ElementBuilder extends ThrowingAstVisitor<void> {
var element = ClassElementImpl(name, nameToken.offset);
element.isAbstract = node.abstractKeyword != null;
element.isMacro = node.macroKeyword != null;
element.isSealed = node.sealedKeyword != null;
element.isMixinApplication = true;
element.metadata = _buildAnnotations(node.metadata);
_setCodeRange(element, node);
@ -848,6 +850,7 @@ class ElementBuilder extends ThrowingAstVisitor<void> {
var name = nameToken.lexeme;
var element = MixinElementImpl(name, nameToken.offset);
element.isSealed = node.sealedKeyword != null;
element.metadata = _buildAnnotations(node.metadata);
_setCodeRange(element, node);
_setDocumentation(element, node);

View file

@ -10,13 +10,15 @@ class ClassElementFlags {
static const int _isAbstract = 1 << 0;
static const int _isMacro = 1 << 1;
static const int _isMixinApplication = 1 << 2;
static const int _isSimplyBounded = 1 << 3;
static const int _isSealed = 1 << 3;
static const int _isSimplyBounded = 1 << 4;
static void read(SummaryDataReader reader, ClassElementImpl element) {
var byte = reader.readByte();
element.isAbstract = (byte & _isAbstract) != 0;
element.isMacro = (byte & _isMacro) != 0;
element.isMixinApplication = (byte & _isMixinApplication) != 0;
element.isSealed = (byte & _isSealed) != 0;
element.isSimplyBounded = (byte & _isSimplyBounded) != 0;
}
@ -25,6 +27,7 @@ class ClassElementFlags {
result |= element.isAbstract ? _isAbstract : 0;
result |= element.isMacro ? _isMacro : 0;
result |= element.isMixinApplication ? _isMixinApplication : 0;
result |= element.isSealed ? _isSealed : 0;
result |= element.isSimplyBounded ? _isSimplyBounded : 0;
sink.writeByte(result);
}
@ -223,15 +226,18 @@ class MethodElementFlags {
}
class MixinElementFlags {
static const int _isSimplyBounded = 1 << 0;
static const int _isSealed = 1 << 0;
static const int _isSimplyBounded = 1 << 1;
static void read(SummaryDataReader reader, MixinElementImpl element) {
var byte = reader.readByte();
element.isSealed = (byte & _isSealed) != 0;
element.isSimplyBounded = (byte & _isSimplyBounded) != 0;
}
static void write(BufferedSink sink, MixinElementImpl element) {
var result = 0;
result |= element.isSealed ? _isSealed : 0;
result |= element.isSimplyBounded ? _isSimplyBounded : 0;
sink.writeByte(result);
}

View file

@ -482,6 +482,16 @@ $code
_assertSource(code, findNode.classDeclaration(code));
}
void test_visitClassDeclaration_sealed() {
var findNode = _parseStringToFindNode(r'''
sealed class A {}
''');
_assertSource(
'sealed class A {}',
findNode.classDeclaration('class A'),
);
}
void test_visitClassDeclaration_singleMember() {
final code = 'class C {var a;}';
final findNode = _parseStringToFindNode(code);
@ -595,6 +605,16 @@ $code
_assertSource(code, findNode.classTypeAlias(code));
}
void test_visitClassTypeAlias_sealed() {
var findNode = _parseStringToFindNode(r'''
sealed class A = S with M;
''');
_assertSource(
'sealed class A = S with M;',
findNode.classTypeAlias('class A'),
);
}
void test_visitClassTypeAlias_withMetadata() {
final code = '@deprecated class A = S with M;';
final findNode = _parseStringToFindNode(code);

View file

@ -325,6 +325,7 @@ class PubPackageResolutionTest extends ContextResolutionTest {
EnableString.macros,
EnableString.patterns,
EnableString.records,
EnableString.sealed_class,
];
@override

View file

@ -290,12 +290,14 @@ class _ElementWriter {
if (e is ClassElement) {
_writeIf(e.isAbstract, 'abstract ');
_writeIf(e.isMacro, 'macro ');
_writeIf(e.isSealed, 'sealed ');
}
_writeIf(!e.isSimplyBounded, 'notSimplyBounded ');
if (e is EnumElement) {
buffer.write('enum ');
} else if (e is MixinElement) {
_writeIf(e.isSealed, 'sealed ');
buffer.write('mixin ');
} else {
buffer.write('class ');
@ -1085,4 +1087,16 @@ extension on ClassElement {
final self = this;
return self is ClassElementImpl && self.isMacro;
}
bool get isSealed {
final self = this;
return self is ClassElementImpl && self.isSealed;
}
}
extension on MixinElement {
bool get isSealed {
final self = this;
return self is MixinElementImpl && self.isSealed;
}
}

View file

@ -7570,6 +7570,18 @@ library
''');
}
test_class_sealed() async {
var library = await buildLibrary('sealed class C {}');
checkElementText(library, r'''
library
definingUnit
classes
sealed class C @13
constructors
synthetic @-1
''');
}
test_class_setter_abstract() async {
var library =
await buildLibrary('abstract class C { void set x(int value); }');
@ -9031,6 +9043,35 @@ library
''');
}
test_classAlias_sealed() async {
var library = await buildLibrary('''
sealed class C = Object with M;
mixin M {}
''');
checkElementText(library, r'''
library
definingUnit
classes
sealed class alias C @13
supertype: Object
mixins
M
constructors
synthetic const @-1
constantInitializers
SuperConstructorInvocation
superKeyword: super @0
argumentList: ArgumentList
leftParenthesis: ( @0
rightParenthesis: ) @0
staticElement: dart:core::@class::Object::@constructor::new
mixins
mixin M @38
superclassConstraints
Object
''');
}
test_classAlias_with_const_constructors() async {
addSource('$testPackageLibPath/a.dart', r'''
class Base {
@ -34207,6 +34248,20 @@ library
''');
}
test_mixin_sealed() async {
var library = await buildLibrary(r'''
sealed mixin M on A {}
''');
checkElementText(library, r'''
library
definingUnit
mixins
sealed mixin M @13
superclassConstraints
Object
''');
}
test_mixin_setter_invokesSuperSelf_getter() async {
var library = await buildLibrary(r'''
mixin M on A {