blob: 9e1ba7245969f04accfed3dbc284bbd6caa87545 [file] [log] [blame] [edit]
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:convert';
import 'package:swift2objc/src/ast/_core/shared/parameter.dart';
import 'package:swift2objc/src/ast/_core/shared/referred_type.dart';
import 'package:swift2objc/src/ast/declarations/built_in/built_in_declaration.dart';
import 'package:swift2objc/src/parser/_core/json.dart';
import 'package:swift2objc/src/parser/_core/parsed_symbolgraph.dart';
import 'package:swift2objc/src/parser/parsers/declaration_parsers/parse_function_declaration.dart';
import 'package:test/test.dart';
void main() {
final parsedSymbols = {
for (final decl in builtInDeclarations)
decl.id: ParsedSymbol(json: Json(null), declaration: decl)
};
final emptySymbolgraph = ParsedSymbolgraph(parsedSymbols, {});
group('Function Valid json', () {
void expectEqualParams(
List<Parameter> actualParams,
List<Parameter> expectedParams,
) {
expect(actualParams.length, expectedParams.length);
for (var i = 0; i < actualParams.length; i++) {
final actualParam = actualParams[i];
final expectedParam = expectedParams[i];
expect(actualParam.name, expectedParam.name);
expect(actualParam.internalName, expectedParam.internalName);
expect(actualParam.type.sameAs(expectedParam.type), isTrue);
}
}
test('Two params with one internal name', () {
final json = Json(jsonDecode(
'''
[
{ "kind": "keyword", "spelling": "init" },
{ "kind": "text", "spelling": "(" },
{ "kind": "externalParam", "spelling": "outerLabel" },
{ "kind": "text", "spelling": " " },
{ "kind": "internalParam", "spelling": "internalLabel" },
{ "kind": "text", "spelling": ": " },
{
"kind": "typeIdentifier",
"spelling": "Int",
"preciseIdentifier": "s:Si"
},
{ "kind": "text", "spelling": ", " },
{ "kind": "externalParam", "spelling": "singleLabel" },
{ "kind": "text", "spelling": ": " },
{
"kind": "typeIdentifier",
"spelling": "Int",
"preciseIdentifier": "s:Si"
},
{ "kind": "text", "spelling": ")" }
]
''',
));
final info = parseFunctionInfo(json, emptySymbolgraph);
final expectedParams = [
Parameter(
name: 'outerLabel',
internalName: 'internalLabel',
type: intType,
),
Parameter(
name: 'singleLabel',
type: intType,
),
];
expectEqualParams(info.params, expectedParams);
expect(info.throws, isFalse);
expect(info.async, isFalse);
});
test('Three params with some optional', () {
final json = Json(jsonDecode(
'''
[
{ "kind": "keyword", "spelling": "init" },
{ "kind": "text", "spelling": "(" },
{ "kind": "externalParam", "spelling": "label1" },
{ "kind": "text", "spelling": " " },
{ "kind": "internalParam", "spelling": "param1" },
{ "kind": "text", "spelling": ": " },
{
"kind": "typeIdentifier",
"spelling": "Int",
"preciseIdentifier": "s:Si"
},
{ "kind": "text", "spelling": "?, " },
{ "kind": "externalParam", "spelling": "label2" },
{ "kind": "text", "spelling": ": " },
{
"kind": "typeIdentifier",
"spelling": "Int",
"preciseIdentifier": "s:Si"
},
{ "kind": "text", "spelling": ", " },
{ "kind": "externalParam", "spelling": "label3" },
{ "kind": "text", "spelling": " " },
{ "kind": "internalParam", "spelling": "param3" },
{ "kind": "text", "spelling": ": " },
{
"kind": "typeIdentifier",
"spelling": "Int",
"preciseIdentifier": "s:Si"
},
{ "kind": "text", "spelling": "?)" }
]
''',
));
final info = parseFunctionInfo(json, emptySymbolgraph);
final expectedParams = [
Parameter(
name: 'label1',
internalName: 'param1',
type: OptionalType(intType),
),
Parameter(
name: 'label2',
type: intType,
),
Parameter(
name: 'label3',
internalName: 'param3',
type: OptionalType(intType),
),
];
expectEqualParams(info.params, expectedParams);
expect(info.throws, isFalse);
expect(info.async, isFalse);
});
test('One param', () {
final json = Json(jsonDecode(
'''
[
{ "kind": "keyword", "spelling": "init" },
{ "kind": "text", "spelling": "(" },
{ "kind": "externalParam", "spelling": "parameter" },
{ "kind": "text", "spelling": ": " },
{
"kind": "typeIdentifier",
"spelling": "Int",
"preciseIdentifier": "s:Si"
},
{ "kind": "text", "spelling": ")" }
]
''',
));
final info = parseFunctionInfo(json, emptySymbolgraph);
final expectedParams = [
Parameter(
name: 'parameter',
type: intType,
),
];
expectEqualParams(info.params, expectedParams);
expect(info.throws, isFalse);
expect(info.async, isFalse);
});
test('No params', () {
final json = Json(jsonDecode(
'''
[
{ "kind": "keyword", "spelling": "init" },
{ "kind": "text", "spelling": "()" }
]
''',
));
final info = parseFunctionInfo(json, emptySymbolgraph);
expectEqualParams(info.params, []);
expect(info.throws, isFalse);
expect(info.async, isFalse);
});
test('Function with return type', () {
// parseFunctionInfo doesn't parse the return type, but it should be able
// to cope with one.
final json = Json(jsonDecode(
'''
[
{ "kind": "keyword", "spelling": "func" },
{ "kind": "text", "spelling": " " },
{ "kind": "identifier", "spelling": "foo" },
{ "kind": "text", "spelling": "(" },
{ "kind": "externalParam", "spelling": "parameter" },
{ "kind": "text", "spelling": ": " },
{
"kind": "typeIdentifier",
"spelling": "Int",
"preciseIdentifier": "s:Si"
},
{ "kind": "text", "spelling": ") -> " },
{
"kind": "typeIdentifier",
"spelling": "Int",
"preciseIdentifier": "s:Si"
}
]
''',
));
final info = parseFunctionInfo(json, emptySymbolgraph);
final expectedParams = [
Parameter(
name: 'parameter',
type: intType,
),
];
expectEqualParams(info.params, expectedParams);
expect(info.throws, isFalse);
expect(info.async, isFalse);
});
test('Function with no params with return type', () {
final json = Json(jsonDecode(
'''
[
{ "kind": "keyword", "spelling": "func" },
{ "kind": "text", "spelling": " " },
{ "kind": "identifier", "spelling": "foo" },
{ "kind": "text", "spelling": "() -> " },
{
"kind": "typeIdentifier",
"spelling": "Int",
"preciseIdentifier": "s:Si"
}
]
''',
));
final info = parseFunctionInfo(json, emptySymbolgraph);
expectEqualParams(info.params, []);
expect(info.throws, isFalse);
expect(info.async, isFalse);
});
test('Function with no params and no return type', () {
final json = Json(jsonDecode(
'''
[
{ "kind": "keyword", "spelling": "func" },
{ "kind": "text", "spelling": " " },
{ "kind": "identifier", "spelling": "foo" },
{ "kind": "text", "spelling": "()" }
]
''',
));
final info = parseFunctionInfo(json, emptySymbolgraph);
expectEqualParams(info.params, []);
expect(info.throws, isFalse);
expect(info.async, isFalse);
});
test('Function with return type that throws', () {
final json = Json(jsonDecode(
'''
[
{ "kind": "keyword", "spelling": "func" },
{ "kind": "text", "spelling": " " },
{ "kind": "identifier", "spelling": "foo" },
{ "kind": "text", "spelling": "(" },
{ "kind": "externalParam", "spelling": "parameter" },
{ "kind": "text", "spelling": ": " },
{
"kind": "typeIdentifier",
"spelling": "Int",
"preciseIdentifier": "s:Si"
},
{ "kind": "text", "spelling": ") " },
{ "kind": "keyword", "spelling": "throws" },
{ "kind": "text", "spelling": " -> " },
{
"kind": "typeIdentifier",
"spelling": "Int",
"preciseIdentifier": "s:Si"
}
]
''',
));
final info = parseFunctionInfo(json, emptySymbolgraph);
final expectedParams = [
Parameter(
name: 'parameter',
type: intType,
),
];
expectEqualParams(info.params, expectedParams);
expect(info.throws, isTrue);
expect(info.async, isFalse);
});
test('Function with no return type that throws', () {
final json = Json(jsonDecode(
'''
[
{ "kind": "keyword", "spelling": "func" },
{ "kind": "text", "spelling": " " },
{ "kind": "identifier", "spelling": "foo" },
{ "kind": "text", "spelling": "(" },
{ "kind": "externalParam", "spelling": "parameter" },
{ "kind": "text", "spelling": ": " },
{
"kind": "typeIdentifier",
"spelling": "Int",
"preciseIdentifier": "s:Si"
},
{ "kind": "text", "spelling": ") " },
{ "kind": "keyword", "spelling": "throws" }
]
''',
));
final info = parseFunctionInfo(json, emptySymbolgraph);
final expectedParams = [
Parameter(
name: 'parameter',
type: intType,
),
];
expectEqualParams(info.params, expectedParams);
expect(info.throws, isTrue);
expect(info.async, isFalse);
});
test('Function with no params that throws', () {
final json = Json(jsonDecode(
'''
[
{ "kind": "keyword", "spelling": "func" },
{ "kind": "text", "spelling": " " },
{ "kind": "identifier", "spelling": "foo" },
{ "kind": "text", "spelling": "() " },
{ "kind": "keyword", "spelling": "throws" }
]
''',
));
final info = parseFunctionInfo(json, emptySymbolgraph);
expectEqualParams(info.params, []);
expect(info.throws, isTrue);
expect(info.async, isFalse);
});
test('Function with async annotation', () {
final json = Json(jsonDecode(
'''
[
{ "kind": "keyword", "spelling": "func" },
{ "kind": "text", "spelling": " " },
{ "kind": "identifier", "spelling": "foo" },
{ "kind": "text", "spelling": "(" },
{ "kind": "externalParam", "spelling": "parameter" },
{ "kind": "text", "spelling": ": " },
{
"kind": "typeIdentifier",
"spelling": "Int",
"preciseIdentifier": "s:Si"
},
{ "kind": "text", "spelling": ") " },
{ "kind": "keyword", "spelling": "async" },
{ "kind": "text", "spelling": " -> " },
{
"kind": "typeIdentifier",
"spelling": "Int",
"preciseIdentifier": "s:Si"
}
]
''',
));
final info = parseFunctionInfo(json, emptySymbolgraph);
final expectedParams = [
Parameter(
name: 'parameter',
type: intType,
),
];
expectEqualParams(info.params, expectedParams);
expect(info.throws, isFalse);
expect(info.async, isTrue);
});
test('Function with async and throws annotations', () {
final json = Json(jsonDecode(
'''
[
{ "kind": "keyword", "spelling": "func" },
{ "kind": "text", "spelling": " " },
{ "kind": "identifier", "spelling": "foo" },
{ "kind": "text", "spelling": "(" },
{ "kind": "externalParam", "spelling": "parameter" },
{ "kind": "text", "spelling": ": " },
{
"kind": "typeIdentifier",
"spelling": "Int",
"preciseIdentifier": "s:Si"
},
{ "kind": "text", "spelling": ") " },
{ "kind": "keyword", "spelling": "async" },
{ "kind": "text", "spelling": " " },
{ "kind": "keyword", "spelling": "throws" },
{ "kind": "text", "spelling": " -> " },
{
"kind": "typeIdentifier",
"spelling": "Int",
"preciseIdentifier": "s:Si"
}
]
''',
));
final info = parseFunctionInfo(json, emptySymbolgraph);
final expectedParams = [
Parameter(
name: 'parameter',
type: intType,
),
];
expectEqualParams(info.params, expectedParams);
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', () {
test('Parameter with no outer label', () {
final json = Json(jsonDecode(
'''
[
{ "kind": "keyword", "spelling": "init" },
{ "kind": "text", "spelling": "(" },
{ "kind": "internalParam", "spelling": "internalLabel" },
{ "kind": "text", "spelling": ": " },
{
"kind": "typeIdentifier",
"spelling": "Int",
"preciseIdentifier": "s:Si"
},
{ "kind": "text", "spelling": ")" }
]
''',
));
expect(
() => parseFunctionInfo(json, emptySymbolgraph),
throwsA(isA<Exception>()),
);
});
test('Parameter with no type', () {
final json = Json(jsonDecode(
'''
[
{ "kind": "keyword", "spelling": "init" },
{ "kind": "text", "spelling": "(" },
{ "kind": "externalParam", "spelling": "outerLabel" },
{ "kind": "text", "spelling": " " },
{ "kind": "internalParam", "spelling": "internalLabel" },
{ "kind": "text", "spelling": ")" }
]
''',
));
expect(
() => parseFunctionInfo(json, emptySymbolgraph),
throwsA(isA<Exception>()),
);
});
test('Parameter with just a type (no label)', () {
final json = Json(jsonDecode(
'''
[
{ "kind": "keyword", "spelling": "init" },
{ "kind": "text", "spelling": "(" },
{ "kind": "text", "spelling": ": " },
{
"kind": "typeIdentifier",
"spelling": "Int",
"preciseIdentifier": "s:Si"
},
{ "kind": "text", "spelling": ")" }
]
''',
));
expect(
() => parseFunctionInfo(json, emptySymbolgraph),
throwsA(isA<Exception>()),
);
});
});
}