pub struct TypeScriptOracle<'t> { /* private fields */ }Expand description
A type oracle for one parsed TypeScript file.
Implementations§
Source§impl<'t> TypeScriptOracle<'t>
impl<'t> TypeScriptOracle<'t>
Sourcepub fn new(support: &TypeScriptSupport, tree: &'t Tree, source: &'t str) -> Self
pub fn new(support: &TypeScriptSupport, tree: &'t Tree, source: &'t str) -> Self
Build an oracle for one parsed file.
Cheap by construction: everything expensive happened in TypeScriptSupport::probe.
That is what lets a caller build one of these per query rather than per run.
Sourcepub fn with_imports(
self,
file: &'t FilePath,
imports: &'t dyn ImportResolution,
) -> Self
pub fn with_imports( self, file: &'t FilePath, imports: &'t dyn ImportResolution, ) -> Self
Let this oracle follow a name into the file that declares it.
Without it every arm behaves exactly as it did before cross-file resolution existed — an import is a name with a module and no type — which is what makes a within-file oracle still a thing this crate can hand out.
Sourcepub fn with_exhaustion(self, exhausted: &'t Cell<bool>) -> Self
pub fn with_exhaustion(self, exhausted: &'t Cell<bool>) -> Self
Let this oracle report that its depth bound — rather than the program — is why it answered nothing.
For a caller that threads a depth it has already spent and memoizes what comes back.
A bare None cannot say this on its own — it is what the bound and an untypeable
node both answer — and the bound is checked several frames below any public method, so
the flag is the channel rather than a return type.
Sourcepub fn type_of_from(&self, node: Node<'t>, depth: u32) -> Option<Type>
pub fn type_of_from(&self, node: Node<'t>, depth: u32) -> Option<Type>
The type of node, starting from a depth already spent.
For a provider that has followed an import: the recursion crosses files, and a bound reset at every boundary is not a bound at all.
Sourcepub fn declaration_type_from(
&self,
declaration: Node<'t>,
depth: u32,
) -> Option<Type>
pub fn declaration_type_from( &self, declaration: Node<'t>, depth: u32, ) -> Option<Type>
The type a declaration gives the name it declares, from a depth already spent.
Sourcepub fn annotation_type_from(
&self,
annotation: Node<'t>,
depth: u32,
) -> Option<Type>
pub fn annotation_type_from( &self, annotation: Node<'t>, depth: u32, ) -> Option<Type>
The type a type_annotation (or a bare type node) denotes, from a depth already spent.
Steps through the type_annotation wrapper, then types the node in type position —
number comes back as Primitive::Number, not a nominal named number, which is
what separates it from Self::type_named_by. For the provider, typing a member’s
declared type in the file that declares the member.
Sourcepub fn return_type_from(&self, node: Node<'t>, depth: u32) -> Option<Type>
pub fn return_type_from(&self, node: Node<'t>, depth: u32) -> Option<Type>
The return type of node, from a depth already spent. See Self::type_of_from.
Sourcepub fn type_named_by(&self, node: Node<'t>) -> Option<Type>
pub fn type_named_by(&self, node: Node<'t>) -> Option<Type>
The type a name in type position denotes: an alias followed, a nominal otherwise.
Self::type_of cannot stand in for it. In expression position an identifier is a
value, so class A extends B {}’s B would be typed as whatever value B holds —
which for a class declaration is nothing at all — rather than as the type it names.
Sourcepub fn type_of(&self, node: Node<'t>) -> Option<Type>
pub fn type_of(&self, node: Node<'t>) -> Option<Type>
The type of the expression at node, or None when the oracle cannot be sure.
None is an answer rather than a failure. A rule that stays silent on it reports
only what was established, which is the posture every rule built on this oracle is
expected to take.
Sourcepub fn symbol_of(&self, node: Node<'t>) -> Option<Symbol>
pub fn symbol_of(&self, node: Node<'t>) -> Option<Symbol>
Where the name at node came from, or None if nothing in this file declares it.
Distinct from Self::type_of and useful where that returns nothing: an imported
value has no type this oracle can read, and still has a name and a module — which is
exactly what a rule distinguishing one library’s Decimal from a local class needs.
Answers in type position as well as expression position, because the resolver does.
Sourcepub fn return_type_of(&self, node: Node<'t>) -> Option<Type>
pub fn return_type_of(&self, node: Node<'t>) -> Option<Type>
What calling the function at node yields.
Separate from Self::type_of rather than folded into it, and the reason is the
vocabulary rather than the plumbing: a function declaration is not an expression, and
giving type_of a signature type would mean a Type::Function variant every rule
asking a simpler question would then have to unpack. There is exactly one question
rules ask about a function, so there is exactly one method.
Accepts a call expression (whose callee is resolved), a function-like declaration, or an identifier bound to one.
A generic call whose signature returns a bare type parameter answers None, not the
type the call site would instantiate it to: useMemo(() => 0n, []), whose signature
returns T, is None here rather than bigint. Instantiating a parameter from an
argument’s type is inference this oracle does not do — the tsc provider does it and
answers bigint. The honest label for what this one cannot see is the same None every
unread thing gets, never a nominal named T: a rule may branch on None, whereas a
text-only T would be a non-undefined answer carrying no field to branch on. The
type parameter itself is dropped by Self::type_named_by for the same reason.
Sourcepub fn annotated_type_node(&self, expr: Node<'t>) -> Option<(Node<'t>, bool)>
pub fn annotated_type_node(&self, expr: Node<'t>) -> Option<(Node<'t>, bool)>
The type node a bound name is annotated with, and whether that binding is nullable.
A binding with no annotation gives nothing — this milestone does not infer a variable’s type from its initializer for the purpose of a member read. Exposed for the provider, which resolves the base of a cross-file chain in the asking file before folding the rest.
Trait Implementations§
Source§impl Debug for TypeScriptOracle<'_>
Hand-written because Arc<dyn BindingResolver> is not Debug — the trait answers
identifier questions, not requests to describe itself, and requiring every implementor
to add one for the sake of this impl is not worth it. The same reasoning, and the same
fix, as LanguageRegistry in lanekeep-lang.
impl Debug for TypeScriptOracle<'_>
Hand-written because Arc<dyn BindingResolver> is not Debug — the trait answers
identifier questions, not requests to describe itself, and requiring every implementor
to add one for the sake of this impl is not worth it. The same reasoning, and the same
fix, as LanguageRegistry in lanekeep-lang.
tree has no such problem — Tree’s own Debug delegates to the root Node’s,
which prints one line (measured: {Tree {Node program (0, 0) - (0, 12)}}) rather than
the whole parse tree, so it costs nothing to include.
source is the one field deliberately summarized rather than printed. It is a whole
file, and a Debug that puts a file into every line it appears in is not one anybody
can read; its length identifies which file this is for as well as the bytes would.
Same call as LanguageRegistry, which prints its keys and not the languages behind
them.