diff --git a/pkgs/swift2objc/lib/src/ast/declarations/compounds/protocol_declaration.dart b/pkgs/swift2objc/lib/src/ast/declarations/compounds/protocol_declaration.dart index 197b5ee6d..28e6c3da5 100644 --- a/pkgs/swift2objc/lib/src/ast/declarations/compounds/protocol_declaration.dart +++ b/pkgs/swift2objc/lib/src/ast/declarations/compounds/protocol_declaration.dart @@ -4,13 +4,14 @@ import '../../_core/interfaces/compound_declaration.dart'; import '../../_core/interfaces/nestable_declaration.dart'; +import '../../_core/interfaces/objc_annotatable.dart'; import '../../_core/shared/referred_type.dart'; import 'members/initializer_declaration.dart'; import 'members/method_declaration.dart'; import 'members/property_declaration.dart'; /// Describes the declaration of a Swift protocol. -class ProtocolDeclaration implements CompoundDeclaration { +class ProtocolDeclaration implements CompoundDeclaration, ObjCAnnotatable { @override String id; @@ -23,9 +24,20 @@ class ProtocolDeclaration implements CompoundDeclaration { @override covariant List methods; + /// Only present if indicated with `@objc` + @override + List optionalProperties; + + /// Only present if indicated with `@objc` + @override + List optionalMethods; + @override List> conformedProtocols; + @override + bool hasObjCAnnotation; + @override List typeParams; @@ -46,7 +58,10 @@ class ProtocolDeclaration implements CompoundDeclaration { required this.initializers, required this.conformedProtocols, required this.typeParams, + this.hasObjCAnnotation = false, this.nestingParent, this.nestedDeclarations = const [], + this.optionalMethods = const [], + this.optionalProperties = const [], }); } diff --git a/pkgs/swift2objc/lib/src/parser/_core/parsed_symbolgraph.dart b/pkgs/swift2objc/lib/src/parser/_core/parsed_symbolgraph.dart index e98010719..88684dc66 100644 --- a/pkgs/swift2objc/lib/src/parser/_core/parsed_symbolgraph.dart +++ b/pkgs/swift2objc/lib/src/parser/_core/parsed_symbolgraph.dart @@ -35,6 +35,10 @@ class ParsedRelation { } enum ParsedRelationKind { + requirementOf, + defaultImplementationOf, + optionalRequirementOf, + conformsTo, memberOf; static final _supportedRelationKindsMap = { diff --git a/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_compound_declaration.dart b/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_compound_declaration.dart index 0940833db..27ecfa3f4 100644 --- a/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_compound_declaration.dart +++ b/pkgs/swift2objc/lib/src/parser/parsers/declaration_parsers/parse_compound_declaration.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import '../../../ast/_core/interfaces/compound_declaration.dart'; +import '../../../ast/_core/interfaces/declaration.dart'; import '../../../ast/_core/interfaces/nestable_declaration.dart'; import '../../../ast/declarations/compounds/class_declaration.dart'; import '../../../ast/declarations/compounds/members/initializer_declaration.dart'; @@ -111,9 +112,125 @@ StructDeclaration parseStructDeclaration( // This won't work as there's more for a protocol declaration // Placing this here as placeholder declaration +// TODO: Implement extensions before adding support for default implementations + // 5. protocol func with reimpl in extension -> requirementOf + // and defaultImplementationOf, swift.method + // 6. protocol var with reimpl in extension -> requirementOf + // and defaultImplementationOf, swift.property, +// TODO: Replace generics to associatedType and implement ProtocolDeclaration parseProtocolDeclaration( ParsedSymbol protocolSymbol, ParsedSymbolgraph symbolgraph ) { - return _parseCompoundDeclaration(protocolSymbol, ProtocolDeclaration.new, symbolgraph); + final compoundId = parseSymbolId(protocolSymbol.json); + final compoundRelations = symbolgraph.relations[compoundId] ?? []; + + // construct protocol + final protocol = ProtocolDeclaration( + id: compoundId, name: parseSymbolName(protocolSymbol.json), + properties: [], + methods: [], + initializers: [], + conformedProtocols: [], + typeParams: [] + ); + + // get optional member declarations if any + final optionalMemberDeclarations = compoundRelations.where((relation) { + final isOptionalRequirementRelation = + relation.kind == ParsedRelationKind.optionalRequirementOf; + final isMemberOfProtocol = relation.targetId == compoundId; + return isMemberOfProtocol && isOptionalRequirementRelation; + }).map((relation) { + final memberSymbol = symbolgraph.symbols[relation.sourceId]; + if (memberSymbol == null) { + return null; + } + return tryParseDeclaration(memberSymbol, symbolgraph); + }) + .nonNulls + .dedupeBy((decl) => decl.id) + .toList() + ; + + // get normal member declarations + final memberDeclarations = compoundRelations.where((relation) { + final isOptionalRequirementRelation = + relation.kind == ParsedRelationKind.requirementOf + && relation.kind != ParsedRelationKind.optionalRequirementOf; + final isMemberOfProtocol = relation.targetId == compoundId; + return isMemberOfProtocol && isOptionalRequirementRelation; + }).map((relation) { + final memberSymbol = symbolgraph.symbols[relation.sourceId]; + if (memberSymbol == null) { + return null; + } + return tryParseDeclaration(memberSymbol, symbolgraph); + }) + .nonNulls + .dedupeBy((decl) => decl.id) + .toList() + ; + + // get conformed protocols + final conformedProtocolDeclarations = compoundRelations.where((relation) { + final isOptionalRequirementRelation = + relation.kind == ParsedRelationKind.conformsTo; + final isMemberOfProtocol = relation.targetId == compoundId; + return isMemberOfProtocol && isOptionalRequirementRelation; + }).map((relation) { + final memberSymbol = symbolgraph.symbols[relation.sourceId]; + if (memberSymbol == null) { + return null; + } + var conformedDecl = tryParseDeclaration(memberSymbol, symbolgraph) as ProtocolDeclaration; + return conformedDecl.asDeclaredType; + }) + .nonNulls + .dedupeBy((decl) => decl.id) + .toList() + ; + + // If the protocol has optional members, it must be annotated with `@objc` + if (optionalMemberDeclarations.isNotEmpty) { + protocol.hasObjCAnnotation = true; + } + + protocol.methods.addAll( + memberDeclarations + .whereType() + .dedupeBy((m) => m.fullName), + ); + + protocol.properties.addAll( + memberDeclarations.whereType(), + ); + + protocol.optionalMethods.addAll( + optionalMemberDeclarations + .whereType() + .dedupeBy((m) => m.fullName), + ); + + protocol.optionalProperties.addAll( + optionalMemberDeclarations.whereType(), + ); + + protocol.conformedProtocols.addAll( + conformedProtocolDeclarations + ); + + protocol.initializers.addAll( + memberDeclarations + .whereType() + .dedupeBy((m) => m.fullName), + ); + + protocol.nestedDeclarations.addAll( + memberDeclarations.whereType(), + ); + + protocol.nestedDeclarations.fillNestingParents(protocol); + + return protocol; } \ No newline at end of file