Skip to content
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
31 changes: 14 additions & 17 deletions crates/oxc_semantic/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -479,25 +479,22 @@ impl<'a> SemanticBuilder<'a> {
let flags = reference.flags();
if flags.is_type() && symbol_flags.can_be_referenced_by_type()
|| flags.is_value() && symbol_flags.can_be_referenced_by_value()
|| flags.is_ts_type_query() && symbol_flags.is_import()
|| flags.is_value_as_type()
&& (symbol_flags.can_be_referenced_by_value()
|| symbol_flags.is_type_import())
{
// The non type-only ExportSpecifier can reference a type/value symbol,
// If the symbol is a value symbol and reference flag is not type-only, remove the type flag.
if symbol_flags.is_value() && !flags.is_type_only() {
if flags.is_value_as_type() {
// Resolve pending type references (e.g., from `typeof` expressions) to proper type references.
*reference.flags_mut() = ReferenceFlags::Type;
} else if symbol_flags.is_value() && !flags.is_type_only() {
// The non type-only ExportSpecifier can reference a type/value symbol,
// If the symbol is a value symbol and reference flag is not type-only, remove the type flag.
*reference.flags_mut() -= ReferenceFlags::Type;
} else {
// If the symbol is a type symbol and reference flag is not type-only, remove the value flag.
*reference.flags_mut() -= ReferenceFlags::Value;
}

// import type { T } from './mod'; type A = typeof T
// ^ can reference type-only import
// If symbol is type-import, we need to replace the ReferenceFlags::Value with ReferenceFlags::Type
if flags.is_ts_type_query() && symbol_flags.is_type_import() {
*reference.flags_mut() -= ReferenceFlags::Value;
*reference.flags_mut() |= ReferenceFlags::Type;
}

reference.set_symbol_id(symbol_id);
resolved_references.push(reference_id);
false
Expand Down Expand Up @@ -1834,19 +1831,18 @@ impl<'a> SemanticBuilder<'a> {
if signature.key.is_expression() {
// interface A { [prop]: string }
// ^^^^^ The property can reference value or [`SymbolFlags::TypeImport`] symbol
self.current_reference_flags =
ReferenceFlags::Read | ReferenceFlags::TSTypeQuery; // TODO: Should use another flag
self.current_reference_flags = ReferenceFlags::ValueAsType;
}
}
AstKind::TSTypeQuery(_) => {
// type A = typeof a;
// ^^^^^^^^
self.current_reference_flags = ReferenceFlags::Read | ReferenceFlags::TSTypeQuery;
self.current_reference_flags = ReferenceFlags::ValueAsType;
}
AstKind::TSTypeParameterInstantiation(_) => {
// type A<T> = typeof a<T>;
// ^^^ avoid treat T as a value and TSTypeQuery
self.current_reference_flags -= ReferenceFlags::Read | ReferenceFlags::TSTypeQuery;
self.current_reference_flags -= ReferenceFlags::ValueAsType;
}
AstKind::TSTypeName(_) => {
match self.nodes.parent_kind(self.current_node_id) {
Expand All @@ -1862,7 +1858,8 @@ impl<'a> SemanticBuilder<'a> {
// ^^^ Keep the current reference flag
}
_ => {
if !self.current_reference_flags.is_ts_type_query() {
// Handled in `AstKind::PropertySignature` or `AstKind::TSTypeQuery`
if !self.current_reference_flags.is_value_as_type() {
self.current_reference_flags = ReferenceFlags::Type;
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_semantic/src/reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,6 @@ impl Reference {
/// Returns `true` if this reference is used in a type context.
#[inline]
pub fn is_type(&self) -> bool {
self.flags.is_type() || self.flags.is_ts_type_query()
self.flags.is_type()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ input_file: crates/oxc_semantic/tests/fixtures/oxc/type-declarations/signatures/
"node": "ImportDefaultSpecifier",
"references": [
{
"flags": "ReferenceFlags(Type | TSTypeQuery)",
"flags": "ReferenceFlags(Type)",
"id": 0,
"name": "X",
"node_id": 15
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/class/declarati
"node_id": 8
},
{
"flags": "ReferenceFlags(Read | TSTypeQuery)",
"flags": "ReferenceFlags(Type)",
"id": 1,
"name": "A",
"node_id": 13
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/import/type-def
"node": "ImportDefaultSpecifier",
"references": [
{
"flags": "ReferenceFlags(Type | TSTypeQuery)",
"flags": "ReferenceFlags(Type)",
"id": 0,
"name": "foo",
"node_id": 10
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/import/type-inl
"node": "ImportSpecifier(foo)",
"references": [
{
"flags": "ReferenceFlags(Type | TSTypeQuery)",
"flags": "ReferenceFlags(Type)",
"id": 0,
"name": "foo",
"node_id": 11
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/import/type-nam
"node": "ImportSpecifier(foo)",
"references": [
{
"flags": "ReferenceFlags(Type | TSTypeQuery)",
"flags": "ReferenceFlags(Type)",
"id": 0,
"name": "foo",
"node_id": 11
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/instantiation-e
"node": "Function(makeBox)",
"references": [
{
"flags": "ReferenceFlags(Read | TSTypeQuery)",
"flags": "ReferenceFlags(Type)",
"id": 2,
"name": "makeBox",
"node_id": 27
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/type-declaratio
"node": "VariableDeclarator(arg)",
"references": [
{
"flags": "ReferenceFlags(Read | TSTypeQuery)",
"flags": "ReferenceFlags(Type)",
"id": 0,
"name": "arg",
"node_id": 15
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/type-declaratio
"node": "VariableDeclarator(k)",
"references": [
{
"flags": "ReferenceFlags(Read | TSTypeQuery)",
"flags": "ReferenceFlags(Type)",
"id": 1,
"name": "k",
"node_id": 21
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/type-declaratio
"node": "VariableDeclarator(x)",
"references": [
{
"flags": "ReferenceFlags(Read | TSTypeQuery)",
"flags": "ReferenceFlags(Type)",
"id": 0,
"name": "x",
"node_id": 14
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/type-declaratio
"node": "VariableDeclarator(x)",
"references": [
{
"flags": "ReferenceFlags(Read | TSTypeQuery)",
"flags": "ReferenceFlags(Type)",
"id": 0,
"name": "x",
"node_id": 21
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/type-declaratio
"node": "Function(foo)",
"references": [
{
"flags": "ReferenceFlags(Read | TSTypeQuery)",
"flags": "ReferenceFlags(Type)",
"id": 2,
"name": "foo",
"node_id": 29
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/type-declaratio
"node": "VariableDeclarator(x)",
"references": [
{
"flags": "ReferenceFlags(Read | TSTypeQuery)",
"flags": "ReferenceFlags(Type)",
"id": 0,
"name": "x",
"node_id": 9
Expand Down
38 changes: 17 additions & 21 deletions crates/oxc_syntax/src/reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,22 +51,17 @@ bitflags! {
/// There are three general categories of references:
/// 1. Values being referenced as values
/// 2. Types being referenced as types
/// 3. Values being referenced as types
/// 3. Values being used in type contexts
///
/// ## Values
/// Reading a value is indicated by [`Read`], writing a value
/// is indicated by [`Write`]. References can be both a read
/// and a write, such as in this scenario:
/// ## Value as Type
/// The [`ValueAsType`] flag is a temporary marker for references that need to
/// resolve to value symbols initially, but will ultimately be treated as type references.
/// This flag is crucial in scenarios like TypeScript's `typeof` operator.
///
/// ```js
/// let a = 1;
/// a++;
/// ```
///
/// When a value symbol is used as a type, such as in `typeof a`, it has
/// [`TSTypeQuery`] added to its flags. It is, however, still
/// considered a read. A good rule of thumb is that if a reference has [`Read`]
/// or [`Write`] in its flags, it is referencing a value symbol.
/// For example, in `type T = typeof a`:
/// 1. The reference to 'a' is initially flagged with [`ValueAsType`].
/// 2. This ensures that during symbol resolution, 'a' should be a value symbol.
/// 3. However, the final resolved reference's flags will be treated as a type.
///
/// ## Types
/// Type references are indicated by [`Type`]. These are used primarily in
Expand All @@ -75,7 +70,8 @@ bitflags! {
///
/// [`Read`]: ReferenceFlags::Read
/// [`Write`]: ReferenceFlags::Write
/// [`TSTypeQuery`]: ReferenceFlags::TSTypeQuery
/// [`Type`]: ReferenceFlags::Type
/// [`ValueAsType`]: ReferenceFlags::ValueAsType
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
pub struct ReferenceFlags: u8 {
Expand All @@ -84,10 +80,10 @@ bitflags! {
const Read = 1 << 0;
/// A symbol is being written to in a Value context.
const Write = 1 << 1;
// Used in type definitions.
/// Used in type definitions.
const Type = 1 << 2;
// Used in `typeof xx`
const TSTypeQuery = 1 << 3;
/// A value symbol is used in a type context, such as in `typeof` expressions.
const ValueAsType = 1 << 3;
/// The symbol being referenced is a value.
///
/// Note that this does not necessarily indicate the reference is used
Expand Down Expand Up @@ -144,10 +140,10 @@ impl ReferenceFlags {
self.contains(Self::Read | Self::Write)
}

/// The identifier is used in a type referenced
/// Checks if the reference is a value being used in a type context.
#[inline]
pub fn is_ts_type_query(&self) -> bool {
self.contains(Self::TSTypeQuery)
pub fn is_value_as_type(&self) -> bool {
self.contains(Self::ValueAsType)
}

/// The identifier is used in a type definition.
Expand Down