Giter VIP home page Giter VIP logo

stc's People

Contributors

acrylicshrimp avatar alicewriteswrongs avatar amareis avatar arturjs avatar awareness481 avatar bcheidemann avatar blacktoast avatar camc314 avatar carrotzrule123 avatar cmrigney avatar desdaemon avatar elilichtblau avatar ever0de avatar filipw01 avatar hwoongkang avatar hyf0 avatar hyp3rflow avatar infix avatar kdy1 avatar kharvd avatar ko3yb avatar littledivy avatar mrkldshv avatar nissy-dev avatar padorang684 avatar rottencandy avatar simonbuchan avatar sunrabbit123 avatar togami2864 avatar yamgent avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stc's Issues

Use `Iterable` instead of `Iterator` for generator functions

interface BadGenerator extends Iterator<number>, Iterable<string> { }
export function* g3(): BadGenerator { }

This should fail because a generator function uses the item from Iterable as yield type.
i.e., g3 is inferred to emit a string as an item, and the inferred generator (Generator<number, any, undefined>) is assigned to BadGenerator.

It should file with

tests/tsc/types/builtin/generator/bad/1.ts:2:24 - error TS2322: Type 'Generator<string, any, undefined>' is not assignable to type 'BadGenerator'.
  The types returned by 'next(...)' are incompatible between these types.
    Type 'IteratorResult<string, any>' is not assignable to type 'IteratorResult<number, any>'.
      Type 'IteratorYieldResult<string>' is not assignable to type 'IteratorResult<number, any>'.
        Type 'IteratorYieldResult<string>' is not assignable to type 'IteratorYieldResult<number>'.
          Type 'string' is not assignable to type 'number'.

But currently, stc fails to emit an error because stc uses the Iterator interface instead of the Iterable interface while inferring the yield type.

if let Some(declared) = self.scope.declared_return_type().cloned() {
// TODO(kdy1): Change this to `get_iterable_element_type`
if let Ok(el_ty) = self.get_iterator_element_type(span, Cow::Owned(declared), true, Default::default()) {
types.push(el_ty.into_owned());
}
}

Mark nested modules as valid LHS

M2.M3 = { x: 3 }; // OK
(M2).M3 = { x: 3 }; // OK
(M2.M3) = { x: 3 }; // OK

The lines above should not report an error, but stc currently emits
image

The cause is

match rhs {
// TODO(kdy1): Use unique id for module type.
Type::Module(rhs) => {
if to.name.eq_ignore_span(&rhs.name) {
return Ok(());
}
}
_ => {}
}
dbg!();
return Err(Error::InvalidLValue { span: to.span() });

and it should be fixed to allow assignment to nested modules

perf: Handle type arguments smartly

Currently, we need to instantiate the full interface to infer the type of parameter.
This is too inefficient, and it can be done by

  • Attach id to type parameters and store type parameter information in the Scope.

  • Store type arguments in types like Interface, Class, ..etc

The first option seems better.

Change error code for missing `new` for classes

module Tools {
export class NullLogger { }
}
var logger = Tools.NullLogger();

The code above should fail with TS2348, but currently it failes with TS2349

image

It has the context of tried to calculate return type, which is used at

return Err(if kind == ExtractKind::Call {
Error::NoCallSignature { span, callee: box callee }.context("tried to calculate return type")
} else {
Error::NoNewSignature { span, callee: box callee }.context("tried to calculate return type")
});

But error is NoCallablePropertyWithName, which is TS2349, and

image


So we should modify

Error::NoNewSignature { span, .. } => Error::NoCallablePropertyWithName {
span,
obj: box obj_type.clone(),
key: box prop.clone(),
},

to use different error variant.

Handle variance correctly

There's some logic to assign parameters in a reverse direction, but I'm not sure when it's applied.

let reverse = !opts.for_overload
&& match (l_ty.normalize_instance(), r_ty.normalize_instance()) {
(Type::Union(..), Type::Union(..)) => false,
(Type::Function(..), Type::Function(..)) => false,
(
Type::Function(..) | Type::Constructor(..) | Type::Class(..),
Type::TypeLit(..) | Type::Interface(..),
) => false,
(_, Type::Union(..)) => true,
_ => true,
};
let res = if reverse {
self.assign_with_opts(data, AssignOpts { ..opts }, &r_ty, &l_ty)
.context("tried to assign the type of a parameter to another (reversed)")
} else {
self.assign_with_opts(data, AssignOpts { ..opts }, &l_ty, &r_ty)
.context("tried to assign the type of a parameter to another")
};

I think the current logic is wrong, but not sure as I don't know enough about variance.
To be exact, I understood variance itself, but I'm not sure which variance I should use for an assignment in form of LHS = RHS as there are two types, and therefore two variances.

