Skip to content

Commit 93dd7e1

Browse files
committed
add GlobalInNonModule cheker
Fix #740
1 parent 019c08f commit 93dd7e1

File tree

6 files changed

+101
-0
lines changed

6 files changed

+101
-0
lines changed

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+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ mod duplicate_index_test;
1010
mod duplicate_require_test;
1111
mod enum_value_mismatch_test;
1212
mod generic_constraint_mismatch_test;
13+
mod global_in_non_module_test;
1314
mod incomplete_signature_doc_test;
1415
mod inject_field_test;
1516
mod missing_fields_test;

0 commit comments

Comments
 (0)