Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(semantic): TSConditionalType scope enter/exit locations #6351

Merged
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 crates/oxc_ast/src/ast/ts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -347,10 +347,12 @@ pub struct TSConditionalType<'a> {
/// The type before `extends` in the test expression.
pub check_type: TSType<'a>,
/// The type `check_type` is being tested against.
#[scope(enter_before)]
pub extends_type: TSType<'a>,
/// The type evaluated to if the test is true.
pub true_type: TSType<'a>,
/// The type evaluated to if the test is false.
#[scope(exit_before)]
pub false_type: TSType<'a>,
#[serde(skip)]
#[clone_in(default)]
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_ast/src/generated/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1913,12 +1913,12 @@ pub mod walk {
pub fn walk_ts_conditional_type<'a, V: Visit<'a>>(visitor: &mut V, it: &TSConditionalType<'a>) {
let kind = AstKind::TSConditionalType(visitor.alloc(it));
visitor.enter_node(kind);
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
visitor.visit_ts_type(&it.check_type);
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
visitor.visit_ts_type(&it.extends_type);
visitor.visit_ts_type(&it.true_type);
visitor.visit_ts_type(&it.false_type);
visitor.leave_scope();
visitor.visit_ts_type(&it.false_type);
visitor.leave_node(kind);
}

Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_ast/src/generated/visit_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1956,12 +1956,12 @@ pub mod walk_mut {
) {
let kind = AstType::TSConditionalType;
visitor.enter_node(kind);
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
visitor.visit_ts_type(&mut it.check_type);
visitor.enter_scope(ScopeFlags::empty(), &it.scope_id);
visitor.visit_ts_type(&mut it.extends_type);
visitor.visit_ts_type(&mut it.true_type);
visitor.visit_ts_type(&mut it.false_type);
visitor.leave_scope();
visitor.visit_ts_type(&mut it.false_type);
visitor.leave_node(kind);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,44 @@ SCOPES
{
"children": [
{
"children": [
"children": [],
"flags": "ScopeFlags(StrictMode)",
"id": 2,
"node": "TSConditionalType",
"symbols": [
{
"children": [],
"flags": "ScopeFlags(StrictMode)",
"id": 3,
"node": "TSConditionalType",
"symbols": [
"flags": "SymbolFlags(TypeParameter)",
"id": 2,
"name": "U",
"node": "TSTypeParameter(U)",
"references": [
{
"flags": "SymbolFlags(TypeParameter)",
"id": 3,
"flags": "ReferenceFlags(Type)",
"id": 2,
"name": "U",
"node": "TSTypeParameter(U)",
"references": [
{
"flags": "ReferenceFlags(Type)",
"id": 5,
"name": "U",
"node_id": 33
}
]
"node_id": 19
}
]
}
],
]
},
{
"children": [],
"flags": "ScopeFlags(StrictMode)",
"id": 2,
"id": 3,
"node": "TSConditionalType",
"symbols": [
{
"flags": "SymbolFlags(TypeParameter)",
"id": 2,
"id": 3,
"name": "U",
"node": "TSTypeParameter(U)",
"references": [
{
"flags": "ReferenceFlags(Type)",
"id": 2,
"id": 5,
"name": "U",
"node_id": 19
"node_id": 33
}
]
}
Expand Down
22 changes: 22 additions & 0 deletions crates/oxc_semantic/tests/integration/scopes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,3 +239,25 @@ fn get_child_ids() {
let child_scope_ids = scopes.get_child_ids(child_scope_ids[0]);
assert!(child_scope_ids.is_empty());
}

#[test]
fn test_ts_conditional_types() {
SemanticTester::ts("type A<T> = T extends string ? T : false;")
.has_some_symbol("T")
.has_number_of_references(2)
.test();

// Conditional types create a new scope after check_type.
SemanticTester::ts(
"type S<A> = A extends (infer B extends number ? string : never) ? B : false;",
)
.has_some_symbol("B")
.has_number_of_references(1)
.test();

// Inferred type parameter is only available within true branch
SemanticTester::ts("type S<A> = A extends infer R ? never : R")
.has_some_symbol("R")
.has_number_of_references(0)
.test();
}
16 changes: 8 additions & 8 deletions crates/oxc_traverse/src/generated/walk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3964,13 +3964,6 @@ pub(crate) unsafe fn walk_ts_conditional_type<'a, Tr: Traverse<'a>>(
ctx: &mut TraverseCtx<'a>,
) {
traverser.enter_ts_conditional_type(&mut *node, ctx);
let previous_scope_id = ctx.current_scope_id();
ctx.set_current_scope_id(
(*((node as *mut u8).add(ancestor::OFFSET_TS_CONDITIONAL_TYPE_SCOPE_ID)
as *mut Cell<Option<ScopeId>>))
.get()
.unwrap(),
);
let pop_token = ctx.push_stack(Ancestor::TSConditionalTypeCheckType(
ancestor::TSConditionalTypeWithoutCheckType(node, PhantomData),
));
Expand All @@ -3979,6 +3972,13 @@ pub(crate) unsafe fn walk_ts_conditional_type<'a, Tr: Traverse<'a>>(
(node as *mut u8).add(ancestor::OFFSET_TS_CONDITIONAL_TYPE_CHECK_TYPE) as *mut TSType,
ctx,
);
let previous_scope_id = ctx.current_scope_id();
ctx.set_current_scope_id(
(*((node as *mut u8).add(ancestor::OFFSET_TS_CONDITIONAL_TYPE_SCOPE_ID)
as *mut Cell<Option<ScopeId>>))
.get()
.unwrap(),
);
ctx.retag_stack(AncestorType::TSConditionalTypeExtendsType);
walk_ts_type(
traverser,
Expand All @@ -3991,14 +3991,14 @@ pub(crate) unsafe fn walk_ts_conditional_type<'a, Tr: Traverse<'a>>(
(node as *mut u8).add(ancestor::OFFSET_TS_CONDITIONAL_TYPE_TRUE_TYPE) as *mut TSType,
ctx,
);
ctx.set_current_scope_id(previous_scope_id);
ctx.retag_stack(AncestorType::TSConditionalTypeFalseType);
walk_ts_type(
traverser,
(node as *mut u8).add(ancestor::OFFSET_TS_CONDITIONAL_TYPE_FALSE_TYPE) as *mut TSType,
ctx,
);
ctx.pop_stack(pop_token);
ctx.set_current_scope_id(previous_scope_id);
traverser.exit_ts_conditional_type(&mut *node, ctx);
}

Expand Down
12 changes: 6 additions & 6 deletions tasks/coverage/snapshots/semantic_typescript.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3897,13 +3897,13 @@ Bindings mismatch:
after transform: ScopeId(2): ["P", "attrs"]
rebuilt : ScopeId(1): ["attrs"]
Scope children mismatch:
after transform: ScopeId(2): [ScopeId(3)]
after transform: ScopeId(2): [ScopeId(3), ScopeId(5)]
rebuilt : ScopeId(1): []
Bindings mismatch:
after transform: ScopeId(6): ["P", "attrs"]
rebuilt : ScopeId(2): ["attrs"]
Scope children mismatch:
after transform: ScopeId(6): [ScopeId(7)]
after transform: ScopeId(6): [ScopeId(7), ScopeId(8)]
rebuilt : ScopeId(2): []
Bindings mismatch:
after transform: ScopeId(9): ["P", "attrs"]
Expand Down Expand Up @@ -8852,7 +8852,7 @@ Bindings mismatch:
after transform: ScopeId(3): ["D", "clientDef"]
rebuilt : ScopeId(1): ["clientDef"]
Scope children mismatch:
after transform: ScopeId(3): [ScopeId(4)]
after transform: ScopeId(3): [ScopeId(4), ScopeId(5)]
rebuilt : ScopeId(1): []
Unresolved references mismatch:
after transform: ["Record"]
Expand Down Expand Up @@ -17164,7 +17164,7 @@ Scope children mismatch:
after transform: ScopeId(8): [ScopeId(9), ScopeId(10)]
rebuilt : ScopeId(2): []
Symbol reference IDs mismatch for "state":
after transform: SymbolId(8): [ReferenceId(11), ReferenceId(12), ReferenceId(13), ReferenceId(15)]
after transform: SymbolId(9): [ReferenceId(11), ReferenceId(12), ReferenceId(13), ReferenceId(15)]
rebuilt : SymbolId(1): [ReferenceId(0), ReferenceId(1), ReferenceId(2)]
Unresolved references mismatch:
after transform: ["State", "true"]
Expand Down Expand Up @@ -52205,10 +52205,10 @@ Scope children mismatch:
after transform: ScopeId(0): [ScopeId(1), ScopeId(4), ScopeId(5), ScopeId(6), ScopeId(7), ScopeId(10), ScopeId(11), ScopeId(12), ScopeId(13), ScopeId(16), ScopeId(17), ScopeId(18), ScopeId(19), ScopeId(22), ScopeId(23), ScopeId(24), ScopeId(25), ScopeId(28), ScopeId(29), ScopeId(30), ScopeId(31), ScopeId(34), ScopeId(35), ScopeId(36), ScopeId(37), ScopeId(40), ScopeId(41), ScopeId(42), ScopeId(43), ScopeId(44), ScopeId(47), ScopeId(48), ScopeId(49), ScopeId(50), ScopeId(51), ScopeId(54), ScopeId(55), ScopeId(56), ScopeId(57), ScopeId(58), ScopeId(61), ScopeId(63), ScopeId(64), ScopeId(67), ScopeId(69), ScopeId(71), ScopeId(73), ScopeId(77), ScopeId(80), ScopeId(84), ScopeId(87), ScopeId(89), ScopeId(90), ScopeId(91), ScopeId(92), ScopeId(95), ScopeId(96), ScopeId(98), ScopeId(99), ScopeId(100), ScopeId(101), ScopeId(102), ScopeId(103), ScopeId(107), ScopeId(108), ScopeId(109), ScopeId(110), ScopeId(111)]
rebuilt : ScopeId(0): [ScopeId(1), ScopeId(2)]
Reference symbol mismatch for "x1":
after transform: SymbolId(126) "x1"
after transform: SymbolId(127) "x1"
rebuilt : <None>
Reference symbol mismatch for "x2":
after transform: SymbolId(132) "x2"
after transform: SymbolId(133) "x2"
rebuilt : <None>
Unresolved references mismatch:
after transform: ["Promise"]
Expand Down