Skip to content
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
2 changes: 2 additions & 0 deletions pkgs/ffigen/lib/src/code_generator/objc_category.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ class ObjCCategory extends NoLookUpBinding with ObjCMethods, HasLocalScope {
final s = StringBuffer();
s.write('\n');
s.write(makeDartDoc(dartDoc));
final deprecatedAnnotation = makeDeprecatedAnnotation(deprecatedMessage);
s.write(deprecatedAnnotation.isEmpty ? '' : '$deprecatedAnnotation\n');
s.write('''
extension $name on ${parent.getDartType(context)} {
${generateMethodBindings(w, parent)}
Expand Down
7 changes: 7 additions & 0 deletions pkgs/ffigen/lib/src/code_generator/objc_interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ class ObjCInterface extends BindingType with ObjCMethods, HasLocalScope {
name ??
originalName,
) {
deprecatedMessage = apiAvailability.alwaysDeprecated
? (apiAvailability.deprecatedMessage?.isNotEmpty ?? false
? apiAvailability.deprecatedMessage
: '')
: null;
classObject = ObjCInternalGlobal(
'_class_$originalName',
() => '${ObjCBuiltInFunctions.getClass.gen(context)}("$lookupName")',
Expand Down Expand Up @@ -89,6 +94,8 @@ class ObjCInterface extends BindingType with ObjCMethods, HasLocalScope {
''');
}
s.write(makeDartDoc(dartDoc));
final deprecatedAnnotation = makeDeprecatedAnnotation(deprecatedMessage);
s.write(deprecatedAnnotation.isEmpty ? '' : '$deprecatedAnnotation\n');

