Skip to content

Commit 196895e

Browse files
committed
support const function and add more analysis for safe nav
1 parent 1ed5d58 commit 196895e

10 files changed

Lines changed: 130 additions & 7 deletions

File tree

crates/emmylua_code_analysis/src/compilation/analyzer/decl/stats.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,11 @@ pub fn analyze_func_stat(analyzer: &mut DeclAnalyzer, stat: LuaFuncStat) -> Opti
317317
pub fn analyze_local_func_stat(analyzer: &mut DeclAnalyzer, stat: LuaLocalFuncStat) -> Option<()> {
318318
let local_name = stat.get_local_name()?;
319319
let name_token = local_name.get_name_token()?;
320+
let is_const = if stat.is_const() {
321+
Some(LocalAttribute::Const)
322+
} else {
323+
None
324+
};
320325
let name = name_token.get_name_text();
321326
let range = local_name.get_range();
322327
let file_id = analyzer.get_file_id();
@@ -326,7 +331,7 @@ pub fn analyze_local_func_stat(analyzer: &mut DeclAnalyzer, stat: LuaLocalFuncSt
326331
range,
327332
LuaDeclExtra::Local {
328333
kind: local_name.syntax().kind(),
329-
attrib: None,
334+
attrib: is_const,
330335
},
331336
None,
332337
);

crates/emmylua_code_analysis/src/compilation/analyzer/flow/bind_analyze/exprs/bind_binary_expr.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub fn bind_binary_expr(
1818
match op_token.get_op() {
1919
BinaryOperator::OpAnd => bind_and_expr(binder, binary_expr, current),
2020
BinaryOperator::OpOr => bind_or_expr(binder, binary_expr, current),
21+
BinaryOperator::OpNilCoalescing => bind_or_expr(binder, binary_expr, current),
2122
_ => {
2223
bind_each_child(binder, LuaAst::LuaBinaryExpr(binary_expr.clone()), current);
2324
Some(())
@@ -74,7 +75,7 @@ pub fn is_binary_logical(expr: &LuaExpr) -> bool {
7475

7576
return matches!(
7677
op_token.get_op(),
77-
BinaryOperator::OpAnd | BinaryOperator::OpOr
78+
BinaryOperator::OpAnd | BinaryOperator::OpOr | BinaryOperator::OpNilCoalescing
7879
);
7980
}
8081
LuaExpr::ParenExpr(paren_expr) => {

crates/emmylua_code_analysis/src/compilation/analyzer/flow/bind_analyze/exprs/mod.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,34 @@ pub fn bind_index_expr(
9595
current: FlowId,
9696
) -> Option<()> {
9797
binder.bind_syntax_node(index_expr.get_syntax_id(), current);
98+
if index_expr.is_safe_index() {
99+
return bind_safe_index_expr(binder, index_expr, current);
100+
}
98101
bind_each_child(binder, LuaAst::LuaIndexExpr(index_expr.clone()), current);
99102
Some(())
100103
}
101104

105+
fn bind_safe_index_expr(
106+
binder: &mut FlowBinder,
107+
index_expr: LuaIndexExpr,
108+
current: FlowId,
109+
) -> Option<()> {
110+
let prefix_expr = index_expr.get_prefix_expr()?;
111+
112+
let pre_access = binder.create_branch_label();
113+
bind_condition_expr(
114+
binder,
115+
prefix_expr,
116+
current,
117+
pre_access,
118+
binder.false_target,
119+
);
120+
let current = finish_flow_label(binder, pre_access, current);
121+
122+
bind_each_child(binder, LuaAst::LuaIndexExpr(index_expr), current);
123+
Some(())
124+
}
125+
102126
pub fn bind_paren_expr(
103127
binder: &mut FlowBinder,
104128
paren_expr: emmylua_parser::LuaParenExpr,

crates/emmylua_code_analysis/src/diagnostic/checker/need_check_nil.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ fn check_call_expr(
3535
semantic_model: &SemanticModel,
3636
call_expr: LuaCallExpr,
3737
) -> Option<()> {
38+
if call_expr.has_safe_navigation() {
39+
return None;
40+
}
41+
3842
let prefix = call_expr.get_prefix_expr()?;
3943
let func = semantic_model.infer_expr(prefix.clone()).ok()?;
4044
if func.is_nullable() {

crates/emmylua_code_analysis/src/semantic/infer/infer_binary/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ fn infer_binary_expr_type(
113113
BinaryOperator::OpConcat => infer_binary_expr_concat(db, left_type, right_type),
114114
BinaryOperator::OpOr => infer_binary_expr_or(db, left_type, right_type),
115115
BinaryOperator::OpAnd => infer_binary_expr_and(db, left_type, right_type),
116+
BinaryOperator::OpNilCoalescing => {
117+
infer_binary_expr_nil_coalescing(db, left_type, right_type)
118+
}
116119
BinaryOperator::OpLt
117120
| BinaryOperator::OpLe
118121
| BinaryOperator::OpGt
@@ -537,3 +540,20 @@ fn float_cmp(left: f64, right: f64, op: BinaryOperator) -> bool {
537540
_ => false,
538541
}
539542
}
543+
544+
fn infer_binary_expr_nil_coalescing(db: &DbIndex, left: LuaType, right: LuaType) -> InferResult {
545+
if left.is_nil() || left.is_any() || left.is_unknown() {
546+
return Ok(right);
547+
}
548+
549+
if right.is_nil() || right.is_any() || right.is_unknown() {
550+
return Ok(left);
551+
}
552+
553+
let left_without_nil = TypeOps::Remove.apply(db, &left, &LuaType::Nil);
554+
if left_without_nil.is_unknown() {
555+
return Ok(right);
556+
}
557+
558+
Ok(TypeOps::Union.apply(db, &left_without_nil, &right))
559+
}

crates/emmylua_code_analysis/src/semantic/infer/infer_call/mod.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::semantic::overload_resolve::callable_accepts_args;
1212
use crate::{
1313
AsyncState, CacheEntry, DbIndex, InFiled, LuaFunctionType, LuaGenericType, LuaInstanceType,
1414
LuaIntersectionType, LuaOperatorMetaMethod, LuaOperatorOwner, LuaSignature, LuaSignatureId,
15-
LuaType, LuaTypeDeclId, LuaUnionType, TypeVisitTrait, VariadicType,
15+
LuaType, LuaTypeDeclId, LuaUnionType, TypeOps, TypeVisitTrait, VariadicType,
1616
};
1717
use crate::{
1818
InferGuardRef,
@@ -767,19 +767,27 @@ pub fn infer_call_expr(
767767

768768
check_can_infer(db, cache, &call_expr)?;
769769

770+
let is_safe_call = call_expr.has_safe_navigation();
771+
770772
let prefix_expr = call_expr.get_prefix_expr().ok_or(InferFailReason::None)?;
771773
let prefix_type = infer_expr(db, cache, prefix_expr)?;
772774
let ret_type = infer_call_expr_func(
773775
db,
774776
cache,
775777
call_expr.clone(),
776-
prefix_type,
778+
prefix_type.clone(),
777779
&InferGuard::new(),
778780
None,
779781
)?
780782
.get_ret()
781783
.clone();
782784

785+
let ret_type = if is_safe_call && prefix_type.is_nullable() {
786+
TypeOps::Union.apply(db, &ret_type, &LuaType::Nil)
787+
} else {
788+
ret_type
789+
};
790+
783791
if !cache.is_no_flow()
784792
&& let Some(tree) = db.get_flow_index().get_flow_tree(&cache.get_file_id())
785793
&& let Some(flow_id) = tree.get_flow_id(call_expr.get_syntax_id())

crates/emmylua_code_analysis/src/semantic/infer/infer_index/mod.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ pub fn infer_index_expr(
7575
index_expr: LuaIndexExpr,
7676
pass_flow: bool,
7777
) -> InferResult {
78+
let is_safe = index_expr.is_safe_index();
7879
let prefix_expr = index_expr.get_prefix_expr().ok_or(InferFailReason::None)?;
7980
let prefix_type = infer_expr_for_index(db, cache, prefix_expr)?;
8081
let index_member_expr = LuaIndexMemberExpr::IndexExpr(index_expr.clone());
@@ -87,10 +88,16 @@ pub fn infer_index_expr(
8788
&InferGuard::new(),
8889
)?;
8990

91+
let result_type = if is_safe && prefix_type.is_nullable() {
92+
TypeOps::Union.apply(db, &member_type, &LuaType::Nil)
93+
} else {
94+
member_type
95+
};
96+
9097
if pass_flow {
91-
infer_member_type_pass_flow(db, cache, index_expr, member_type)
98+
infer_member_type_pass_flow(db, cache, index_expr, result_type)
9299
} else {
93-
Ok(member_type)
100+
Ok(result_type)
94101
}
95102
}
96103

crates/emmylua_code_analysis/src/semantic/infer/narrow/condition_flow/binary_flow.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,37 @@ pub fn get_type_at_binary_expr(
8383
condition_flow,
8484
false,
8585
),
86+
BinaryOperator::OpNilCoalescing => {
87+
try_get_at_nil_coalescing(db, cache, var_ref_id, left_expr, condition_flow)
88+
}
8689
_ => Ok(ConditionFlowAction::Continue),
8790
}
8891
}
8992

93+
fn try_get_at_nil_coalescing(
94+
db: &DbIndex,
95+
cache: &mut LuaInferCache,
96+
var_ref_id: &VarRefId,
97+
left_expr: LuaExpr,
98+
condition_flow: InferConditionFlow,
99+
) -> Result<ConditionFlowAction, InferFailReason> {
100+
let Some(left_ref_id) = get_var_expr_var_ref_id(db, cache, left_expr) else {
101+
return Ok(ConditionFlowAction::Continue);
102+
};
103+
104+
if !var_ref_id.eq(&left_ref_id) {
105+
return Ok(ConditionFlowAction::Continue);
106+
}
107+
108+
if condition_flow == InferConditionFlow::FalseCondition {
109+
Ok(ConditionFlowAction::Pending(
110+
PendingConditionNarrow::NarrowTo(LuaType::Nil),
111+
))
112+
} else {
113+
Ok(ConditionFlowAction::Continue)
114+
}
115+
}
116+
90117
#[allow(clippy::too_many_arguments)]
91118
fn try_get_at_eq_or_neq_expr(
92119
db: &DbIndex,

crates/emmylua_parser/src/grammar/lua/stat.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -646,10 +646,33 @@ fn parse_local(p: &mut LuaParser) -> ParseResult {
646646
}
647647

648648
fn parse_const(p: &mut LuaParser) -> ParseResult {
649-
let m = p.mark(LuaSyntaxKind::ConstStat);
649+
let mut m = p.mark(LuaSyntaxKind::ConstStat);
650650
p.bump(); // consume 'const'
651651

652652
match p.current_token() {
653+
LuaTokenKind::TkFunction => {
654+
p.bump();
655+
m.set_kind(p, LuaSyntaxKind::LocalFuncStat);
656+
657+
match parse_local_name(p, false) {
658+
Ok(_) => {}
659+
Err(_) => {
660+
p.push_error(LuaParseError::syntax_error_from(
661+
&t!("expected function name after 'local function'"),
662+
p.current_token_range(),
663+
));
664+
}
665+
}
666+
match parse_closure_expr(p) {
667+
Ok(_) => {}
668+
Err(_) => {
669+
p.push_error(LuaParseError::syntax_error_from(
670+
&t!("invalid function definition"),
671+
p.current_token_range(),
672+
));
673+
}
674+
}
675+
}
653676
LuaTokenKind::TkName => {
654677
parse_variable_name_list(p, false)?;
655678

crates/emmylua_parser/src/syntax/node/lua/stat.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,10 @@ impl LuaLocalFuncStat {
440440
pub fn get_closure(&self) -> Option<LuaClosureExpr> {
441441
self.child()
442442
}
443+
444+
pub fn is_const(&self) -> bool {
445+
self.token_by_kind(LuaTokenKind::TkConst).is_some()
446+
}
443447
}
444448

445449
#[derive(Debug, Clone, PartialEq, Eq, Hash)]

0 commit comments

Comments
 (0)