Skip to content

Commit 37e8d9e

Browse files
committed
Merge remote-tracking branch 'EmmyLuaLs/main' into Generics
2 parents 2512a70 + 7c685e1 commit 37e8d9e

File tree

29 files changed

+475
-101
lines changed

29 files changed

+475
-101
lines changed

CHANGELOG.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,24 @@
44

55
---
66

7+
## [0.15.0] - 2025-10-10
8+
9+
### ✨ Added
10+
- **Use Clippy as linter**: core codebase now uses Clippy as the linter, improving code quality and consistency.
11+
- **Support `textdocument/diagnostic`**: Added support for the `textDocument/diagnostic` request, allowing clients to fetch diagnostics for a specific document.
12+
- **Support annotation `@readonly`**: You can now use the `@readonly` annotation to mark fields as read-only. For example:
13+
```lua
14+
---@readonly
15+
local myVar = 42
16+
```
17+
- **Add check for `global in non module`**: Added a new diagnostic to check for global variable declarations in non-module scope. This helps detect unintended global variable declarations.
18+
19+
### 🔧 Changed
20+
- **Optimize semantic token**: Optimized semantic token handling for delimiter symbols.
21+
22+
### 🐛 Fixed
23+
- **Fix generic pattern matching issue**: Fixed an issue where generic pattern matching aliases could lead to incorrect type inference.
24+
725
## [0.14.0] - 2025-9-19
826

927
### 🔧 Changed

Cargo.lock

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ members = [
77

88
[workspace.dependencies]
99
# local
10-
emmylua_code_analysis = { path = "crates/emmylua_code_analysis", version = "0.14.0" }
11-
emmylua_parser = { path = "crates/emmylua_parser", version = "0.16.0" }
12-
emmylua_parser_desc = { path = "crates/emmylua_parser_desc", version = "0.16.0" }
10+
emmylua_code_analysis = { path = "crates/emmylua_code_analysis", version = "0.15.0" }
11+
emmylua_parser = { path = "crates/emmylua_parser", version = "0.17.0" }
12+
emmylua_parser_desc = { path = "crates/emmylua_parser_desc", version = "0.17.0" }
1313
emmylua_diagnostic_macro = { path = "crates/emmylua_diagnostic_macro", version = "0.5.0" }
1414

1515
# external

crates/emmylua_check/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "emmylua_check"
3-
version = "0.14.0"
3+
version = "0.15.0"
44
edition = "2024"
55
authors = ["CppCXY"]
66
description = "A command-line tool for checking lua code."

crates/emmylua_code_analysis/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "emmylua_code_analysis"
3-
version = "0.14.0"
3+
version = "0.15.0"
44
edition = "2024"
55
authors = ["CppCXY"]
66
description = "A library for analyzing lua code."

crates/emmylua_code_analysis/resources/schema.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,11 @@
430430
"description": "readonly",
431431
"type": "string",
432432
"const": "read-only"
433+
},
434+
{
435+
"description": "Global variable defined in non-module scope",
436+
"type": "string",
437+
"const": "global-in-non-module"
433438
}
434439
]
435440
},
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
use emmylua_parser::{LuaAssignStat, LuaAst, LuaAstNode, LuaBlock, LuaVarExpr};
2+
3+
use crate::{DiagnosticCode, LuaDeclId, SemanticModel};
4+
5+
use super::{Checker, DiagnosticContext};
6+
7+
pub struct GlobalInNonModuleChecker;
8+
9+
impl Checker for GlobalInNonModuleChecker {
10+
const CODES: &[DiagnosticCode] = &[DiagnosticCode::GlobalInNonModule];
11+
12+
fn check(context: &mut DiagnosticContext, semantic_model: &SemanticModel) {
13+
let root = semantic_model.get_root().clone();
14+
for assign_stat in root.descendants::<LuaAssignStat>() {
15+
check_assign_stat(context, semantic_model, assign_stat);
16+
}
17+
}
18+
}
19+
20+
fn check_assign_stat(
21+
context: &mut DiagnosticContext,
22+
semantic_model: &SemanticModel,
23+
assign_stat: LuaAssignStat,
24+
) -> Option<()> {
25+
let file_id = semantic_model.get_file_id();
26+
27+
let (vars, _) = assign_stat.get_var_and_expr_list();
28+
for var in vars {
29+
let decl_id = LuaDeclId::new(file_id, var.get_position());
30+
if let Some(decl) = semantic_model.get_db().get_decl_index().get_decl(&decl_id)
31+
&& decl.is_global()
32+
&& is_global_define_in_non_module_scope(semantic_model, var.clone(), decl_id)
33+
{
34+
context.add_diagnostic(
35+
DiagnosticCode::GlobalInNonModule,
36+
var.get_range(),
37+
t!("Global variable should only be defined in module scope").to_string(),
38+
None,
39+
);
40+
}
41+
}
42+
43+
Some(())
44+
}
45+
46+
fn is_global_define_in_non_module_scope(
47+
semantic_model: &SemanticModel,
48+
var: LuaVarExpr,
49+
decl_id: LuaDeclId,
50+
) -> bool {
51+
for block in var.ancestors::<LuaBlock>() {
52+
let parent = block.get_parent::<LuaAst>();
53+
match parent {
54+
Some(LuaAst::LuaChunk(_)) => {
55+
return false;
56+
}
57+
Some(LuaAst::LuaClosureExpr(_)) => {
58+
break;
59+
}
60+
_ => {}
61+
}
62+
}
63+
64+
let name = var.get_text();
65+
let Some(global_id) = semantic_model
66+
.get_db()
67+
.get_global_index()
68+
.resolve_global_decl_id(semantic_model.get_db(), &name)
69+
else {
70+
return true;
71+
};
72+
73+
global_id == decl_id
74+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mod duplicate_require;
1717
mod duplicate_type;
1818
mod enum_value_mismatch;
1919
mod generic;
20+
mod global_in_non_module;
2021
mod incomplete_signature_doc;
2122
mod local_const_reassign;
2223
mod missing_fields;
@@ -120,6 +121,7 @@ pub fn check_file(context: &mut DiagnosticContext, semantic_model: &SemanticMode
120121
semantic_model,
121122
);
122123
run_check::<readonly_check::ReadOnlyChecker>(context, semantic_model);
124+
run_check::<global_in_non_module::GlobalInNonModuleChecker>(context, semantic_model);
123125
Some(())
124126
}
125127

crates/emmylua_code_analysis/src/diagnostic/lua_diagnostic_code.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ pub enum DiagnosticCode {
105105
PreferredLocalAlias,
106106
/// readonly
107107
ReadOnly,
108+
/// Global variable defined in non-module scope
109+
GlobalInNonModule,
108110

109111
#[serde(other)]
110112
None,
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#[cfg(test)]
2+
mod tests {
3+
use crate::{DiagnosticCode, VirtualWorkspace};
4+
5+
#[test]
6+
fn test_global_in_non_module() {
7+
let mut ws = VirtualWorkspace::new();
8+
assert!(!ws.check_code_for(
9+
DiagnosticCode::GlobalInNonModule,
10+
r#"
11+
local function name()
12+
bbbb = 123
13+
end
14+
"#
15+
));
16+
}
17+
}

0 commit comments

Comments
 (0)