Skip to content

Commit a893968

Browse files
authored
fix(fast_check): infer literal types in const contexts (#555)
1 parent 7c4a101 commit a893968

File tree

6 files changed

+108
-60
lines changed

6 files changed

+108
-60
lines changed

src/fast_check/swc_helpers.rs

+34-23
Original file line numberDiff line numberDiff line change
@@ -179,28 +179,39 @@ pub fn ts_tuple_element(ts_type: TsType) -> TsTupleElement {
179179
}
180180
}
181181

182-
pub fn maybe_lit_to_ts_type_const(lit: &Lit) -> Option<TsType> {
183-
match lit {
184-
Lit::Str(lit_str) => Some(ts_lit_type(TsLit::Str(lit_str.clone()))),
185-
Lit::Bool(lit_bool) => Some(ts_lit_type(TsLit::Bool(*lit_bool))),
186-
Lit::Null(_) => Some(ts_keyword_type(TsKeywordTypeKind::TsNullKeyword)),
187-
Lit::Num(lit_num) => Some(ts_lit_type(TsLit::Number(lit_num.clone()))),
188-
Lit::BigInt(lit_bigint) => {
189-
Some(ts_lit_type(TsLit::BigInt(lit_bigint.clone())))
190-
}
191-
Lit::Regex(_) => Some(regex_type()),
192-
Lit::JSXText(_) => None,
193-
}
194-
}
195-
196-
pub fn maybe_lit_to_ts_type(lit: &Lit) -> Option<TsType> {
197-
match lit {
198-
Lit::Str(_) => Some(ts_keyword_type(TsKeywordTypeKind::TsStringKeyword)),
199-
Lit::Bool(_) => Some(ts_keyword_type(TsKeywordTypeKind::TsBooleanKeyword)),
200-
Lit::Null(_) => Some(ts_keyword_type(TsKeywordTypeKind::TsNullKeyword)),
201-
Lit::Num(_) => Some(ts_keyword_type(TsKeywordTypeKind::TsNumberKeyword)),
202-
Lit::BigInt(_) => Some(ts_keyword_type(TsKeywordTypeKind::TsBigIntKeyword)),
203-
Lit::Regex(_) => Some(regex_type()),
204-
Lit::JSXText(_) => None,
182+
pub enum DeclMutabilityKind {
183+
Const,
184+
Mutable,
185+
}
186+
187+
pub fn maybe_lit_to_ts_type(
188+
lit: &Lit,
189+
decl_kind: DeclMutabilityKind,
190+
) -> Option<TsType> {
191+
match decl_kind {
192+
DeclMutabilityKind::Const => match lit {
193+
Lit::Str(lit_str) => Some(ts_lit_type(TsLit::Str(lit_str.clone()))),
194+
Lit::Bool(lit_bool) => Some(ts_lit_type(TsLit::Bool(*lit_bool))),
195+
Lit::Null(_) => Some(ts_keyword_type(TsKeywordTypeKind::TsNullKeyword)),
196+
Lit::Num(lit_num) => Some(ts_lit_type(TsLit::Number(lit_num.clone()))),
197+
Lit::BigInt(lit_bigint) => {
198+
Some(ts_lit_type(TsLit::BigInt(lit_bigint.clone())))
199+
}
200+
Lit::Regex(_) => Some(regex_type()),
201+
Lit::JSXText(_) => None,
202+
},
203+
DeclMutabilityKind::Mutable => match lit {
204+
Lit::Str(_) => Some(ts_keyword_type(TsKeywordTypeKind::TsStringKeyword)),
205+
Lit::Bool(_) => {
206+
Some(ts_keyword_type(TsKeywordTypeKind::TsBooleanKeyword))
207+
}
208+
Lit::Null(_) => Some(ts_keyword_type(TsKeywordTypeKind::TsNullKeyword)),
209+
Lit::Num(_) => Some(ts_keyword_type(TsKeywordTypeKind::TsNumberKeyword)),
210+
Lit::BigInt(_) => {
211+
Some(ts_keyword_type(TsKeywordTypeKind::TsBigIntKeyword))
212+
}
213+
Lit::Regex(_) => Some(regex_type()),
214+
Lit::JSXText(_) => None,
215+
},
205216
}
206217
}

src/fast_check/transform.rs

+51-24
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ use super::swc_helpers::is_void_type;
4141
use super::swc_helpers::maybe_lit_to_ts_type;
4242
use super::swc_helpers::new_ident;
4343
use super::swc_helpers::ts_keyword_type;
44+
use super::swc_helpers::DeclMutabilityKind;
4445
use super::swc_helpers::ReturnStatementAnalysis;
4546
use super::transform_dts::FastCheckDtsDiagnostic;
4647
use super::transform_dts::FastCheckDtsTransformer;
@@ -858,14 +859,20 @@ impl<'a> FastCheckTransformer<'a> {
858859
_ => None,
859860
};
860861
explicit_type_ann.or_else(|| {
861-
self.maybe_infer_type_from_expr(&assign.right).map(
862-
|type_ann| {
862+
self
863+
.maybe_infer_type_from_expr(
864+
&assign.right,
865+
match prop.readonly {
866+
true => DeclMutabilityKind::Const,
867+
false => DeclMutabilityKind::Mutable,
868+
},
869+
)
870+
.map(|type_ann| {
863871
Box::new(TsTypeAnn {
864872
span: DUMMY_SP,
865873
type_ann: Box::new(type_ann),
866874
})
867-
},
868-
)
875+
})
869876
})
870877
}
871878
}
@@ -1006,10 +1013,15 @@ impl<'a> FastCheckTransformer<'a> {
10061013
return Ok(true);
10071014
}
10081015
if n.type_ann.is_none() {
1009-
let inferred_type = n
1010-
.value
1011-
.as_ref()
1012-
.and_then(|e| self.maybe_infer_type_from_expr(e));
1016+
let inferred_type = n.value.as_ref().and_then(|e| {
1017+
self.maybe_infer_type_from_expr(
1018+
e,
1019+
match n.readonly {
1020+
true => DeclMutabilityKind::Const,
1021+
false => DeclMutabilityKind::Mutable,
1022+
},
1023+
)
1024+
});
10131025
match inferred_type {
10141026
Some(t) => {
10151027
n.type_ann = Some(Box::new(TsTypeAnn {
@@ -1056,10 +1068,9 @@ impl<'a> FastCheckTransformer<'a> {
10561068
} else if let Some(type_ann) = n.type_ann.clone() {
10571069
type_ann
10581070
} else {
1059-
let inferred_type = n
1060-
.value
1061-
.as_ref()
1062-
.and_then(|e| self.maybe_infer_type_from_expr(e));
1071+
let inferred_type = n.value.as_ref().and_then(|e| {
1072+
self.maybe_infer_type_from_expr(e, DeclMutabilityKind::Mutable)
1073+
});
10631074
match inferred_type {
10641075
Some(t) => Box::new(TsTypeAnn {
10651076
span: DUMMY_SP,
@@ -1244,7 +1255,8 @@ impl<'a> FastCheckTransformer<'a> {
12441255
)?;
12451256
}
12461257
BlockStmtOrExpr::Expr(expr) => {
1247-
let inferred_type = self.maybe_infer_type_from_expr(expr);
1258+
let inferred_type =
1259+
self.maybe_infer_type_from_expr(expr, DeclMutabilityKind::Mutable);
12481260
match inferred_type {
12491261
Some(t) => {
12501262
let mut return_type = Box::new(t);
@@ -1370,7 +1382,10 @@ impl<'a> FastCheckTransformer<'a> {
13701382
Pat::Assign(assign) => match &mut *assign.left {
13711383
Pat::Ident(ident) => {
13721384
if ident.type_ann.is_none() {
1373-
let inferred_type = self.maybe_infer_type_from_expr(&assign.right);
1385+
let inferred_type = self.maybe_infer_type_from_expr(
1386+
&assign.right,
1387+
DeclMutabilityKind::Mutable,
1388+
);
13741389
match inferred_type {
13751390
Some(t) => {
13761391
ident.type_ann = Some(Box::new(TsTypeAnn {
@@ -1491,7 +1506,7 @@ impl<'a> FastCheckTransformer<'a> {
14911506
// don't need to do anything for ambient decls
14921507
if !is_ambient {
14931508
for decl in &mut n.decls {
1494-
self.transform_var_declarator(decl)?;
1509+
self.transform_var_declarator(n.kind, decl)?;
14951510
}
14961511
}
14971512

@@ -1500,15 +1515,23 @@ impl<'a> FastCheckTransformer<'a> {
15001515

15011516
fn transform_var_declarator(
15021517
&mut self,
1518+
decl_kind: VarDeclKind,
15031519
n: &mut VarDeclarator,
15041520
) -> Result<(), Vec<FastCheckDiagnostic>> {
15051521
match &mut n.name {
15061522
Pat::Ident(ident) => {
15071523
if ident.type_ann.is_none() {
1508-
let inferred_type = n
1509-
.init
1510-
.as_ref()
1511-
.and_then(|e| self.maybe_infer_type_from_expr(e));
1524+
let inferred_type = n.init.as_ref().and_then(|e| {
1525+
self.maybe_infer_type_from_expr(
1526+
e,
1527+
match decl_kind {
1528+
VarDeclKind::Var | VarDeclKind::Let => {
1529+
DeclMutabilityKind::Mutable
1530+
}
1531+
VarDeclKind::Const => DeclMutabilityKind::Const,
1532+
},
1533+
)
1534+
});
15121535
match inferred_type {
15131536
Some(t) => {
15141537
ident.type_ann = Some(Box::new(TsTypeAnn {
@@ -1719,8 +1742,8 @@ impl<'a> FastCheckTransformer<'a> {
17191742
}
17201743
}
17211744
is_leavable
1722-
},
1723-
Expr::Object(n) => {
1745+
},
1746+
Expr::Object(n) => {
17241747
let mut is_leavable = true;
17251748
for prop in &mut n.props {
17261749
is_leavable = match prop {
@@ -1817,11 +1840,15 @@ impl<'a> FastCheckTransformer<'a> {
18171840
Ok(is_leavable)
18181841
}
18191842

1820-
fn maybe_infer_type_from_expr(&self, expr: &Expr) -> Option<TsType> {
1843+
fn maybe_infer_type_from_expr(
1844+
&self,
1845+
expr: &Expr,
1846+
decl_kind: DeclMutabilityKind,
1847+
) -> Option<TsType> {
18211848
match expr {
18221849
Expr::TsTypeAssertion(n) => infer_simple_type_from_type(&n.type_ann),
18231850
Expr::TsAs(n) => infer_simple_type_from_type(&n.type_ann),
1824-
Expr::Lit(lit) => maybe_lit_to_ts_type(lit),
1851+
Expr::Lit(lit) => maybe_lit_to_ts_type(lit, decl_kind),
18251852
Expr::Call(call_expr) => {
18261853
if self.is_call_expr_symbol_create(call_expr) {
18271854
Some(TsType::TsTypeOperator(TsTypeOperator {
@@ -1837,7 +1864,7 @@ impl<'a> FastCheckTransformer<'a> {
18371864
None
18381865
}
18391866
}
1840-
Expr::Paren(n) => self.maybe_infer_type_from_expr(&n.expr),
1867+
Expr::Paren(n) => self.maybe_infer_type_from_expr(&n.expr, decl_kind),
18411868
Expr::This(_)
18421869
| Expr::Array(_)
18431870
| Expr::Object(_)

src/fast_check/transform_dts.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ use crate::FastCheckDiagnosticRange;
1111
use super::range_finder::ModulePublicRanges;
1212
use super::swc_helpers::any_type_ann;
1313
use super::swc_helpers::maybe_lit_to_ts_type;
14-
use super::swc_helpers::maybe_lit_to_ts_type_const;
1514
use super::swc_helpers::ts_readonly;
1615
use super::swc_helpers::ts_tuple_element;
1716
use super::swc_helpers::type_ann;
17+
use super::swc_helpers::DeclMutabilityKind;
1818

1919
#[derive(Debug, Clone, thiserror::Error)]
2020
pub enum FastCheckDtsDiagnostic {
@@ -419,13 +419,13 @@ impl<'a> FastCheckDtsTransformer<'a> {
419419
members,
420420
}))
421421
}
422-
Expr::Lit(lit) => {
423-
if as_const {
424-
maybe_lit_to_ts_type_const(&lit)
425-
} else {
426-
maybe_lit_to_ts_type(&lit)
427-
}
428-
}
422+
Expr::Lit(lit) => maybe_lit_to_ts_type(
423+
&lit,
424+
match as_const {
425+
true => DeclMutabilityKind::Const,
426+
false => DeclMutabilityKind::Mutable,
427+
},
428+
),
429429
Expr::TsConstAssertion(ts_const) => {
430430
self.expr_to_ts_type(*ts_const.expr, true, true)
431431
}

tests/specs/graph/fast_check/class_properties.txt

+11-1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ export class Baz extends Bar {
4444
override foo = "";
4545
}
4646

47+
export class ReadonlyPropConstant {
48+
readonly static value = "value";
49+
}
50+
4751
# mod.ts
4852
import 'jsr:@scope/a'
4953

@@ -80,7 +84,7 @@ import 'jsr:@scope/a'
8084
},
8185
{
8286
"kind": "esm",
83-
"size": 867,
87+
"size": 941,
8488
"mediaType": "TypeScript",
8589
"specifier": "https://jsr.io/@scope/a/1.0.0/mod.ts"
8690
}
@@ -122,6 +126,9 @@ Fast check https://jsr.io/@scope/a/1.0.0/mod.ts:
122126
export class Baz extends Bar {
123127
declare foo: string;
124128
}
129+
export class ReadonlyPropConstant {
130+
declare static readonly value: "value";
131+
}
125132
--- DTS ---
126133
export declare class RedBlackNode<T> extends BinarySearchNode<T> {
127134
parent: RedBlackNode<T> | null;
@@ -144,3 +151,6 @@ Fast check https://jsr.io/@scope/a/1.0.0/mod.ts:
144151
export declare class Baz extends Bar {
145152
foo: string;
146153
}
154+
export declare class ReadonlyPropConstant {
155+
static readonly value: "value";
156+
}

tests/specs/graph/fast_check/enums.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ Fast check https://jsr.io/@scope/a/1.0.0/mod.ts:
109109
Value1 = "a",
110110
Value2 = "b"
111111
}
112-
const value: number = {} as never;
112+
const value: 10 = {} as never;
113113
export enum EnumWithNonConstInits {
114114
Value1 = new Public1().test,
115115
Value2 = new Public2().asdf * value + NonExportedEnum.Value
@@ -145,7 +145,7 @@ Fast check https://jsr.io/@scope/a/1.0.0/mod.ts:
145145
Value1 = "a",
146146
Value2 = "b"
147147
}
148-
declare const value: number;
148+
declare const value: 10;
149149
export declare enum EnumWithNonConstInits {
150150
Value1,
151151
Value2

tests/specs/graph/fast_check/vars.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ Fast check https://jsr.io/@scope/a/1.0.0/mod.ts:
170170
export const inferred9: [...string] = {} as never;
171171
export const inferred10: (1 | 2n)[] = {} as never;
172172
export const inferred11: (keyof string)[] = {} as never;
173-
export const inferred12: number = {} as never;
173+
export const inferred12: 1 = {} as never;
174174
--- DTS ---
175175
declare const public1: Public1;
176176
export { public1 };
@@ -214,4 +214,4 @@ Fast check https://jsr.io/@scope/a/1.0.0/mod.ts:
214214
export declare const inferred9: [...string];
215215
export declare const inferred10: (1 | 2n)[];
216216
export declare const inferred11: (keyof string)[];
217-
export declare const inferred12: number;
217+
export declare const inferred12: 1;

0 commit comments

Comments
 (0)