Skip to content

Commit 9907a06

Browse files
elicwhitemeta-codesync[bot]
authored andcommitted
Add nested error formatting for all error types (#55376)
Summary: Pull Request resolved: #55376 Reviewer, read `ErrorFormatting-test.snap.js` first, to see the impact of this change. Previously, only `incompatibleTypes` errors showed hierarchical nesting with full path context. Other error types (addedEnumCases, removedUnionCases, addedProps, etc.) displayed flat messages that made it difficult to trace where in the type hierarchy an issue occurred. This change enriches ComparisonResult types with an optional `errorLog` field that carries path context from TypeDiffing through VersionDiffing to ErrorFormatting. Now all error types display with the same recursive nesting format, making it easier for developers to understand exactly where compatibility issues occur in deeply nested structures. Changelog: [Internal] Reviewed By: makovkastar Differential Revision: D91251882 fbshipit-source-id: 3d6f9676e68e5a8471f7870bbd02d35a257d4416
1 parent 31f7e84 commit 9907a06

7 files changed

Lines changed: 655 additions & 185 deletions

File tree

packages/react-native-compatibility-check/src/ComparisonResult.js

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,12 +135,36 @@ export type NullableComparisonResult = {
135135
export type ComparisonResult =
136136
| {status: 'matching'}
137137
| {status: 'skipped'}
138-
| {status: 'nullableChange', nullableLog: NullableComparisonResult}
139-
| {status: 'properties', propertyLog: PropertiesComparisonResult}
140-
| {status: 'members', memberLog: MembersComparisonResult}
141-
| {status: 'unionMembers', memberLog: UnionMembersComparisonResult}
142-
| {status: 'functionChange', functionChangeLog: FunctionComparisonResult}
143-
| {status: 'positionalTypeChange', changeLog: PositionalComparisonResult}
138+
| {
139+
status: 'nullableChange',
140+
nullableLog: NullableComparisonResult,
141+
errorLog?: TypeComparisonError,
142+
}
143+
| {
144+
status: 'properties',
145+
propertyLog: PropertiesComparisonResult,
146+
errorLog?: TypeComparisonError,
147+
}
148+
| {
149+
status: 'members',
150+
memberLog: MembersComparisonResult,
151+
errorLog?: TypeComparisonError,
152+
}
153+
| {
154+
status: 'unionMembers',
155+
memberLog: UnionMembersComparisonResult,
156+
errorLog?: TypeComparisonError,
157+
}
158+
| {
159+
status: 'functionChange',
160+
functionChangeLog: FunctionComparisonResult,
161+
errorLog?: TypeComparisonError,
162+
}
163+
| {
164+
status: 'positionalTypeChange',
165+
changeLog: PositionalComparisonResult,
166+
errorLog?: TypeComparisonError,
167+
}
144168
| {status: 'error', errorLog: TypeComparisonError};
145169

146170
export function isPropertyLogEmpty(

packages/react-native-compatibility-check/src/ErrorFormatting.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export function formatErrorMessage(
3232
): string {
3333
switch (error.type) {
3434
case 'PropertyComparisonError':
35+
const propertyPreviousError = error.previousError;
3536
const formattedProperties = error.mismatchedProperties.map(
3637
individualPropertyError =>
3738
indentedLineStart(indent + 1) +
@@ -42,7 +43,14 @@ export function formatErrorMessage(
4243
formatErrorMessage(individualPropertyError.fault, indent + 2)
4344
: ''),
4445
);
45-
return error.message + formattedProperties.join('');
46+
return (
47+
(propertyPreviousError != null
48+
? formatErrorMessage(propertyPreviousError, indent) +
49+
indentedLineStart(indent + 1)
50+
: '') +
51+
error.message +
52+
formattedProperties.join('')
53+
);
4654
case 'PositionalComparisonError':
4755
const formattedPositionalChanges = error.erroneousItems.map(
4856
([index, type]) =>
@@ -188,7 +196,13 @@ function formatTypeAnnotation(annotation: CompleteTypeAnnotation): string {
188196
? `'${annotation.value}'`
189197
: annotation.value;
190198
case 'UnionTypeAnnotation':
191-
const validUnionType = parseValidUnionType(annotation);
199+
let validUnionType;
200+
try {
201+
validUnionType = parseValidUnionType(annotation);
202+
} catch (_e: mixed) {
203+
// parseValidUnionType throws for unsupported union types
204+
return 'Union<mixed>';
205+
}
192206
switch (validUnionType) {
193207
case 'boolean':
194208
if (

packages/react-native-compatibility-check/src/TypeDiffing.js

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -569,12 +569,22 @@ export function compareObjectTypes<T: CompleteTypeAnnotation>(
569569
return {
570570
status: 'properties',
571571
propertyLog: {missingProperties: sortedOlderTypes},
572+
errorLog: typeAnnotationComparisonError(
573+
'Object has property changes',
574+
objectTypeAnnotation(newerPropertyTypes),
575+
objectTypeAnnotation(olderPropertyTypes),
576+
),
572577
};
573578
}
574579
if (sortedOlderTypes.length === 0) {
575580
return {
576581
status: 'properties',
577582
propertyLog: {addedProperties: sortedNewerTypes},
583+
errorLog: typeAnnotationComparisonError(
584+
'Object has property changes',
585+
objectTypeAnnotation(newerPropertyTypes),
586+
objectTypeAnnotation(olderPropertyTypes),
587+
),
578588
};
579589
}
580590
const result = comparePropertyArrays(sortedNewerTypes, sortedOlderTypes);
@@ -602,14 +612,20 @@ export function compareObjectTypes<T: CompleteTypeAnnotation>(
602612
return makeError(
603613
typeAnnotationComparisonError(
604614
'Object types do not match.',
605-
// $FlowFixMe[incompatible-type]
606615
objectTypeAnnotation(newerPropertyTypes),
607-
// $FlowFixMe[incompatible-type]
608616
objectTypeAnnotation(olderPropertyTypes),
609617
),
610618
);
611619
}
612-
return {status: 'properties', propertyLog: result};
620+
return {
621+
status: 'properties',
622+
propertyLog: result,
623+
errorLog: typeAnnotationComparisonError(
624+
'Object has property changes',
625+
objectTypeAnnotation(newerPropertyTypes),
626+
objectTypeAnnotation(olderPropertyTypes),
627+
),
628+
};
613629
}
614630

615631
function objectTypeAnnotation<T>(
@@ -856,7 +872,15 @@ export function compareEnumDeclarationWithMembers(
856872
);
857873
}
858874

859-
return {status: 'members', memberLog: result};
875+
return {
876+
status: 'members',
877+
memberLog: result,
878+
errorLog: typeAnnotationComparisonError(
879+
'Enum has member changes',
880+
newerDeclaration,
881+
olderDeclaration,
882+
),
883+
};
860884
}
861885

862886
function compareNullableChange(
@@ -895,6 +919,11 @@ function compareNullableChange(
895919
newType: newerAnnotation,
896920
oldType: olderAnnotation,
897921
},
922+
errorLog: typeAnnotationComparisonError(
923+
'Nullable type has changes',
924+
newerAnnotation,
925+
olderAnnotation,
926+
),
898927
};
899928
}
900929
const interiorLog = compareTypeAnnotation(newVoidRemoved, oldVoidRemoved);
@@ -917,6 +946,11 @@ function compareNullableChange(
917946
newType: newerAnnotation,
918947
oldType: olderAnnotation,
919948
},
949+
errorLog: typeAnnotationComparisonError(
950+
'Nullable type has changes',
951+
newerAnnotation,
952+
olderAnnotation,
953+
),
920954
};
921955
default:
922956
return {
@@ -928,6 +962,12 @@ function compareNullableChange(
928962
newType: newerAnnotation,
929963
oldType: olderAnnotation,
930964
},
965+
errorLog: typeAnnotationComparisonError(
966+
'Nullable type has changes',
967+
newerAnnotation,
968+
olderAnnotation,
969+
interiorLog.errorLog,
970+
),
931971
};
932972
}
933973
}
@@ -977,7 +1017,15 @@ export function compareUnionTypes(
9771017
);
9781018
}
9791019

980-
return {status: 'unionMembers', memberLog: result};
1020+
return {
1021+
status: 'unionMembers',
1022+
memberLog: result,
1023+
errorLog: typeAnnotationComparisonError(
1024+
'Union has member changes',
1025+
newerType,
1026+
olderType,
1027+
),
1028+
};
9811029
}
9821030

9831031
export function comparePromiseTypes(
@@ -1128,6 +1176,11 @@ export function compareStringLiteralUnionTypes(
11281176
return {
11291177
status: 'positionalTypeChange',
11301178
changeLog,
1179+
errorLog: typeAnnotationComparisonError(
1180+
'String literal union has member changes',
1181+
newerType,
1182+
olderType,
1183+
),
11311184
};
11321185
case 'matching':
11331186
return {status: 'matching'};
@@ -1206,7 +1259,15 @@ export function compareFunctionTypes(
12061259
if (isFunctionLogEmpty(functionChanges)) {
12071260
return {status: 'matching'};
12081261
}
1209-
return {status: 'functionChange', functionChangeLog: functionChanges};
1262+
return {
1263+
status: 'functionChange',
1264+
functionChangeLog: functionChanges,
1265+
errorLog: typeAnnotationComparisonError(
1266+
'Function has parameter or return type changes',
1267+
newerType,
1268+
olderType,
1269+
),
1270+
};
12101271
}
12111272

12121273
type ArrayComparisonResult =

0 commit comments

Comments
 (0)