Skip to content

Commit fa4045e

Browse files
Erich Grahamfacebook-github-bot
Erich Graham
authored andcommittedMay 20, 2021
Add ios_assume_nonnull flag to react native codegen library (#31543)
Summary: Pull Request resolved: #31543 Changelog: [iOS][Added] - Description When compiling iOS apps with flag `-Wnullability-completeness` (like Lightspeed app and soon Instagram), Objective-C headers are required to either have full *explicit* nullability annotations on all members of its public API, or none at all; partially annotated headers will fail to build that module. RN native modules are currently generated with *partial* annotations. This works today because most apps are not compiled with `-Wnullability-completeness` turned on. But when we flip the switch for Instagram, the app doesn't build due to importing these RN partially annotated modules. JavsScript Flow types are implied nonnull, and the current RN codegen translates Flow's [maybe/optional](https://flow.org/en/docs/types/maybe/) type to Obj-C `_Nullable` annotation, and everything else without an explicit Obj-C annotation. However this creates a mismatch with the Obj-C type system, where the implied default is *unannotated*, which is handled differently from nonnull when built with the nullability compiler flags. There is a simple Obj-C macro that automatically adds *explicit nonnull* annotations to all members in a header: `NS_ASSUME_NONNULL_BEGIN` / `NS_ASSUME_NONNULL_END`. If we add this to *all* RN-generated headers, however, we run into issues: 1) We may erroneously assume any previously-unannotated header was meant to be nonnull and cause future bugs 2) Another compiler flag (`-Wnullable-to-nonnull-conversion`) statically analyzes Obj-C implementation code to prevent us from ever passing null to one of these headers. Much existing Obj-C code will break here, and it's ambiguous if these are true or false positives because of the first point. Instead, in this diff we add a new BUCK flag `ios_assume_nonnull` to let module authors opt into automatic nonnull for unannotated members so that Obj-C headers are generated correctly in alignment with Flow's type system. We can migrate all libraries individually as needed and eventually make this the RN native codegen default. Reviewed By: RSNara Differential Revision: D28396446 fbshipit-source-id: ad3a3a97ab19183df4ef504b1c3140596c8f69ca
1 parent d670381 commit fa4045e

27 files changed

+1246
-10
lines changed
 

‎Libraries/BUCK

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ rn_codegen(
1212
name = "FBReactNativeSpec",
1313
android_package_name = "com.facebook.fbreact.specs",
1414
codegen_modules = True,
15+
ios_assume_nonnull = False,
1516
library_labels = ["supermodule:xplat/default/public.react_native.infra"],
1617
native_module_spec_name = "FBReactNativeSpec",
1718
)
@@ -20,5 +21,6 @@ rn_codegen(
2021
rn_codegen(
2122
name = "FBReactNativeComponentSpec",
2223
codegen_components = True,
24+
ios_assume_nonnull = False,
2325
library_labels = ["supermodule:xplat/default/public.react_native.infra"],
2426
)

‎packages/react-native-codegen/BUCK

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ rn_codegen_components(
2929
rn_codegen_modules(
3030
name = "FBReactNativeTestSpec",
3131
android_package_name = "com.facebook.fbreact.specs",
32+
ios_assume_nonnull = False,
3233
schema_target = ":codegen_tests_schema",
3334
)
3435

‎packages/react-native-codegen/DEFS.bzl

+3-1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ def rn_codegen_cli():
105105
def rn_codegen_modules(
106106
name,
107107
android_package_name,
108+
ios_assume_nonnull,
108109
library_labels = [],
109110
schema_target = ""):
110111
generate_fixtures_rule_name = "{}-codegen-modules".format(name)
@@ -118,11 +119,12 @@ def rn_codegen_modules(
118119
fb_native.genrule(
119120
name = generate_fixtures_rule_name,
120121
srcs = native.glob(["src/generators/**/*.js"]),
121-
cmd = "$(exe {generator_script}) $(location {schema_target}) {library_name} $OUT {android_package_name}".format(
122+
cmd = "$(exe {generator_script}) $(location {schema_target}) {library_name} $OUT {android_package_name} {ios_assume_nonnull}".format(
122123
generator_script = react_native_root_target("packages/react-native-codegen:generate_all_from_schema"),
123124
schema_target = schema_target,
124125
library_name = name,
125126
android_package_name = android_package_name,
127+
ios_assume_nonnull = ios_assume_nonnull,
126128
),
127129
out = "codegenfiles-{}".format(name),
128130
labels = ["codegen_rule"],

‎packages/react-native-codegen/e2e/__tests__/modules/GenerateModuleObjCpp-test.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,19 @@ function getModules(): SchemaType {
3838
describe('GenerateModuleObjCpp', () => {
3939
it('can generate a header file NativeModule specs', () => {
4040
const libName = 'RNCodegenModuleFixtures';
41-
const output = generator.generate(libName, getModules());
41+
const output = generator.generate(libName, getModules(), undefined, false);
42+
expect(output.get(libName + '.h')).toMatchSnapshot();
43+
});
44+
45+
it('can generate a header file NativeModule specs with assume nonnull enabled', () => {
46+
const libName = 'RNCodegenModuleFixtures';
47+
const output = generator.generate(libName, getModules(), undefined, true);
4248
expect(output.get(libName + '.h')).toMatchSnapshot();
4349
});
4450

4551
it('can generate an implementation file NativeModule specs', () => {
4652
const libName = 'RNCodegenModuleFixtures';
47-
const output = generator.generate(libName, getModules());
53+
const output = generator.generate(libName, getModules(), undefined, false);
4854
expect(output.get(libName + '-generated.mm')).toMatchSnapshot();
4955
});
5056
});

0 commit comments

Comments
 (0)
Please sign in to comment.