Skip to content
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

[swift2objc] Add support for mutating functions #1944

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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 @@ -45,24 +45,27 @@ class MethodDeclaration extends AstNode

bool isStatic;

bool mutating;

String get fullName => [
name,
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,
}) : 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 @@ -74,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,31 +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,
);
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 @@ -80,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 @@ -139,6 +141,11 @@ ParsedFunctionInfo parseFunctionInfo(
params: parameters,
throws: annotations.contains('throws'),
async: annotations.contains('async'),
mutating: declarationFragments
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that the check is in here, you can make it more specific. If a mutating keyword appears anywhere in the function declaration, this will mark the entire function as mutating. It might be the case that there it's not syntactically valid for mutating to appear anywhere else, but I don't know that's true, and it might not be true in future.

See the chunk of code that checks for annotations? You can copy it to the start of this function and use it to check for annotations that come before the (, replacing the final openParen = tokens.indexWhere( stuff, and store them in a prefixAnnotations set or something. Better yet, factor out that chunk of code into a separate function that prefixAnnotations and annotations both use.

Then this line becomes mutating: prefixAnnotations.contains('mutating'),.

.where((j) =>
j['kind'].get<String?>() == 'keyword' &&
j['spelling'].get<String?>() == 'mutating')
.isNotEmpty
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ public struct MyStruct {
public func myMethod3() {
1 + 1
}

public mutating func myMethod4() {
2 + 2
}
}

public struct MyOtherStruct {}
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,9 @@ import Foundation
return wrappedInstance.myMethod3()
}

@objc public func myMethod4() {
return wrappedInstance.myMethod4()
}

}

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