Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 15 additions & 25 deletions crates/emmylua_code_analysis/src/check/checker/attribute_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -281,29 +281,19 @@ fn add_type_check_diagnostic(
arg_type: &LuaType,
param_type: &LuaType,
) {
match check_type_detail(semantic_model, arg_type, param_type) {
Ok(()) => {}
Err(reason) => {
let reason_message = match reason {
TypeCheckFailReason::TypeNotMatchWithReason(reason) => reason,
TypeCheckFailReason::TypeNotMatch => String::new(),
TypeCheckFailReason::TypeRecursion => "type recursion".to_string(),
};
let reason = if reason_message.is_empty() {
String::new()
} else {
format!(" {}", reason_message)
};
context.add_diagnostic(
DiagnosticCode::AttributeParamTypeMismatch,
range,
t!(
"expected `%{expected}` but found `%{found}`.%{reason}",
expected = humanize_type(semantic_model, param_type),
found = humanize_type(semantic_model, arg_type),
reason = reason
),
);
}
}
let reason = match check_type_detail(semantic_model, arg_type, param_type) {
Err(TypeCheckFailReason::TypeNotMatchWithReason(reason)) => format!(" {reason}"),
Err(TypeCheckFailReason::TypeNotMatch) => String::new(),
_ => return,
};
context.add_diagnostic(
DiagnosticCode::AttributeParamTypeMismatch,
range,
t!(
"expected `%{expected}` but found `%{found}`.%{reason}",
expected = humanize_type(semantic_model, param_type),
found = humanize_type(semantic_model, arg_type),
reason = reason
),
);
}
19 changes: 19 additions & 0 deletions crates/emmylua_code_analysis/src/check/checker/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,25 @@ pub fn check_file(semantic_model: &SemanticModel<'_>, config: Arc<CheckConfig>)
run_checked::<check_field::CheckFieldChecker>(&mut context, semantic_model);
run_checked::<discard_returns::DiscardReturnsChecker>(&mut context, semantic_model);
run_checked::<global_non_module::GlobalInNonModuleChecker>(&mut context, semantic_model);
let mut ranges: Vec<_> = semantic_model
.type_evaluation_errors()
.into_iter()
.map(|origin| {
origin
.and_then(|id| semantic_model.type_def_of(&id))
.filter(|def| def.file_id == semantic_model.file_id())
.map_or(TextRange::empty(0.into()), |def| def.name_range)
})
.collect();
ranges.sort_unstable_by_key(|range| (range.start(), range.end()));
ranges.dedup();
for range in ranges {
context.add_diagnostic(
DiagnosticCode::TypeComplexity,
range,
t!("Type evaluation is too complex or possibly infinite."),
);
}
context.into_diagnostics()
}

Expand Down
2 changes: 2 additions & 0 deletions crates/emmylua_code_analysis/src/check/diagnostic_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ pub enum DiagnosticCode {
DocSyntaxError,
/// Type not found
TypeNotFound,
/// Type evaluation exceeded its resource budget
TypeComplexity,
/// Missing return statement
MissingReturn,
/// Param Type not match
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1905,6 +1905,9 @@ return t
---@class B
---@class C
---@class D
---@alias Pair A | B
---@alias TooWide A | B | C | D
---@alias Identity<T> T

---@param a A | B | C
---@return boolean
Expand All @@ -1913,21 +1916,23 @@ return t
end
"#,
);
assert!(!ws.has_no_diagnostic(
DiagnosticCode::AssignTypeMismatch,
r#"
---@type fun(a: A | B | C | D): boolean
local tmp = condition
"#,
));

assert!(ws.has_no_diagnostic(
DiagnosticCode::AssignTypeMismatch,
r#"
---@type fun(a: A | B): boolean
local tmp = condition
"#,
));
for (parameter, accepted) in [
("A | B | C | D", false),
("TooWide", false),
("Identity<A | B | C | D>", false),
("A | B", true),
("Pair", true),
("Identity<A | B>", true),
] {
assert_eq!(
ws.has_no_diagnostic(
DiagnosticCode::AssignTypeMismatch,
&format!("---@type fun(a: {parameter}): boolean\nlocal tmp = condition"),
),
accepted,
"{parameter}"
);
}
}

#[test]
Expand Down
1 change: 1 addition & 0 deletions crates/emmylua_code_analysis/src/check/test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ mod param_type_check_test;
mod readonly_test;
mod redefined_local_test;
mod redundant_parameter_test;
mod reentry_test;
mod require_module_visibility_test;
mod return_type_mismatch_test;
mod syntax_error_test;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1680,23 +1680,39 @@ mod test {
#[test]
fn test_index_access_with_keyof_alias() {
let mut ws = VirtualWorkspace::new();
assert!(ws.has_no_diagnostic(
DiagnosticCode::ParamTypeMismatch,
ws.def(
r#"
---@class A
---@field one 1

---@class B
---@field one string
---@alias Key 'one'
---@alias KeyofA keyof A
---@type A[KeyofA]
local tmp

---@param v 1
local function test(v)
end

test(tmp)
"#,
));
---@alias KeyofB keyof B
"#,
);
for indexed in [
"A[KeyofA]",
"A[Key]",
"A[KeyofB]",
"RawGet<A, Key>",
"RawGet<A, keyof B>",
"[1, string][0]",
"RawGet<[1, string], 0>",
] {
for (parameter, accepted) in [("1", true), ("string", false)] {
assert_eq!(
ws.has_no_diagnostic(
DiagnosticCode::ParamTypeMismatch,
&format!(
"---@type {indexed}\nlocal value\n---@param v {parameter}\nlocal function accept(v) end\naccept(value)"
),
),
accepted,
"{indexed} -> {parameter}"
);
}
}
}

#[test]
Expand Down
Loading