Skip to content

Commit 849ea70

Browse files
lilyballLily Ballard
authored andcommitted
swift: fix initializers with properties named self
When a type has a field `self`, the synthesized initializers need to avoid using `self` as the internal parameter name, instead permuting it. By default we pick `_self`, but we also ensure this doesn't conflict with any other declared properties; in the event there is a conflict, we append `_` until it's unique. This makes the test introduced by the previous commit start passing. Fixes #1530.
1 parent 7962955 commit 849ea70

4 files changed

Lines changed: 113 additions & 23 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- `apollo-codegen-scala`
1010
- <First `apollo-codegen-scala` related entry goes here>
1111
- `apollo-codegen-swift`
12+
- Ensure fields named `self` don't cause compilation errors in the generated code [#1533](https://github.com/apollographql/apollo-tooling/pull/1533)
1213
- Preserve leading/trailing underscores on field names [#1533](https://github.com/apollographql/apollo-tooling/pull/1533)
1314
- `apollo-codegen-typescript`
1415
- <First `apollo-codegen-typescript` related entry goes here>

packages/apollo-codegen-swift/src/codeGeneration.ts

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,9 @@ export class SwiftAPIGenerator extends SwiftGenerator<CompilerContext> {
643643
swift`"__typename": ${SwiftSource.string(
644644
variant.possibleTypes[0].toString()
645645
)}`,
646-
...properties.map(this.propertyAssignmentForField, this)
646+
...properties.map(p =>
647+
this.propertyAssignmentForField(p, properties)
648+
)
647649
],
648650
", "
649651
) || ":",
@@ -683,7 +685,9 @@ export class SwiftAPIGenerator extends SwiftGenerator<CompilerContext> {
683685
swift`"__typename": ${SwiftSource.string(
684686
possibleType.toString()
685687
)}`,
686-
...properties.map(this.propertyAssignmentForField, this)
688+
...properties.map(p =>
689+
this.propertyAssignmentForField(p, properties)
690+
)
687691
],
688692
", "
689693
) || ":",
@@ -695,30 +699,37 @@ export class SwiftAPIGenerator extends SwiftGenerator<CompilerContext> {
695699
}
696700
}
697701

