Skip to content

Commit 4831ffd

Browse files
committed
impl extends keyword new
1 parent 36588b6 commit 4831ffd

File tree

13 files changed

+247
-24
lines changed

13 files changed

+247
-24
lines changed

crates/emmylua_code_analysis/src/compilation/analyzer/doc/infer_type.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -675,7 +675,14 @@ fn infer_conditional_type(
675675
let true_type = infer_type(analyzer, when_true);
676676
let false_type = infer_type(analyzer, when_false);
677677

678-
return LuaConditionalType::new(condition_type, true_type, false_type, infer_params).into();
678+
return LuaConditionalType::new(
679+
condition_type,
680+
true_type,
681+
false_type,
682+
infer_params,
683+
cond_type.has_new().unwrap_or(false),
684+
)
685+
.into();
679686
}
680687

681688
LuaType::Unknown

crates/emmylua_code_analysis/src/compilation/test/generic_test.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,4 +432,38 @@ mod test {
432432
assert_eq!(ws.expr_ty("E"), ws.ty("int"));
433433
assert_eq!(ws.expr_ty("F"), ws.ty("string"));
434434
}
435+
436+
#[test]
437+
fn test_infer_new_constructor() {
438+
let mut ws = VirtualWorkspace::new();
439+
ws.def(
440+
r#"
441+
---@alias ConstructorParameters<T> T extends new (fun(...: infer P): any) and P or never
442+
443+
---@generic T
444+
---@param name `T`|T
445+
---@param ... ConstructorParameters<T>
446+
function f(name, ...)
447+
end
448+
"#,
449+
);
450+
assert!(ws.check_code_for(
451+
DiagnosticCode::ParamTypeNotMatch,
452+
r#"
453+
---@class A
454+
---@overload fun(name: string, age: number)
455+
local A = {}
456+
457+
f(A, "b", 1)
458+
f("A", "b", 1)
459+
460+
"#,
461+
));
462+
assert!(!ws.check_code_for(
463+
DiagnosticCode::ParamTypeNotMatch,
464+
r#"
465+
f("A", "b", "1")
466+
"#,
467+
));
468+
}
435469
}

crates/emmylua_code_analysis/src/db_index/type/types.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1474,6 +1474,7 @@ pub struct LuaConditionalType {
14741474
false_type: LuaType,
14751475
/// infer 参数声明, 这些参数只在 true_type 的作用域内可见
14761476
infer_params: Vec<GenericParam>,
1477+
pub has_new: bool,
14771478
}
14781479

