Skip to content

[swift2objc] Add support for mutating functions #1944

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 3, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,20 @@ class MethodDeclaration extends AstNode
for (final p in params) p.name,
].join(':');

MethodDeclaration({
required this.id,
required this.name,
required this.returnType,
required this.params,
this.typeParams = const [],
this.hasObjCAnnotation = false,
this.statements = const [],
this.isStatic = false,
this.isOverriding = false,
this.throws = false,
this.async = false,
this.mutating = false
}) : assert(!isStatic || !isOverriding);
MethodDeclaration(
{required this.id,
required this.name,
required this.returnType,
required this.params,
this.typeParams = const [],
this.hasObjCAnnotation = false,
this.statements = const [],
this.isStatic = false,
this.isOverriding = false,
this.throws = false,
this.async = false,
this.mutating = false})
: assert(!isStatic || !isOverriding);

@override
void visit(Visitation visitation) => visitation.visitMethodDeclaration(this);
Expand All @@ -77,4 +77,5 @@ class MethodDeclaration extends AstNode
visitor.visitAll(typeParams);
visitor.visit(returnType);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -36,32 +36,32 @@ MethodDeclaration parseMethodDeclaration(
final info =
parseFunctionInfo(methodSymbolJson['declarationFragments'], symbolgraph);
return MethodDeclaration(
id: parseSymbolId(methodSymbolJson),
name: parseSymbolName(methodSymbolJson),
returnType: _parseFunctionReturnType(methodSymbolJson, symbolgraph),
params: info.params,
hasObjCAnnotation: parseSymbolHasObjcAnnotation(methodSymbolJson),
isStatic: isStatic,
throws: info.throws,
async: info.async,
mutating: _parseFunctionIsMutating(methodSymbolJson)
);
id: parseSymbolId(methodSymbolJson),
name: parseSymbolName(methodSymbolJson),
returnType: _parseFunctionReturnType(methodSymbolJson, symbolgraph),
params: info.params,
hasObjCAnnotation: parseSymbolHasObjcAnnotation(methodSymbolJson),
isStatic: isStatic,
throws: info.throws,
async: info.async,
mutating: info.mutating);
}

typedef ParsedFunctionInfo = ({
List<Parameter> params,
bool throws,
bool async,
bool mutating,
});

ParsedFunctionInfo parseFunctionInfo(
Json declarationFragments,
ParsedSymbolgraph symbolgraph,
) {
// `declarationFragments` describes each part of the function declaration,
// things like the `func` keyword, brackets, spaces, etc. We only care about
// the parameter fragments and annotations here, and they always appear in
// this order:
// things like the `func` keyword, brackets, spaces, etc.
// For the most part, We only care about the parameter fragments and
// annotations here, and they always appear in this order:
// [
// ..., '(',
// externalParam, ' ', internalParam, ': ', type..., ', '
Expand All @@ -81,6 +81,7 @@ ParsedFunctionInfo parseFunctionInfo(
);

var tokens = TokenList(declarationFragments);

String? maybeConsume(String kind) {
if (tokens.isEmpty) return null;
final spelling = getSpellingForKind(tokens[0], kind);
Expand Down Expand Up @@ -140,16 +141,14 @@ ParsedFunctionInfo parseFunctionInfo(
params: parameters,
throws: annotations.contains('throws'),
async: annotations.contains('async'),
mutating: declarationFragments
.where((j) =>
j['kind'].get<String?>() == 'keyword' &&
j['spelling'].get<String?>() == 'mutating')
.isNotEmpty
);
}

bool _parseFunctionIsMutating(Json methodSymbolJson) {
return methodSymbolJson['declarationFragments']
.where((j) => j['kind'].get<String?>() == 'keyword'
&& j['spelling'].get<String?>() == 'mutating')
.isNotEmpty;
}

ReferredType _parseFunctionReturnType(
Json methodSymbolJson,
ParsedSymbolgraph symbolgraph,
Expand Down
90 changes: 90 additions & 0 deletions pkgs/swift2objc/test/unit/parse_function_info_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,96 @@ void main() {
expect(info.throws, isTrue);
expect(info.async, isTrue);
});

test('Mutating Function that throws', () {
final json = Json(jsonDecode('''
[
{
"kind": "keyword",
"spelling": "mutating"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "keyword",
"spelling": "func"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "identifier",
"spelling": "moveBy"
},
{
"kind": "text",
"spelling": "("
},
{
"kind": "externalParam",
"spelling": "x"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "internalParam",
"spelling": "deltaX"
},
{
"kind": "text",
"spelling": ": "
},
{
"kind": "typeIdentifier",
"spelling": "Double",
"preciseIdentifier": "s:Sd"
},
{
"kind": "text",
"spelling": ", "
},
{
"kind": "externalParam",
"spelling": "y"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "internalParam",
"spelling": "deltaY"
},
{
"kind": "text",
"spelling": ": "
},
{
"kind": "typeIdentifier",
"spelling": "Double",
"preciseIdentifier": "s:Sd"
},
{
"kind": "text",
"spelling": ") "
},
{
"kind": "keyword",
"spelling": "throws"
}
]
'''));

final info = parseFunctionInfo(json, emptySymbolgraph);

expect(info.throws, isTrue);
expect(info.mutating, isTrue);
});
});

group('Invalid json', () {
Expand Down
Loading