diff --git a/pkg/analyzer/lib/src/dart/sdk/patch.dart b/pkg/analyzer/lib/src/dart/sdk/patch.dart index 02b497cf392..44d48cb1cdf 100644 --- a/pkg/analyzer/lib/src/dart/sdk/patch.dart +++ b/pkg/analyzer/lib/src/dart/sdk/patch.dart @@ -100,9 +100,26 @@ class SdkPatcher { void _patchClassMembers( ClassDeclaration baseClass, ClassDeclaration patchClass) { + String className = baseClass.name.name; List membersToAppend = []; for (ClassMember patchMember in patchClass.members) { - if (patchMember is MethodDeclaration) { + if (patchMember is FieldDeclaration) { + if (_hasPatchAnnotation(patchMember.metadata)) { + _failInPatch('attempts to patch a field', patchMember.offset); + } + List fields = patchMember.fields.variables; + if (fields.length != 1) { + _failInPatch('contains a field declaration with more than one field', + patchMember.offset); + } + String name = fields[0].name.name; + if (!Identifier.isPrivateName(className) && + !Identifier.isPrivateName(name)) { + // TODO(scheglov) allow adding public fields into dart:_internal + _failInPatch('contains a public field', patchMember.offset); + } + membersToAppend.add(patchMember); + } else if (patchMember is MethodDeclaration) { String name = patchMember.name.name; if (_hasPatchAnnotation(patchMember.metadata)) { for (ClassMember baseMember in baseClass.members) { @@ -181,7 +198,7 @@ class SdkPatcher { } } else { if (name == null) { - if (!Identifier.isPrivateName(baseClass.name.name)) { + if (!Identifier.isPrivateName(className)) { _failInPatch( 'contains an unnamed public constructor', patchMember.offset); } @@ -191,7 +208,6 @@ class SdkPatcher { membersToAppend.add(patchMember); } } else { - // TODO(scheglov) support field String className = patchClass.name.name; _failInPatch('contains an unsupported class member in $className', patchMember.offset); diff --git a/pkg/analyzer/test/src/dart/sdk/patch_test.dart b/pkg/analyzer/test/src/dart/sdk/patch_test.dart index 840d4c5280c..5b4422b52e6 100644 --- a/pkg/analyzer/test/src/dart/sdk/patch_test.dart +++ b/pkg/analyzer/test/src/dart/sdk/patch_test.dart @@ -226,6 +226,96 @@ class C { _assertPrevNextToken(constructor.endToken, clazz.rightBracket); } + test_class_field_append() { + CompilationUnit unit = _doTopLevelPatching( + r''' +class C { + void a() {} +} +''', + r''' +@patch +class C { + int _b = 42; +} +'''); + _assertUnitCode(unit, 'class C {void a() {} int _b = 42;}'); + ClassDeclaration clazz = unit.declarations[0]; + MethodDeclaration a = clazz.members[0]; + FieldDeclaration b = clazz.members[1]; + _assertPrevNextToken(a.endToken, b.beginToken); + _assertPrevNextToken(b.endToken, clazz.rightBracket); + } + + test_class_field_append_fail_moreThanOne() { + expect(() { + _doTopLevelPatching( + r''' +class A {} +''', + r''' +@patch +class A { + @patch + int _f1, _f2; +} +'''); + }, throwsArgumentError); + } + + test_class_field_append_fail_notPrivate() { + expect(() { + _doTopLevelPatching( + r''' +class A {} +''', + r''' +@patch +class A { + @patch + int b; +} +'''); + }, throwsArgumentError); + } + + test_class_field_append_publiInPrivateClass() { + CompilationUnit unit = _doTopLevelPatching( + r''' +class _C { + void a() {} +} +''', + r''' +@patch +class _C { + int b = 42; +} +'''); + _assertUnitCode(unit, 'class _C {void a() {} int b = 42;}'); + ClassDeclaration clazz = unit.declarations[0]; + MethodDeclaration a = clazz.members[0]; + FieldDeclaration b = clazz.members[1]; + _assertPrevNextToken(a.endToken, b.beginToken); + _assertPrevNextToken(b.endToken, clazz.rightBracket); + } + + test_class_field_patch_fail() { + expect(() { + _doTopLevelPatching( + r''' +class A {} +''', + r''' +@patch +class A { + @patch + int _f; +} +'''); + }, throwsArgumentError); + } + test_class_getter_append() { CompilationUnit unit = _doTopLevelPatching( r'''