Skip to content

Commit 81d1434

Browse files
committed
add more to spec
1 parent cd4d9e9 commit 81d1434

File tree

3 files changed

+376
-73
lines changed

3 files changed

+376
-73
lines changed

compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateSourceLocations.ts

Lines changed: 101 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ import {Result} from '../Utils/Result';
2727

2828
/**
2929
* Some common node types that are important for coverage tracking.
30-
* Based on istanbul-lib-instrument
30+
* Based on istanbul-lib-instrument + some other common nodes we expect to be present in the generated AST.
31+
*
32+
* Note: For VariableDeclaration, VariableDeclarator, and Identifier, we enforce stricter validation
33+
* that requires both the source location AND node type to match in the generated AST. This ensures
34+
* that variable declarations maintain their structural integrity through compilation.
3135
*/
3236
const IMPORTANT_INSTRUMENTED_TYPES = new Set([
3337
'ArrowFunctionExpression',
@@ -54,6 +58,14 @@ const IMPORTANT_INSTRUMENTED_TYPES = new Set([
5458
'LabeledStatement',
5559
'ConditionalExpression',
5660
'LogicalExpression',
61+
62+
/**
63+
* Note: these aren't important for coverage tracking,
64+
* but we still want to track them to ensure we aren't regressing them when
65+
* we fix the source location tracking for other nodes.
66+
*/
67+
'VariableDeclaration',
68+
'Identifier',
5769
]);
5870

5971
/**
@@ -115,9 +127,10 @@ export function validateSourceLocations(
115127
const errors = new CompilerError();
116128

117129
// Step 1: Collect important locations from the original source
130+
// Note: Multiple node types can share the same location (e.g. VariableDeclarator and Identifier)
118131
const importantOriginalLocations = new Map<
119132
string,
120-
{loc: t.SourceLocation; nodeType: string}
133+
{loc: t.SourceLocation; nodeTypes: Set<string>}
121134
>();
122135

123136
func.traverse({
@@ -137,20 +150,31 @@ export function validateSourceLocations(
137150
// Collect the location if it exists
138151
if (node.loc) {
139152
const key = locationKey(node.loc);
140-
importantOriginalLocations.set(key, {
141-
loc: node.loc,
142-
nodeType: node.type,
143-
});
153+
const existing = importantOriginalLocations.get(key);
154+
if (existing) {
155+
existing.nodeTypes.add(node.type);
156+
} else {
157+
importantOriginalLocations.set(key, {
158+
loc: node.loc,
159+
nodeTypes: new Set([node.type]),
160+
});
161+
}
144162
}
145163
},
146164
});
147165

148-
// Step 2: Collect all locations from the generated AST
149-
const generatedLocations = new Set<string>();
166+
// Step 2: Collect all locations from the generated AST with their node types
167+
const generatedLocations = new Map<string, Set<string>>();
150168

151169
function collectGeneratedLocations(node: t.Node): void {
152170
if (node.loc) {
153-
generatedLocations.add(locationKey(node.loc));
171+
const key = locationKey(node.loc);
172+
const nodeTypes = generatedLocations.get(key);
173+
if (nodeTypes) {
174+
nodeTypes.add(node.type);
175+
} else {
176+
generatedLocations.set(key, new Set([node.type]));
177+
}
154178
}
155179

156180
// Use Babel's VISITOR_KEYS to traverse only actual node properties
@@ -184,21 +208,74 @@ export function validateSourceLocations(
184208
}
185209

186210
// Step 3: Validate that all important locations are preserved
187-
for (const [key, {loc, nodeType}] of importantOriginalLocations) {
188-
if (!generatedLocations.has(key)) {
189-
errors.pushDiagnostic(
190-
CompilerDiagnostic.create({
191-
category: ErrorCategory.Todo,
192-
reason: 'Important source location missing in generated code',
193-
description:
194-
`Source location for ${nodeType} is missing in the generated output. This can cause coverage instrumentation ` +
195-
`to fail to track this code properly, resulting in inaccurate coverage reports.`,
196-
}).withDetails({
197-
kind: 'error',
198-
loc,
199-
message: null,
200-
}),
201-
);
211+
// For certain node types, also validate that the node type matches
212+
const strictNodeTypes = new Set([
213+
'VariableDeclaration',
214+
'VariableDeclarator',
215+
'Identifier',
216+
]);
217+
218+
const reportMissingLocation = (loc: t.SourceLocation, nodeType: string) => {
219+
errors.pushDiagnostic(
220+
CompilerDiagnostic.create({
221+
category: ErrorCategory.Todo,
222+
reason: 'Important source location missing in generated code',
223+
description:
224+
`Source location for ${nodeType} is missing in the generated output. This can cause coverage instrumentation ` +
225+
`to fail to track this code properly, resulting in inaccurate coverage reports.`,
226+
}).withDetails({
227+
kind: 'error',
228+
loc,
229+
message: null,
230+
}),
231+
);
232+
};
233+
234+
const reportWrongNodeType = (
235+
loc: t.SourceLocation,
236+
expectedType: string,
237+
actualTypes: Set<string>,
238+
) => {
239+
errors.pushDiagnostic(
240+
CompilerDiagnostic.create({
241+
category: ErrorCategory.Todo,
242+
reason: 'Important source location has wrong node type in generated code',
243+
description:
244+
`Source location for ${expectedType} exists in the generated output but with wrong node type(s): ${Array.from(actualTypes).join(', ')}. ` +
245+
`This can cause coverage instrumentation to fail to track this code properly, resulting in inaccurate coverage reports.`,
246+
}).withDetails({
247+
kind: 'error',
248+
loc,
249+
message: null,
250+
}),
251+
);
252+
};
253+
254+
for (const [key, {loc, nodeTypes}] of importantOriginalLocations) {
255+
const generatedNodeTypes = generatedLocations.get(key);
256+
257+
if (!generatedNodeTypes) {
258+
// Location is completely missing
259+
reportMissingLocation(loc, Array.from(nodeTypes).join(', '));
260+
} else {
261+
// Location exists, check each node type
262+
for (const nodeType of nodeTypes) {
263+
if (strictNodeTypes.has(nodeType) && !generatedNodeTypes.has(nodeType)) {
264+
// For strict node types, the specific node type must be present
265+
// Check if any generated node type is also an important original node type
266+
const hasValidNodeType = Array.from(generatedNodeTypes).some(genType =>
267+
nodeTypes.has(genType)
268+
);
269+
270+
if (hasValidNodeType) {
271+
// At least one generated node type is valid (also in original), so this is just missing
272+
reportMissingLocation(loc, nodeType);
273+
} else {
274+
// None of the generated node types are in original - this is wrong node type
275+
reportWrongNodeType(loc, nodeType, generatedNodeTypes);
276+
}
277+
}
278+
}
202279
}
203280
}
204281

0 commit comments

Comments
 (0)