final ctorBody = [
apiAvailability.runtimeCheck(
Expand Down
8 changes: 8 additions & 0 deletions pkgs/ffigen/lib/src/code_generator/objc_methods.dart
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ enum ObjCMethodFamily {
class ObjCMethod extends AstNode with HasLocalScope {
final Context context;
final String? dartDoc;
final String? deprecatedMessage;
final String originalName;
Symbol symbol;
final String originalProtocolMethodName;
Expand Down Expand Up @@ -221,6 +222,7 @@ class ObjCMethod extends AstNode with HasLocalScope {
required this.symbol,
required String protocolMethodName,
this.dartDoc,
this.deprecatedMessage,
required this.kind,
required this.isClassMethod,
required this.isOptional,
Expand All @@ -239,6 +241,7 @@ class ObjCMethod extends AstNode with HasLocalScope {
required String originalName,
required String name,
String? dartDoc,
String? deprecatedMessage,
required ObjCMethodKind kind,
required bool isClassMethod,
required bool isOptional,
Expand Down Expand Up @@ -289,6 +292,7 @@ class ObjCMethod extends AstNode with HasLocalScope {
symbol: Symbol(name, SymbolKind.method),
protocolMethodName: protocolMethodName,
dartDoc: dartDoc,
deprecatedMessage: deprecatedMessage,
kind: kind,
isClassMethod: isClassMethod,
isOptional: isOptional,
Expand Down Expand Up @@ -450,7 +454,11 @@ class ObjCMethod extends AstNode with HasLocalScope {
final paramStr = _joinParamStr(context, params);

// The method declaration.
final deprecatedAnnotation = makeDeprecatedAnnotation(deprecatedMessage);
s.write('\n ${makeDartDoc(dartDoc)} ');
if (deprecatedAnnotation.isNotEmpty) {
s.write('$deprecatedAnnotation\n ');
}
late String targetStr;
if (isClassMethod) {
targetStr = (target as ObjCInterface).classObject.name;
Expand Down
9 changes: 8 additions & 1 deletion pkgs/ffigen/lib/src/code_generator/objc_protocol.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ class ObjCProtocol extends BindingType with ObjCMethods, HasLocalScope {
name ??
originalName,
) {
deprecatedMessage = apiAvailability.alwaysDeprecated
? (apiAvailability.deprecatedMessage?.isNotEmpty ?? false
? apiAvailability.deprecatedMessage
: '')
: null;
_conformsTo = context.objCBuiltInFunctions.getSelObject(
'conformsToProtocol:',
);
Expand Down Expand Up @@ -90,6 +95,8 @@ class ObjCProtocol extends BindingType with ObjCMethods, HasLocalScope {
''');
}
s.write(makeDartDoc(dartDoc ?? originalName));
final deprecatedAnnotation = makeDeprecatedAnnotation(deprecatedMessage);
s.write(deprecatedAnnotation.isEmpty ? '' : '$deprecatedAnnotation\n');

final sp = [
protocolBase,
Expand Down Expand Up @@ -140,7 +147,7 @@ ${generateInstanceMethodBindings(w, this)}
if (!generateAsStub) {
final builder = '$name\$Builder';
s.write('''
interface class $builder {
${deprecatedAnnotation.isEmpty ? '' : '$deprecatedAnnotation\n'}interface class $builder {
''');

final buildArgs = <String>[];
Expand Down
19 changes: 13 additions & 6 deletions pkgs/ffigen/lib/src/context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
// BSD-style license that can be found in the LICENSE file.

import 'dart:ffi';
import 'dart:io';

import 'package:meta/meta.dart';

import 'package:logging/logging.dart';

Expand Down Expand Up @@ -40,12 +43,14 @@ class Context {
// ignore: deprecated_member_use_from_same_package
generator.objectiveC?.generateForPackageObjectiveC ?? false,
);
final libclangDylibPath =
// ignore: deprecated_member_use_from_same_package
generator.libclangDylib?.toFilePath() ??
libclangDylib?.toFilePath() ??
findDylibAtDefaultLocations(logger);
_clang ??= Clang(DynamicLibrary.open(libclangDylibPath));
if (Platform.environment['FFIGEN_NO_CLANG'] != 'true') {
Copy link
Contributor

Choose a reason for hiding this comment

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

If you're setting a dummy clang in deprecation_codegen_test.dart, why do you also need this environment variable stuff? Seems like they do the same job.

final libclangDylibPath =
// ignore: deprecated_member_use_from_same_package
generator.libclangDylib?.toFilePath() ??
libclangDylib?.toFilePath() ??
findDylibAtDefaultLocations(logger);
_clang ??= Clang(DynamicLibrary.open(libclangDylibPath));
}
}
}

Expand All @@ -60,6 +65,8 @@ class Context {
// skew. So the safest thing is to simply load clang the first time a Context
// is created, and reuse it for all subsequent runs.
Clang get clang => _clang!;
@visibleForTesting
set clang(Clang value) => _clang = value;
Clang? _clang;

class LibraryImports {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,11 @@ ObjCCategory? parseObjCCategoryDeclaration(
availability: apiAvailability.dartDoc,
),
context: context,
);
)..deprecatedMessage = apiAvailability.alwaysDeprecated
? (apiAvailability.deprecatedMessage?.isNotEmpty ?? false
? apiAvailability.deprecatedMessage
: '')
: null;

context.bindingsIndex.addObjCCategoryToSeen(usr, category);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,11 @@ void _parseSuperType(
originalName: getterName,
name: filters.renameMember(decl, getterName),
dartDoc: dartDoc ?? getterName,
deprecatedMessage: apiAvailability.alwaysDeprecated
? (apiAvailability.deprecatedMessage?.isNotEmpty ?? false
? apiAvailability.deprecatedMessage
: '')
: null,
kind: ObjCMethodKind.propertyGetter,
isClassMethod: isClassMethod,
isOptional: isOptionalMethod,
Expand All @@ -223,6 +228,7 @@ void _parseSuperType(
symbol: getter.symbol,
protocolMethodName: setterName,
dartDoc: dartDoc ?? setterName,
deprecatedMessage: getter.deprecatedMessage,
kind: ObjCMethodKind.propertySetter,
isClassMethod: isClassMethod,
isOptional: isOptionalMethod,
Expand Down Expand Up @@ -320,6 +326,11 @@ ObjCMethod? parseObjCMethod(
fallbackComment: methodName,
availability: apiAvailability.dartDoc,
),
deprecatedMessage: apiAvailability.alwaysDeprecated
? (apiAvailability.deprecatedMessage?.isNotEmpty ?? false
? apiAvailability.deprecatedMessage
: '')
: null,
kind: ObjCMethodKind.method,
isClassMethod: isClassMethod,
isOptional: isOptionalMethod,
Expand Down
2 changes: 1 addition & 1 deletion pkgs/ffigen/test/test_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ String configPath(String directory, String file) =>
absPath(configPathForTest(directory, file));

/// Returns the temp directory used to store bindings generated by tests.
String tmpDir = path.join(packagePathForTests, 'test', '.temp');
String tmpDir = path.join(packagePathForTests, 'test', '.temp with spaces');
Copy link
Contributor

Choose a reason for hiding this comment

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

you've mixed in changes from your other PR. Please revert these changes.


/// Generates actual file using library and tests using [expect] with expected.
///
Expand Down
174 changes: 174 additions & 0 deletions pkgs/ffigen/test/unit_tests/deprecation_codegen_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@

import 'dart:ffi' as ffi;
import 'package:ffigen/src/code_generator.dart';
import 'package:ffigen/src/code_generator/objc_interface.dart';
import 'package:ffigen/src/code_generator/objc_methods.dart';
import 'package:ffigen/src/config_provider/config.dart';
import 'package:ffigen/src/config_provider/config_types.dart';
import 'package:ffigen/src/context.dart';
import 'package:ffigen/src/header_parser/clang_bindings/clang_bindings.dart';
import 'package:ffigen/src/header_parser/sub_parsers/api_availability.dart';
import 'package:logging/logging.dart';
import 'package:test/test.dart';

import '../test_utils.dart';

void main() {
group('Deprecation Codegen', () {
late Context context;

setUp(() {
// Set a dummy clang to bypass libclang loading.
clang = Clang.fromLookup(<T extends ffi.NativeType>(String name) {
return ffi.Pointer<ffi.Void>.fromAddress(0).cast<T>();
});

final ffiGen = FfiGenerator(
output: Output(dartFile: Uri.file('unused.dart')),
headers: Headers(entryPoints: [Uri.file('unused.h')]),
functions: Functions.includeAll,
structs: Structs.includeAll,
enums: Enums.includeAll,
typedefs: Typedefs.includeAll,
objectiveC: const ObjectiveC(
interfaces: Interfaces.includeAll,
protocols: Protocols.includeAll,
categories: Categories.includeAll,
),
);
context = Context(Logger('test'), ffiGen);
});

test('func_deprecated', () {
final func = Func(
name: 'func_deprecated',
returnType: voidType,
originalName: 'func_deprecated',
)..deprecatedMessage = 'Deprecated';

final library = Library(
context: context,
bindings: [func],
);
library.forceFillNamesForTesting();

final generated = library.generate();
expect(generated, contains('@Deprecated("Deprecated")'));
expect(generated, contains('void func_deprecated()'));
});

test('struct_deprecated', () {
final struct = Struct(
name: 'DeprecatedStruct',
context: context,
)..deprecatedMessage = 'struct deprecated';

final library = Library(
context: context,
bindings: [struct],
);
library.forceFillNamesForTesting();

final generated = library.generate();
expect(generated, contains('@Deprecated("struct deprecated")'));
expect(generated, contains('final class DeprecatedStruct extends ffi.Opaque'));
});

test('enum_deprecated', () {
final enumClass = EnumClass(
name: 'DeprecatedEnum',
context: context,
)..deprecatedMessage = 'enum deprecated';

final library = Library(
context: context,
bindings: [enumClass],
);
library.forceFillNamesForTesting();

final generated = library.generate();
expect(generated, contains('@Deprecated("enum deprecated")'));
expect(generated, contains('class DeprecatedEnum'));
});

test('typedef_deprecated', () {
final typedef = Typealias(
name: 'DeprecatedTypedef',
type: intType,
)..deprecatedMessage = 'typedef deprecated';

final library = Library(
context: context,
bindings: [typedef],
);
library.forceFillNamesForTesting();

final generated = library.generate();
expect(generated, contains('@Deprecated("typedef deprecated")'));
expect(generated, contains('typedef DeprecatedTypedef = ffi.Int;'));
});

test('objc_interface_deprecated', () {
final itf = ObjCInterface(
originalName: 'DeprecatedInterface',
apiAvailability: ApiAvailability(
alwaysDeprecated: true,
deprecatedMessage: 'interface deprecated',
externalVersions: null,
),
context: context,
);

final library = Library(
context: context,
bindings: [itf],
);
library.forceFillNamesForTesting();

final generated = library.generate();
expect(generated, contains('@Deprecated("interface deprecated")'));
expect(generated, contains('extension type DeprecatedInterface'));
});

test('objc_method_deprecated', () {
final itf = ObjCInterface(
originalName: 'InterfaceWithDeprecatedMethod',
apiAvailability: ApiAvailability(externalVersions: null),
context: context,
);
final method = ObjCMethod(
context: context,
originalName: 'deprecatedMethod',
name: 'deprecatedMethod',
kind: ObjCMethodKind.method,
isClassMethod: false,
isOptional: false,
returnType: voidType,
params: [],
family: null,
apiAvailability: ApiAvailability(
alwaysDeprecated: true,
deprecatedMessage: 'method deprecated',
externalVersions: null,
),
ownershipAttribute: null,
consumesSelfAttribute: false,
deprecatedMessage: 'method deprecated',
);
itf.addMethod(method);

// We need to fill the method's msgSend to avoid late initialization error during generation
method.msgSend = context.objCBuiltInFunctions.getMsgSendFunc(voidType, []);

final library = Library(
context: context,
bindings: [itf],
);
library.forceFillNamesForTesting();

final generated = library.generate();
expect(generated, contains('@Deprecated("method deprecated")'));
expect(generated, contains('void deprecatedMethod()'));
});
});
}
13 changes: 13 additions & 0 deletions pkgs/ffigen/test/unit_tests/utils_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

import 'package:ffigen/src/code_generator/utils.dart';
import 'package:test/test.dart';

void main() {
test('makeDeprecatedAnnotation', () {
expect(makeDeprecatedAnnotation(null), '');
expect(makeDeprecatedAnnotation(''), "@Deprecated('Deprecated')");
expect(makeDeprecatedAnnotation('foo'), '@Deprecated("foo")');
expect(makeDeprecatedAnnotation('foo "bar"'), r'@Deprecated("foo \"bar\"")');
expect(makeDeprecatedAnnotation(r'foo $bar'), r'@Deprecated("foo \$bar")');
});
}
2 changes: 1 addition & 1 deletion pkgs/hooks/test/helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Future<void> inTempDir(
String? prefix,
bool keepTemp = false,
}) async {
final tempDir = await Directory.systemTemp.createTemp(prefix);
final tempDir = await Directory.systemTemp.createTemp('${prefix ?? ''} ');
// Deal with Windows temp folder aliases.
final tempUri = Directory(
await tempDir.resolveSymbolicLinks(),
Expand Down
Loading
Loading