698-
propertyAssignmentForField(field: {
699-
responseKey: string;
700-
propertyName: string;
701-
type: GraphQLType;
702-
isConditional?: boolean;
703-
structName?: string;
704-
}): SwiftSource {
702+
propertyAssignmentForField(
703+
field: {
704+
responseKey: string;
705+
propertyName: string;
706+
type: GraphQLType;
707+
isConditional?: boolean;
708+
structName?: string;
709+
},
710+
properties: { propertyName: string }[]
711+
): SwiftSource {
705712
const {
706713
responseKey,
707714
propertyName,
708715
type,
709716
isConditional,
710717
structName
711718
} = field;
719+
const parameterName = this.helpers.internalParameterName(
720+
propertyName,
721+
properties
722+
);
712723
const valueExpression = isCompositeType(getNamedType(type))
713724
? this.helpers.mapExpressionForType(
714725
type,
715726
isConditional,
716727
expression => swift`${expression}.resultMap`,
717-
SwiftSource.identifier(propertyName),
728+
SwiftSource.identifier(parameterName),
718729
structName!,
719730
"ResultMap"
720731
)
721-
: SwiftSource.identifier(propertyName);
732+
: SwiftSource.identifier(parameterName);
722733
return swift`${SwiftSource.string(responseKey)}: ${valueExpression}`;
723734
}
724735

@@ -882,7 +893,12 @@ export class SwiftAPIGenerator extends SwiftGenerator<CompilerContext> {
882893

883894
this.withinBlock(() => {
884895
properties.forEach(({ propertyName }) => {
885-
this.printOnNewline(swift`self.${propertyName} = ${propertyName}`);
896+
this.printOnNewline(
897+
swift`self.${propertyName} = ${this.helpers.internalParameterName(
898+
propertyName,
899+
properties
900+
)}`
901+
);
886902
});
887903
});
888904
}
@@ -891,12 +907,20 @@ export class SwiftAPIGenerator extends SwiftGenerator<CompilerContext> {
891907
this.print(swift`(`);
892908
this.print(
893909
join(
894-
properties.map(({ propertyName, typeName, isOptional }) =>
895-
join([
896-
swift`${propertyName}: ${typeName}`,
910+
properties.map(({ propertyName, typeName, isOptional }) => {
911+
const internalName = this.helpers.internalParameterName(
912+
propertyName,
913+
properties
914+
);
915+
const decl =
916+
internalName === propertyName
917+
? propertyName
918+
: swift`${propertyName} ${internalName}`;
919+
return join([
920+
swift`${decl}: ${typeName}`,
897921
isOptional ? swift` = nil` : undefined
898-
])
899-
),
922+
]);
923+
}),
900924
", "
901925
)
902926
);
@@ -1168,12 +1192,20 @@ export class SwiftAPIGenerator extends SwiftGenerator<CompilerContext> {
11681192
this.print(swift`(`);
11691193
this.print(
11701194
join(
1171-
properties.map(({ propertyName, typeName, isOptional }) =>
1172-
join([
1173-
swift`${propertyName}: ${typeName}`,
1195+
properties.map(({ propertyName, typeName, isOptional }) => {
1196+
const internalName = this.helpers.internalParameterName(
1197+
propertyName,
1198+
properties
1199+
);
1200+
const decl =
1201+
internalName === propertyName
1202+
? propertyName
1203+
: swift`${propertyName} ${internalName}`;
1204+
return join([
1205+
swift`${decl}: ${typeName}`,
11741206
isOptional ? swift` = nil` : undefined
1175-
])
1176-
),
1207+
]);
1208+
}),
11771209
", "
11781210
)
11791211
);
@@ -1186,7 +1218,12 @@ export class SwiftAPIGenerator extends SwiftGenerator<CompilerContext> {
11861218
join(
11871219
properties.map(
11881220
({ name, propertyName }) =>
1189-
swift`${SwiftSource.string(name)}: ${propertyName}`
1221+
swift`${SwiftSource.string(
1222+
name
1223+
)}: ${this.helpers.internalParameterName(
1224+
propertyName,
1225+
properties
1226+
)}`
11901227
),
11911228
", "
11921229
) || ":",

packages/apollo-codegen-swift/src/helpers.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,24 @@ export class Helpers {
135135
);
136136
}
137137

138+
/**
139+
* Returns the internal parameter name for a given property name.
140+
*
141+
* If the property name is valid to use, it's returned directly. Otherwise it's prefixed with an
142+
* underscore and modified until it's unique among the given property set.
143+
* @param propertyName The name of the property.
144+
* @param properties A list of properties that should be consulted when producing a unique name.
145+
* @returns The name to use for the internal parameter name for the property.
146+
*/
147+
internalParameterName(
148+
propertyName: string,
149+
properties: { propertyName: string }[]
150+
): string {
151+
return SwiftSource.isValidParameterName(propertyName)
152+
? propertyName
153+
: makeUniqueName(`_${propertyName}`, properties);
154+
}
155+
138156
// Properties
139157

140158
propertyFromField(
@@ -351,6 +369,25 @@ function makeClosureSignature(
351369
return closureSignature;
352370
}
353371

372+
/**
373+
* Takes a proposed name and modifies it to be unique given a list of properties.
374+
* @param proposedName The proposed name that shouldn't conflict with any property.
375+
* @param properties A list of properties the name shouldn't conflict with.
376+
* @returns A name based on `proposedName` that doesn't match any existing property name.
377+
*/
378+
function makeUniqueName(
379+
proposedName: string,
380+
properties: { propertyName: string }[]
381+
): string {
382+
// Assume conflicts are very rare and property lists are short, and just do a linear search. If
383+
// we find a conflict, start over with the modified name.
384+
for (let name = proposedName; ; name += "_") {
385+
if (properties.every(prop => prop.propertyName != name)) {
386+
return name;
387+
}
388+
}
389+
}
390+
354391
/**
355392
* Converts a value from "underscore_case" to "camelCase".
356393
*

packages/apollo-codegen-swift/src/language.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,21 @@ export class SwiftSource {
215215
);
216216
}
217217

218+
/**
219+
* Returns whether the given name is valid as a method parameter name.
220+
*
221+
* Certain tokens aren't valid as method parameter names, even when escaped with backticks, as
222+
* the compiler interprets the keyword and identifier as the same thing. In particular, `self`
223+
* works this way.
224+
* @param input The proposed parameter name.
225+
* @returns `true` if the name can be used, or `false` if it needs a separate internal parameter
226+
* name.
227+
*/
228+
static isValidParameterName(input: string): boolean {
229+
// Right now `self` is the only known token that we can't use with escaping.
230+
return input !== "self";
231+
}
232+
218233
/**
219234
* Template tag for producing a `SwiftSource` value without performing escaping.
220235
*

0 commit comments

Comments
 (0)