Handle static properties properly

We should remove ClassInstance and add ClassDef type.
ClassDef is used for accessing static properties and Class is used for accessing non-static properties.

Make it work for `express`

stc_ts_file_analyzer:

  • find_imported_var: Support namespace imports.

stc_ts_type_checker:

  • Process all typings before processing user-specified files. This is required to handle modules created using declare module 'http' or declare namespace NodeJs.

Don't report an error on array pattern elements with a default value

This should not error, but currently, stc emits TupleIndexError

image


We should recover from the error based on the variant at

let elem_ty = ty
.as_ref()
.try_map(|ty| -> VResult<_> {
Ok(self
.get_element_from_iterator(span, Cow::Borrowed(&ty), idx)
.with_context(|| {
format!(
"tried to get the type of {}th element from iterator to declare vars with an array pattern",
idx
)
})?
.into_owned())
})?
.freezed();

Default value of an object pattern property should not change type if it's assignable

export interface BarProps {
barProp?: string;
}
export interface FooProps {
fooProps?: BarProps & object;
}
declare const foo: FooProps;
const { fooProps = {} } = foo;
fooProps.barProp;

This should success.

image

access_property of ((BarProps#1 & object) | {}) is expected to fail, because {} does not have the property.
But the type of fooProps should be (BarProps#1 & object) (without union)

I think the logic at

if prop.value.is_some() {
prop_ty = prop_ty.map(|ty| {
// Optional
ty.map(|ty| {
Type::new_union(
span,
vec![
ty,
Type::Keyword(KeywordType {
span,
kind: TsKeywordTypeKind::TsUndefinedKeyword,
metadata: Default::default(),
}),
],
)
})
});
}
is problematic, and I think we should adjust it to ignore default value if the type of the default value is assignable to the original type.

Cleanup + Refactor

  • ValidationResult => VResult
  • derive(Is) of Type should be replaced with a smarter implementation, which is aware of normalization.
  • Type#normalize should be renamed

Append `undefined` to type list on optional member access

declare const o6: <T>() => undefined | ({ x: number });
o6<number>()?.["x"];

The type of o6<number>()?.["x"]; above should be number | undefined, but currently it's number.

Relevant code for optional chaining is

let prop = self.validate_key(&me.prop, me.computed)?;
let obj = me.obj.validate_with(self)?;
let is_obj_optional = self.is_obj_optional(&obj)?;
let obj = obj.remove_falsy();
let ctx = Ctx {
in_opt_chain: true,
..self.ctx
};
let ty = self
.with_ctx(ctx)
.access_property(span, &obj, &prop, TypeOfMode::RValue, IdCtx::Var, Default::default())
.context("tried to access property to validate an optional chaining expression")?;
//
if is_obj_optional {
let mut types = vec![Type::undefined(span, Default::default()), ty];
types.dedup_type();
Ok(Type::union(types))
} else {
Ok(ty)
}

ts/checker: tsc test keeps failing with compiling `testing_macros` error

After updating testing_macros upto ^0.2.0, this error will be resolved.
Currently we are using v0.1.2 instead which seems to be failed to compile.

   Compiling testing_macros v0.1.2
error[E0432]: unresolved import `proc_macro2::SourceFile`
 --> /Users/flow/.cargo/registry/src/github.com-1ecc6299db9ec823/testing_macros-0.1.2/src/fixture.rs:6:19
  |
6 | use proc_macro2::{SourceFile, Span};
  |                   ^^^^^^^^^^ no `SourceFile` in the root

error[E0599]: no method named `source_file` found for struct `proc_macro2::Span` in the current scope
  --> /Users/flow/.cargo/registry/src/github.com-1ecc6299db9ec823/testing_macros-0.1.2/src/lib.rs:58:34
   |
58 |     let file = Span::call_site().source_file();
   |                                  ^^^^^^^^^^^ method not found in `proc_macro2::Span`

Some errors have detailed explanations: E0432, E0599.
For more information about an error, try `rustc --explain E0432`.
error: could not compile `testing_macros` due to 2 previous errors

Split file analyzer

Compile time is delaying development.

Crates

traits

  • Defines ExpandRef
  • Defines Assign
  • Defines InferType

assignments

  • Depends on traits.

Handles assignments

generics

Handles type inference.

Caching

Module graph should return RModule along with Arc<SourceMap>, Arc<SourceFile>.
It will allow using types from cache without degrading span.

Metadata system

All types should have the same struct.
This is required to preserve current semantics.

And it should have fields for each type.

pub struct Metadata {
    pub type_lit: TypeLitMetadata,
}

Class Id

derivedClassTransitivity.ts:

// subclassing is not transitive when you can remove required parameters and add optional parameters

class C {
    foo(x: number) { }
}

class D extends C {
    foo() { } // ok to drop parameters
}

class E extends D {
    foo(x?: string) { } // ok to add optional parameters
}

var c: C;
var d: D;
var e: E;
c = e;
var r = c.foo(1);
var r2 = e.foo('');

We need logic to compare private properties.

We should store the id of declaring class in properties, because properties from multiple class can be mixed.

Regression of binary size

Size regressed from 8MB to 12MB due to dudykr/stc-archive#31.
This is because of Visit being shared between types and ast nodes.

This can be fixed by macros like noop_{visit, visit_mut, fold}_{ast, type}.
As this is not critical for development, I merged the pr

CowArc<T>

CowArcVec would be useful

BoxedCowArc is boxed variant

type TypePtr = BoxedCowArc

Handle assignment of tuples with rest

I postponed it from #170 because I have another item to work on

Basically,

//@strict: true

type ArgsUnion = [number, string] | [number, Error];
type TupleUnionFunc = (...params: ArgsUnion) => number;

const funcUnionTupleRest: TupleUnionFunc = (...params) => {
  const [num, strOrErr] = params;
  return num;
};


export { }

should work.

It can be done by fixing code at

// TODO: Handle Type::Rest
if elems.len() < rhs_elems.len() {
return Err(Error::AssignFailedBecauseTupleLengthDiffers { span });
}
// TODO: Handle Type::Rest
if elems.len() > rhs_elems.len() {
return Err(Error::AssignFailedBecauseTupleLengthDiffers { span });
}
to handle an element with rest type specially.

Split env

I made a mistake while working on #15. I forgot splitting env.

Change error code for using `module` as LHS

This line should fail with TS2708, but it fails with TS2539.

image

The problematic code is at

ty_of_left = analyzer
.type_of_var(i, TypeOfMode::LValue, None)
.context("tried to get type of lhs of an assignment")
.map(|ty| {
mark_var_as_truthy = true;
ty
})
.map_err(|v| {
match v.actual() {
Error::CannotAssignToNonVariable { .. } => {
skip_right = true;
}
_ => {}
}
v
})
.report(&mut analyzer.storage);

and we should use Errorr::convert_err to adjust the error variant.

macro: Dummy debug

We need a debug impl which is replaced with dummy impl on release build.

Correct hanlding of recursive type parameters

Currently, type parameters are incorrectly stored, but when I tried storing them recursively, it resulted in a stack overflow.

https://github.com/swc-project/stc/blob/76e1a7d0c48ea2c9e78e97fba4f32187d31cd625/typescript/file_analyzer/src/analyzer/convert/mod.rs#L120-L131

I tried preventing stack overflow, but the 'validity' of type can't be ignored. Especially, Fixer cannot give up fixing types, because it makes validity assertion of a type fail.

Solutions

Solution 1: Allow invalid state

Allowing the constraints of type parameters to be invalid and fixing them each time contraint is used can be a solution, but it does not seem like a good solution.

Solution 2: Use Type::Ref for constraints.

This would make code dirty, but it would be easy to handle infinite recursion because many methods have their own logic to prevent infinite recursions. (fix() does not have way to prevent recursion, and it's the problem)

Usage

Just wondering how to use stc, at its current state.
Do I need to consume it as a library or run it through a test shim?

I cloned the repository, updated the dependencies through yarn and compiled the project with rustc build.
Alas, the resulting stc binary only contains two subcommands (help and iterate) and simply providing a TypeScript source file simply returns a Found argument '<pathToTSFile>' which wasn't expected, or isn't valid in this context

Improve performance

  • infer_type: Fast path for Type::Mapped with Type::Array.
  • normalize: Partial expansion.
  • fix: Make it shallow.
  • fix: Reduce call.
  • normalize: Cache(?).

expr/call_new.rs

  • Skip reevaluation if there's no argument requires reevaluation.

loops.rs

  • Use result from first run if no type fact is generated.

Handle enums while reducing intersections

const enum Tag1 { }
const enum Tag2 { }
declare let s1: string & Tag1;
declare let s2: string & Tag2;
s1 = s2;
s2 = s1;

const enum Tag1 { }
const enum Tag2 { }
declare let t1: string & Tag1 | undefined;
declare let t2: string & Tag2 | undefined;
t1 = t2;
t2 = t1;

In the files above, the assignment should succeed because Tag1 and Tag2 are enum without an initializer, and enums without initializers are numbers while reducing.

This can be done by checking for enums in

pub(crate) fn normalize_intersection_types(&mut self, span: Span, types: &[Type], opts: NormalizeTypeOpts) -> VResult<Option<Type>> {
macro_rules! never {
() => {{
Ok(Some(Type::Keyword(KeywordType {
span,
kind: TsKeywordTypeKind::TsNeverKeyword,
metadata: KeywordTypeMetadata { ..Default::default() },
})))
}};
}
let is_str = types.iter().any(|ty| ty.is_str());
let is_num = types.iter().any(|ty| ty.is_num());
let is_bool = types.iter().any(|ty| ty.is_bool());
if u32::from(is_str) + u32::from(is_num) + u32::from(is_bool) >= 2 {
return never!();
}
if !self.rule().always_strict && types.len() == 2 {
let (a, b) = (&types[0], &types[1]);
if (a.is_str_lit() && b.is_str_lit() || (a.is_num_lit() && b.is_num_lit()) || (a.is_bool_lit() && b.is_bool_lit()))
&& !a.type_eq(&b)
{
return never!();
}
}

perf: AType

pub struct AType {
    arc: Either<Type, Arc<Type>>,
}

AType.normalize() returns type and Arc<Type> is used while cloning.

Rest patterns should get the correct type

This line should not error, but stc fails to validate it because stc thinks the type of a1 is string[] instead of [string, boolean].

I think the problematic code is

RPat::Rest(elem) => {
// Rest element is special.
let type_for_rest_arg = match ty {
Some(ty) => self
.get_rest_elements(Some(span), Cow::Owned(ty), idx)
.context(
"tried to get lefting elements of an iterator to declare variables using a rest \
pattern",
)
.map(Cow::into_owned)
.report(&mut self.storage),
None => None,
}
.freezed();
let default = match default {
Some(ty) => self
.get_rest_elements(Some(span), Cow::Owned(ty), idx)
.context(
"tried to get lefting elements of an iterator to declare variables using a rest \
pattern",
)
.map(Cow::into_owned)
.report(&mut self.storage),
None => None,
}
.freezed();
self.add_vars(&elem.arg, type_for_rest_arg, None, default, opts)
.context("tried to declare lefting elements to the arugment of a rest pattern")
.report(&mut self.storage);
break;
}
_ => {}
}
, which means get_rest_elements is returning a wrong type for the input above.

Store type flags in `Type` instead of span.

Span is too implicit and it causes some bugs.

Required metadata

All metadata will be implemented as a field metadata and all metadata structs must always return true for TypeEq and EqIgnoreSpan.

Type::Keyword:

  • Implicit.

Type::TypeLit:

  • is_exact: Used for assignemnts. If is_exact is false, unhandled rhs will be ignored.

Relicense at some day

I think AGPL 3.0 will not work for many vendors, but I don't want to change the license at now.

(Actually, I rebased the whole repo to add the AGPL License file to the first commit)

Allow assignment of tuples with different length if it's `undefined` or `any`

(function ([f, g = f, h = i, i = f]) { // error for h = i
})([1])

This line should not report an argumentnt error because fields are optional.

image

The type of arguments are correctly inferred as [any, any, any, any] but we should allow assignment of [1] to [any, any, any, any].

Type::Tuple(Tuple { elems: ref rhs_elems, .. }) => {
// TODO: Handle Type::Rest
if elems.len() < rhs_elems.len() {
return Err(Error::AssignFailedBecauseTupleLengthDiffers { span });
}
// TODO: Handle Type::Rest
if elems.len() > rhs_elems.len() {
return Err(Error::AssignFailedBecauseTupleLengthDiffers { span });
}

The assignment logic for tuple should be aware of any or undefined and allow the length to mismatch for these cases.

Language server

Features

  • Autocompletion in

    • Class name

      • from file name.
    • Super class

    • Types

It can be inferred from usages. (including methods, as stc is fast)

  • Generate function when a function is not defined.

  • Generate method when a method is not defined.

  • import boss: Block @material-ui/core from being imported.

  • auto-recompile for kind of shared modules (path dependencies).

  • Run directly from editor.
    -[ ] Run tests from editor.

  • Actual type information in generic on hover.

  • Type inference issue

Rest pattern should not trigger `TupleIndexErrror`

The line above should not error because LHS is a rest pattern.
image

But currently stc fails with an error, because of the code below

let elem_ty = ty
.as_ref()
.try_map(|ty| -> VResult<_> {
Ok(self
.get_element_from_iterator(span, Cow::Borrowed(&ty), idx)
.with_context(|| {
format!(
"tried to get the type of {}th element from iterator to declare vars with an array pattern",
idx
)
})?
.into_owned())
})?
.freezed();

This is quite similar to #182, though.

Improve TypeFacts

Currently there's no clear way to distinguish typeof a === 'string' || typeof a === ' number' and typeof a === 'string' && typeof a === ' number'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.