14791480
impl TypeVisitTrait for LuaConditionalType {
@@ -1493,12 +1494,14 @@ impl LuaConditionalType {
14931494
true_type: LuaType,
14941495
false_type: LuaType,
14951496
infer_params: Vec<GenericParam>,
1497+
has_new: bool,
14961498
) -> Self {
14971499
Self {
14981500
condition,
14991501
true_type,
15001502
false_type,
15011503
infer_params,
1504+
has_new,
15021505
}
15031506
}
15041507

crates/emmylua_code_analysis/src/semantic/generic/instantiate_func_generic.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,6 @@ pub fn instantiate_func_generic(
107107
}
108108

109109
let arg_type = infer_expr(db, context.cache, call_arg_expr.clone())?;
110-
dbg!(&func_param_type);
111-
dbg!(&arg_type);
112110

113111
match (func_param_type, &arg_type) {
114112
(LuaType::Variadic(variadic), _) => {
@@ -152,10 +150,10 @@ pub fn instantiate_func_generic(
152150
substitutor.add_self_type(self_type);
153151
}
154152
if let LuaType::DocFunction(f) = instantiate_doc_function(db, func, &substitutor) {
155-
dbg!(&func);
156-
dbg!(&substitutor);
157-
dbg!(&call_expr);
158-
dbg!(&f);
153+
// dbg!(&func);
154+
// dbg!(&substitutor);
155+
// dbg!(&call_expr);
156+
// dbg!(&f);
159157
Ok(f.deref().clone())
160158
} else {
161159
Ok(func.clone())

crates/emmylua_code_analysis/src/semantic/generic/instantiate_type_generic.rs

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use std::{
44
};
55

66
use crate::{
7-
DbIndex, GenericTpl, GenericTplId, LuaArrayType, LuaMemberKey, LuaSignatureId, LuaTupleStatus,
8-
TypeOps, check_type_compact,
7+
DbIndex, GenericTpl, GenericTplId, LuaArrayType, LuaMemberKey, LuaOperatorMetaMethod,
8+
LuaSignatureId, LuaTupleStatus, LuaTypeDeclId, TypeOps, check_type_compact,
99
db_index::{
1010
LuaAliasCallKind, LuaConditionalType, LuaFunctionType, LuaGenericType, LuaIntersectionType,
1111
LuaMappedType, LuaObjectType, LuaTupleType, LuaType, LuaUnionType, VariadicType,
@@ -41,8 +41,6 @@ pub fn instantiate_type_generic(
4141
LuaType::Signature(sig_id) => instantiate_signature(db, sig_id, substitutor),
4242
LuaType::Call(alias_call) => instantiate_alias_call(db, alias_call, substitutor),
4343
LuaType::Variadic(variadic) => instantiate_variadic_type(db, variadic, substitutor),
44-
LuaType::Conditional(conditional) => instantiate_conditional(db, conditional, substitutor),
45-
LuaType::Mapped(mapped) => instantiate_mapped_type(db, mapped.deref(), substitutor),
4644
LuaType::SelfInfer => {
4745
if let Some(typ) = substitutor.get_self_type() {
4846
typ.clone()
@@ -54,6 +52,8 @@ pub fn instantiate_type_generic(
5452
let inner = instantiate_type_generic(db, guard.deref(), substitutor);
5553
LuaType::TypeGuard(inner.into())
5654
}
55+
LuaType::Conditional(conditional) => instantiate_conditional(db, conditional, substitutor),
56+
LuaType::Mapped(mapped) => instantiate_mapped_type(db, mapped.deref(), substitutor),
5757
_ => ty.clone(),
5858
}
5959
}
@@ -472,9 +472,22 @@ fn instantiate_conditional(
472472
&& alias_call.get_call_kind() == LuaAliasCallKind::Extends
473473
&& alias_call.get_operands().len() == 2
474474
{
475-
let left = instantiate_type_generic(db, &alias_call.get_operands()[0], substitutor);
475+
let mut left = instantiate_type_generic(db, &alias_call.get_operands()[0], substitutor);
476476
let right_origin = &alias_call.get_operands()[1];
477477
let right = instantiate_type_generic(db, right_origin, substitutor);
478+
// 如果存在 new 标记与左侧为类定义, 那么我们需要的是他的构造函数签名
479+
if conditional.has_new
480+
&& let LuaType::Ref(id) | LuaType::Def(id) = &left
481+
{
482+
if let Some(decl) = db.get_type_index().get_type_decl(id) {
483+
// 我们取第一个构造函数签名
484+
if decl.is_class()
485+
&& let Some(constructor) = get_default_constructor(db, id)
486+
{
487+
left = constructor;
488+
}
489+
}
490+
}
478491

479492
// infer 必须位于条件语句中(right), 判断是否包含并收集
480493
if contains_conditional_infer(&right)
@@ -523,6 +536,7 @@ fn instantiate_conditional(
523536
new_true,
524537
new_false,
525538
conditional.get_infer_params().to_vec(),
539+
conditional.has_new,
526540
)
527541
.into(),
528542
)
@@ -866,3 +880,13 @@ fn collect_mapped_key_atoms(key_ty: &LuaType, acc: &mut Vec<LuaType>) {
866880
_ => acc.push(key_ty.clone()),
867881
}
868882
}
883+
884+
fn get_default_constructor(db: &DbIndex, decl_id: &LuaTypeDeclId) -> Option<LuaType> {
885+
let ids = db
886+
.get_operator_index()
887+
.get_operators(&decl_id.clone().into(), LuaOperatorMetaMethod::Call)?;
888+
889+
let id = ids.first()?;
890+
let operator = db.get_operator_index().get_operator(id)?;
891+
Some(operator.get_operator_func(db))
892+
}

crates/emmylua_code_analysis/src/semantic/type_check/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,13 @@ fn check_general_type_compact(
145145
}
146146
Err(TypeCheckFailReason::TypeNotMatch)
147147
}
148+
LuaType::Never => {
149+
// never 只能赋值给 never
150+
if compact_type.is_never() {
151+
return Ok(());
152+
}
153+
Err(TypeCheckFailReason::TypeNotMatch)
154+
}
148155
_ => Err(TypeCheckFailReason::TypeNotMatch),
149156
}
150157
}

crates/emmylua_ls/src/handlers/semantic_token/build_semantic_tokens.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ fn build_tokens_semantic_token(
211211
}
212212
LuaTokenKind::TkDocKeyOf
213213
| LuaTokenKind::TkDocExtends
214+
| LuaTokenKind::TkDocNew
214215
| LuaTokenKind::TkDocAs
215216
| LuaTokenKind::TkDocIn
216217
| LuaTokenKind::TkDocInfer

crates/emmylua_parser/src/grammar/doc/test.rs

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3151,8 +3151,69 @@ Syntax(Chunk)@0..110
31513151
#[test]
31523152
fn test_alias_conditional_infer_dots() {
31533153
let code = r#"
3154-
---@alias ConstructorParameters<T> T extends (fun(infer: infer P): any) and P... or unknown
3154+
---@alias ConstructorParameters<T> T extends new (fun(...: infer P): any) and P or never
31553155
"#;
31563156
print_ast(code);
3157+
let result = r#"
3158+
Syntax(Chunk)@0..106
3159+
Syntax(Block)@0..106
3160+
Token(TkEndOfLine)@0..1 "\n"
3161+
Token(TkWhitespace)@1..9 " "
3162+
Syntax(Comment)@9..97
3163+
Token(TkDocStart)@9..13 "---@"
3164+
Syntax(DocTagAlias)@13..97
3165+
Token(TkTagAlias)@13..18 "alias"
3166+
Token(TkWhitespace)@18..19 " "
3167+
Token(TkName)@19..40 "ConstructorParameters"
3168+
Syntax(DocGenericDeclareList)@40..43
3169+
Token(TkLt)@40..41 "<"
3170+
Syntax(DocGenericParameter)@41..42
3171+
Token(TkName)@41..42 "T"
3172+
Token(TkGt)@42..43 ">"
3173+
Token(TkWhitespace)@43..44 " "
3174+
Syntax(TypeConditional)@44..97
3175+
Syntax(TypeBinary)@44..82
3176+
Syntax(TypeName)@44..45
3177+
Token(TkName)@44..45 "T"
3178+
Token(TkWhitespace)@45..46 " "
3179+
Token(TkDocExtends)@46..53 "extends"
3180+
Token(TkWhitespace)@53..54 " "
3181+
Token(TkDocNew)@54..57 "new"
3182+
Token(TkWhitespace)@57..58 " "
3183+
Token(TkLeftParen)@58..59 "("
3184+
Syntax(TypeFun)@59..81
3185+
Token(TkName)@59..62 "fun"
3186+
Token(TkLeftParen)@62..63 "("
3187+
Syntax(DocTypedParameter)@63..75
3188+
Token(TkDots)@63..66 "..."
3189+
Token(TkColon)@66..67 ":"
3190+
Token(TkWhitespace)@67..68 " "
3191+
Syntax(TypeInfer)@68..75
3192+
Token(TkName)@68..73 "infer"
3193+
Token(TkWhitespace)@73..74 " "
3194+
Syntax(DocGenericParameter)@74..75
3195+
Token(TkName)@74..75 "P"
3196+
Token(TkRightParen)@75..76 ")"
3197+
Token(TkColon)@76..77 ":"
3198+
Token(TkWhitespace)@77..78 " "
3199+
Syntax(DocTypeList)@78..81
3200+
Syntax(DocNamedReturnType)@78..81
3201+
Syntax(TypeName)@78..81
3202+
Token(TkName)@78..81 "any"
3203+
Token(TkRightParen)@81..82 ")"
3204+
Token(TkWhitespace)@82..83 " "
3205+
Token(TkAnd)@83..86 "and"
3206+
Token(TkWhitespace)@86..87 " "
3207+
Syntax(TypeName)@87..88
3208+
Token(TkName)@87..88 "P"
3209+
Token(TkWhitespace)@88..89 " "
3210+
Token(TkOr)@89..91 "or"
3211+
Token(TkWhitespace)@91..92 " "
3212+
Syntax(TypeName)@92..97
3213+
Token(TkName)@92..97 "never"
3214+
Token(TkEndOfLine)@97..98 "\n"
3215+
Token(TkWhitespace)@98..106 " "
3216+
"#;
3217+
assert_ast_eq!(code, result);
31573218
}
31583219
}

crates/emmylua_parser/src/grammar/doc/types.rs

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,22 @@ pub fn parse_binary_operator(
8989
while bop != LuaTypeBinaryOperator::None && bop.get_priority().left > limit {
9090
let range = p.current_token_range();
9191
let m = cm.precede(p, LuaSyntaxKind::TypeBinary);
92-
p.bump();
92+
93+
if bop == LuaTypeBinaryOperator::Extends {
94+
let prev_lexer_state = p.lexer.state;
95+
p.set_lexer_state(LuaDocLexerState::Extends);
96+
p.bump();
97+
p.set_lexer_state(prev_lexer_state);
98+
} else {
99+
p.bump();
100+
}
93101
if p.current_token() != LuaTokenKind::TkDocQuestion {
94102
// infer 只有在条件类型中才能被解析为关键词
95103
let parse_result = if bop == LuaTypeBinaryOperator::Extends {
96-
p.infer_depth += 1;
104+
let prev_state = p.state;
105+
p.set_parser_state(LuaDocParserState::Extends);
97106
let res = parse_sub_type(p, bop.get_priority().right);
98-
p.infer_depth = p.infer_depth.saturating_sub(1);
107+
p.set_parser_state(prev_state);
99108
res
100109
} else {
101110
parse_sub_type(p, bop.get_priority().right)
@@ -150,14 +159,15 @@ fn parse_primary_type(p: &mut LuaDocParser) -> DocParseResult {
150159
| LuaTokenKind::TkTrue
151160
| LuaTokenKind::TkFalse => parse_literal_type(p),
152161
LuaTokenKind::TkName => {
153-
if p.is_infer_context() && p.current_token_text() == "infer" {
162+
if p.state == LuaDocParserState::Extends && p.current_token_text() == "infer" {
154163
parse_infer_type(p)
155164
} else {
156165
parse_name_or_func_type(p)
157166
}
158167
}
159168
LuaTokenKind::TkStringTemplateType => parse_string_template_type(p),
160169
LuaTokenKind::TkDots => parse_vararg_type(p),
170+
LuaTokenKind::TkDocNew => parse_constructor_type(p),
161171
_ => Err(LuaParseError::doc_error_from(
162172
&t!("expect type"),
163173
p.current_token_range(),
@@ -581,3 +591,26 @@ fn parse_one_line_type(p: &mut LuaDocParser) -> DocParseResult {
581591

582592
Ok(m.complete(p))
583593
}
594+
595+
fn parse_constructor_type(p: &mut LuaDocParser) -> DocParseResult {
596+
let new_range = p.current_token_range();
597+
expect_token(p, LuaTokenKind::TkDocNew)?;
598+
599+
let cm = match parse_sub_type(p, 0) {
600+
Ok(cm) => {
601+
if cm.kind != LuaSyntaxKind::TypeFun {
602+
let err = LuaParseError::doc_error_from(
603+
&t!("new keyword must be followed by function type"),
604+
new_range,
605+
);
606+
p.push_error(err.clone());
607+
return Err(err);
608+
}
609+
cm
610+
}
611+
Err(err) => {
612+
return Err(err);
613+
}
614+
};
615+
Ok(cm)
616+
}

crates/emmylua_parser/src/kind/lua_token_kind.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ pub enum LuaTokenKind {
140140
TkDocAnd, // &
141141
TkDocKeyOf, // keyof
142142
TkDocExtends, // extends
143+
TkDocNew, // new
143144
TkDocAs, // as
144145
TkDocIn, // in
145146
TkDocInfer, // infer

0 commit comments

Comments
 (0)