Skip to content

Commit 6505671

Browse files
committed
basic support readonly
1 parent 3ac5779 commit 6505671

File tree

8 files changed

+161
-3
lines changed

8 files changed

+161
-3
lines changed

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

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{
22
AsyncState, LuaDeclId, LuaExport, LuaExportScope, LuaNoDiscard, LuaSemanticDeclId,
3-
LuaSignatureId,
3+
LuaSignatureId, PropertyDeclFeature,
44
};
55

66
use super::{
@@ -10,8 +10,8 @@ use super::{
1010
use crate::compilation::analyzer::doc::tags::report_orphan_tag;
1111
use emmylua_parser::{
1212
LuaAst, LuaAstNode, LuaDocDescriptionOwner, LuaDocTagAsync, LuaDocTagDeprecated,
13-
LuaDocTagExport, LuaDocTagNodiscard, LuaDocTagSource, LuaDocTagVersion, LuaDocTagVisibility,
14-
LuaTableExpr,
13+
LuaDocTagExport, LuaDocTagNodiscard, LuaDocTagReadonly, LuaDocTagSource, LuaDocTagVersion,
14+
LuaDocTagVisibility, LuaTableExpr,
1515
};
1616

1717
pub fn analyze_visibility(
@@ -151,3 +151,15 @@ pub fn analyze_export(analyzer: &mut DocAnalyzer, tag: LuaDocTagExport) -> Optio
151151

152152
Some(())
153153
}
154+
155+
pub fn analyze_readonly(analyzer: &mut DocAnalyzer, readonly: LuaDocTagReadonly) -> Option<()> {
156+
let owner_id = get_owner_id_or_report(analyzer, &readonly)?;
157+
158+
analyzer.db.get_property_index_mut().add_decl_feature(
159+
analyzer.file_id,
160+
owner_id,
161+
PropertyDeclFeature::ReadOnly,
162+
);
163+
164+
Some(())
165+
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use emmylua_parser::{
44

55
use crate::{
66
AnalyzeError, DiagnosticCode, LuaDeclId,
7+
compilation::analyzer::doc::property_tags::analyze_readonly,
78
db_index::{LuaMemberId, LuaSemanticDeclId, LuaSignatureId},
89
};
910

@@ -107,6 +108,9 @@ pub fn analyze_tag(analyzer: &mut DocAnalyzer, tag: LuaDocTag) -> Option<()> {
107108
LuaDocTag::Export(export) => {
108109
analyze_export(analyzer, export)?;
109110
}
111+
LuaDocTag::Readonly(readonly) => {
112+
analyze_readonly(analyzer, readonly)?;
113+
}
110114
_ => {}
111115
}
112116

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2+
#[repr(u32)]
3+
pub enum PropertyDeclFeature {
4+
ReadOnly = 1 << 0,
5+
}
6+
7+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8+
pub struct DeclFeatureFlag(u32);
9+
10+
impl DeclFeatureFlag {
11+
pub fn new() -> Self {
12+
Self(0)
13+
}
14+
15+
pub fn add_feature(&mut self, feature: PropertyDeclFeature) {
16+
self.0 |= feature as u32;
17+
}
18+
19+
pub fn has_feature(&self, feature: PropertyDeclFeature) -> bool {
20+
(self.0 & (feature as u32)) != 0
21+
}
22+
}

crates/emmylua_code_analysis/src/db_index/property/mod.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
mod decl_feature;
12
#[allow(clippy::module_inception)]
23
mod property;
34

45
use std::collections::{HashMap, HashSet};
56

7+
pub use decl_feature::{DeclFeatureFlag, PropertyDeclFeature};
68
use emmylua_parser::{LuaAstNode, LuaDocTagField, LuaDocType, LuaVersionCondition, VisibilityKind};
79
pub use property::LuaCommonProperty;
810
pub use property::{LuaDeprecated, LuaExport, LuaExportScope, LuaPropertyId};
@@ -215,6 +217,23 @@ impl LuaPropertyIndex {
215217
Some(())
216218
}
217219

220+
pub fn add_decl_feature(
221+
&mut self,
222+
file_id: FileId,
223+
owner_id: LuaSemanticDeclId,
224+
feature: PropertyDeclFeature,
225+
) -> Option<()> {
226+
let (property, _) = self.get_or_create_property(owner_id.clone())?;
227+
property.add_decl_feature(feature);
228+
229+
self.in_filed_owner
230+
.entry(file_id)
231+
.or_default()
232+
.insert(owner_id);
233+
234+
Some(())
235+
}
236+
218237
pub fn get_property(&self, owner_id: &LuaSemanticDeclId) -> Option<&LuaCommonProperty> {
219238
self.property_owners_map
220239
.get(owner_id)

crates/emmylua_code_analysis/src/db_index/property/property.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
use emmylua_parser::{LuaVersionCondition, VisibilityKind};
22

3+
use crate::db_index::property::decl_feature::{DeclFeatureFlag, PropertyDeclFeature};
4+
35
#[derive(Debug, Clone, PartialEq, Eq)]
46
pub struct LuaCommonProperty {
57
pub visibility: VisibilityKind,
@@ -9,6 +11,7 @@ pub struct LuaCommonProperty {
911
pub version_conds: Option<Box<Vec<LuaVersionCondition>>>,
1012
pub tag_content: Option<Box<LuaTagContent>>,
1113
pub export: Option<LuaExport>,
14+
pub decl_features: DeclFeatureFlag,
1215
}
1316

1417
impl Default for LuaCommonProperty {
@@ -27,6 +30,7 @@ impl LuaCommonProperty {
2730
version_conds: None,
2831
tag_content: None,
2932
export: None,
33+
decl_features: DeclFeatureFlag::new(),
3034
}
3135
}
3236

@@ -82,6 +86,10 @@ impl LuaCommonProperty {
8286
pub fn add_extra_export(&mut self, export: LuaExport) {
8387
self.export = Some(export);
8488
}
89+
90+
pub fn add_decl_feature(&mut self, feature: PropertyDeclFeature) {
91+
self.decl_features.add_feature(feature);
92+
}
8593
}
8694

8795
#[derive(Debug, Clone, PartialEq, Eq)]

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ mod local_const_reassign;
2222
mod missing_fields;
2323
mod need_check_nil;
2424
mod param_type_check;
25+
mod readonly_check;
2526
mod redefined_local;
2627
mod require_module_visibility;
2728
mod return_type_mismatch;
@@ -118,6 +119,7 @@ pub fn check_file(context: &mut DiagnosticContext, semantic_model: &SemanticMode
118119
context,
119120
semantic_model,
120121
);
122+
run_check::<readonly_check::ReadOnlyChecker>(context, semantic_model);
121123
Some(())
122124
}
123125

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use emmylua_parser::{LuaAssignStat, LuaAst, LuaAstNode, LuaVarExpr};
2+
use rowan::{NodeOrToken, TextRange};
3+
4+
use crate::{
5+
DiagnosticCode, LuaSemanticDeclId, PropertyDeclFeature, SemanticDeclLevel, SemanticModel,
6+
};
7+
8+
use super::{Checker, DiagnosticContext};
9+
10+
pub struct ReadOnlyChecker;
11+
12+
impl Checker for ReadOnlyChecker {
13+
const CODES: &[DiagnosticCode] = &[DiagnosticCode::ReadOnly];
14+
15+
fn check(context: &mut DiagnosticContext, semantic_model: &SemanticModel) {
16+
let root = semantic_model.get_root().clone();
17+
for ast_node in root.descendants::<LuaAst>() {
18+
match ast_node {
19+
LuaAst::LuaAssignStat(assign_stat) => {
20+
check_assignt_stat(context, semantic_model, &assign_stat);
21+
}
22+
// need check?
23+
LuaAst::LuaFuncStat(_) => {}
24+
// we need known function is readonly
25+
LuaAst::LuaCallExpr(_) => {}
26+
_ => {}
27+
}
28+
}
29+
}
30+
}
31+
32+
fn check_and_report_semantic_id(
33+
context: &mut DiagnosticContext,
34+
range: TextRange,
35+
semantic_decl_id: LuaSemanticDeclId,
36+
) -> Option<()> {
37+
// TODO filter self
38+
let property_index = context.db.get_property_index();
39+
if let Some(property) = property_index.get_property(&semantic_decl_id) {
40+
if property
41+
.decl_features
42+
.has_feature(PropertyDeclFeature::ReadOnly)
43+
{
44+
context.add_diagnostic(
45+
DiagnosticCode::ReadOnly,
46+
range,
47+
t!("The variable is marked as readonly and cannot be assigned to.").to_string(),
48+
None,
49+
);
50+
}
51+
}
52+
53+
Some(())
54+
}
55+
56+
fn check_assignt_stat(
57+
context: &mut DiagnosticContext,
58+
semantic_model: &SemanticModel,
59+
assign_stat: &LuaAssignStat,
60+
) -> Option<()> {
61+
let (vars, _) = assign_stat.get_var_and_expr_list();
62+
for var in vars {
63+
let node_or_token = NodeOrToken::Node(var.syntax().clone());
64+
let semantic_decl_id =
65+
semantic_model.find_decl(node_or_token, SemanticDeclLevel::default());
66+
if let Some(semantic_decl_id) = semantic_decl_id {
67+
check_and_report_semantic_id(context, var.get_range(), semantic_decl_id);
68+
}
69+
70+
match var {
71+
LuaVarExpr::IndexExpr(index_expr) => {
72+
let prefix_node = index_expr.get_prefix_expr()?;
73+
let node_or_token = NodeOrToken::Node(prefix_node.syntax().clone());
74+
let semantic_decl_id =
75+
semantic_model.find_decl(node_or_token, SemanticDeclLevel::default());
76+
if let Some(semantic_decl_id) = semantic_decl_id {
77+
check_and_report_semantic_id(
78+
context,
79+
prefix_node.get_range(),
80+
semantic_decl_id,
81+
);
82+
}
83+
}
84+
_ => {}
85+
}
86+
}
87+
88+
Some(())
89+
}

crates/emmylua_code_analysis/src/diagnostic/lua_diagnostic_code.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ pub enum DiagnosticCode {
103103
EnumValueMismatch,
104104
/// preferred-local-alias
105105
PreferredLocalAlias,
106+
/// readonly
107+
ReadOnly,
106108

107109
#[serde(other)]
108110
None,

0 commit comments

Comments
 (0)