Skip to content

Commit

Permalink
tsparser: support qualified name lookups
Browse files Browse the repository at this point in the history
  • Loading branch information
eandre committed Nov 13, 2024
1 parent 904c639 commit 2e6580b
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
source: tsparser/src/parser/types/tests.rs
expression: result
input_file: tsparser/src/parser/types/testdata/qualified_name.ts
---
{
"T1": Basic(
String,
),
"T2": Basic(
Number,
),
}
11 changes: 11 additions & 0 deletions tsparser/src/parser/types/testdata/qualified_name.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Foo {
export type Bar = string;

export namespace Nested {
export type Baz = number;
}
};


export type T1 = Foo.Bar;
export type T2 = Foo.Nested.Baz;
143 changes: 104 additions & 39 deletions tsparser/src/parser/types/type_resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use crate::parser::module_loader::ModuleId;
use crate::parser::types::object::{CheckState, ObjectKind, ResolveState, TypeNameDecl};
use crate::parser::types::Object;
use crate::parser::{module_loader, Range};
use crate::span_err::ErrReporter;

use super::resolved::{Resolved, Resolved::*};
use super::typ::*;
Expand Down Expand Up @@ -526,46 +527,49 @@ impl<'a> Ctx<'a> {
}

fn type_ref(&self, typ: &ast::TsTypeRef) -> Type {
let ident: &ast::Ident = match &typ.type_name {
ast::TsEntityName::Ident(i) => i,
ast::TsEntityName::TsQualifiedName(_qn) => {
HANDLER
.with(|handler| handler.span_err(typ.span, "qualified name not yet supported"));
return Type::Basic(Basic::Never);
}
};
let obj = match &typ.type_name {
ast::TsEntityName::Ident(ident) => {
// Is this a reference to a type parameter?
let type_param = self
.type_params
.iter()
.enumerate()
.find(|tp| tp.1.name.to_id() == ident.to_id())
.map(|tp| (tp.0, *tp.1));
if let Some((idx, type_param)) = type_param {
return if let Some(type_arg) = self.type_args.get(idx) {
type_arg.clone()
} else {
let constraint = type_param.constraint.as_ref().map(|c| self.btyp(c));
Type::Generic(Generic::TypeParam(TypeParam { idx, constraint }))
};
}

// Is this a reference to a type parameter?
let type_param = self
.type_params
.iter()
.enumerate()
.find(|tp| tp.1.name.to_id() == ident.to_id())
.map(|tp| (tp.0, *tp.1));
if let Some((idx, type_param)) = type_param {
return if let Some(type_arg) = self.type_args.get(idx) {
type_arg.clone()
} else {
let constraint = type_param.constraint.as_ref().map(|c| self.btyp(c));
Type::Generic(Generic::TypeParam(TypeParam { idx, constraint }))
};
}
// Otherwise, is this a reference to the current mapped 'key' type?
if let Some(mapped_type_ctx) = &self.mapped_key_id {
if ident.to_id() == *mapped_type_ctx {
// Do we have a mapped key type?
return if let Some(mapped_key_type) = self.mapped_key_type {
mapped_key_type.clone()
} else {
Type::Generic(Generic::MappedKeyType)
};
}
}

// Otherwise, is this a reference to the current mapped 'key' type?
if let Some(mapped_type_ctx) = &self.mapped_key_id {
if ident.to_id() == *mapped_type_ctx {
// Do we have a mapped key type?
return if let Some(mapped_key_type) = self.mapped_key_type {
mapped_key_type.clone()
} else {
Type::Generic(Generic::MappedKeyType)
let Some(obj) = self.ident_obj(ident) else {
HANDLER.with(|handler| handler.span_err(ident.span, "unknown identifier"));
return Type::Basic(Basic::Never);
};
obj
}
ast::TsEntityName::TsQualifiedName(qn) => {
let Some(obj) = self.qualified_name(qn) else {
HANDLER.with(|handler| handler.span_err(qn.span(), "unknown qualified name"));
return Type::Basic(Basic::Never);
};
obj
}
}

let Some(obj) = self.ident_obj(ident) else {
HANDLER.with(|handler| handler.span_err(ident.span, "unknown identifier"));
return Type::Basic(Basic::Never);
};

// Is this a reference to the built-in 'Date' class?
Expand Down Expand Up @@ -597,20 +601,81 @@ impl<'a> Ctx<'a> {
Type::Named(Named::new(obj, type_arguments))
}
ObjectKind::Var(_) | ObjectKind::Using(_) | ObjectKind::Func(_) => {
HANDLER.with(|handler| handler.span_err(ident.span, "value used as type"));
HANDLER.with(|handler| handler.span_err(typ.span, "value used as type"));
Type::Basic(Basic::Never)
}
ObjectKind::Module(_) => {
HANDLER.with(|handler| handler.span_err(ident.span, "module used as type"));
HANDLER.with(|handler| handler.span_err(typ.span, "module used as type"));
Type::Basic(Basic::Never)
}
ObjectKind::Namespace(_) => {
HANDLER.with(|handler| handler.span_err(ident.span, "namespace used as type"));
HANDLER.with(|handler| handler.span_err(typ.span, "namespace used as type"));
Type::Basic(Basic::Never)
}
}
}

fn qualified_name(&self, qn: &ast::TsQualifiedName) -> Option<Rc<Object>> {
let obj = match &qn.left {
ast::TsEntityName::Ident(ident) => self.ident_obj(ident)?,
ast::TsEntityName::TsQualifiedName(qn) => self.qualified_name(qn)?,
};

let name = qn.right.sym.as_str();
match &obj.kind {
ObjectKind::TypeName(_) => {
qn.right
.span
.err("cannot yet resolve qualified name on type");
None
}
ObjectKind::Enum(_) => {
qn.right
.span
.err("cannot yet resolve qualified name on enum");
None
}
ObjectKind::Var(_) => {
qn.right
.span
.err("cannot yet resolve qualified name on variable");
None
}
ObjectKind::Using(_) => {
qn.right
.span
.err("cannot yet resolve qualified name on using");
None
}
ObjectKind::Func(_) => {
qn.right
.span
.err("cannot yet resolve qualified name on function");
None
}
ObjectKind::Class(_) => {
qn.right
.span
.err("cannot yet resolve qualified name on class");
None
}
ObjectKind::Module(module) => {
if name == "default" {
module.data.default_export.clone()
} else {
module.data.named_exports.get(name).cloned()
}
}
ObjectKind::Namespace(ns) => {
if name == "default" {
ns.data.default_export.clone()
} else {
ns.data.named_exports.get(name).cloned()
}
}
}
}

fn array(&self, tt: &ast::TsArrayType) -> Type {
Type::Array(Box::new(self.typ(&tt.elem_type)))
}
Expand Down

0 comments on commit 2e6580b

Please sign in to comment.