Skip to content

Commit f7e213b

Browse files
Merge pull request #129 from code0-tech/76-missing-linter-flow-settings
Linter for FlowSettings
2 parents e73dfc5 + 55ad722 commit f7e213b

File tree

17 files changed

+855
-896
lines changed

17 files changed

+855
-896
lines changed

crates/cli/src/analyser/core.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
use crate::analyser::index_identifier::IdentifierIndex;
2+
use crate::diagnostics::diagnose::Diagnose;
3+
use crate::diagnostics::kinds::DiagnosticKind;
4+
use crate::diagnostics::reporter::Reporter;
5+
use crate::parser::Meta;
6+
use tucana::shared::{DefinitionDataType, FlowType, RuntimeFunctionDefinition};
7+
8+
#[derive(Clone)]
9+
pub struct AnalysableDataType {
10+
pub original_definition: Meta,
11+
pub definition_data_type: DefinitionDataType,
12+
pub id: i16,
13+
}
14+
15+
#[derive(Clone)]
16+
pub struct AnalysableFlowType {
17+
pub original_definition: Meta,
18+
pub flow_type: FlowType,
19+
pub id: i16,
20+
}
21+
22+
#[derive(Clone)]
23+
pub struct AnalysableFunction {
24+
pub original_definition: Meta,
25+
pub function: RuntimeFunctionDefinition,
26+
pub id: i16,
27+
}
28+
29+
pub struct Analyser {
30+
pub reporter: Reporter,
31+
pub(crate) index: IdentifierIndex,
32+
pub data_types: Vec<AnalysableDataType>,
33+
pub flow_types: Vec<AnalysableFlowType>,
34+
pub functions: Vec<AnalysableFunction>,
35+
}
36+
37+
impl Analyser {
38+
pub fn new(path: &str) -> Self {
39+
super::loader::load_from_path(path)
40+
}
41+
42+
pub fn report(&mut self, will_exit: bool) {
43+
// Run analysis passes
44+
for dt in self.data_types.clone() {
45+
self.analyse_data_type(&dt);
46+
}
47+
for ft in self.flow_types.clone() {
48+
self.analyse_flow_type(&ft);
49+
}
50+
for f in self.functions.clone() {
51+
self.analyse_runtime_function(&f);
52+
}
53+
self.reporter.print(will_exit);
54+
}
55+
56+
pub fn data_type_identifier_exists(&self, identifier: &str, except_id: Option<i16>) -> bool {
57+
self.index.has_data_type(identifier, except_id)
58+
}
59+
60+
pub fn generic_key_in_target(&self, key: &str, target: &str) -> bool {
61+
let norm_target = target.to_ascii_lowercase();
62+
self.data_types.iter().any(|dt| {
63+
dt.definition_data_type
64+
.identifier
65+
.eq_ignore_ascii_case(&norm_target)
66+
&& dt
67+
.definition_data_type
68+
.generic_keys
69+
.contains(&key.to_string())
70+
})
71+
}
72+
73+
pub fn null_field(&mut self, name: String, adt: &AnalysableDataType) {
74+
self.reporter.add(Diagnose::new(
75+
adt.definition_data_type.identifier.clone(),
76+
adt.original_definition.clone(),
77+
DiagnosticKind::NullField { field_name: name },
78+
));
79+
}
80+
}
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
use crate::analyser::core::{AnalysableDataType, Analyser};
2+
use crate::diagnostics::diagnose::Diagnose;
3+
use crate::diagnostics::kinds::DiagnosticKind;
4+
use tucana::shared::DataTypeIdentifier;
5+
use tucana::shared::data_type_identifier::Type;
6+
use tucana::shared::definition_data_type_rule::Config;
7+
8+
impl Analyser {
9+
pub fn analyse_data_type(&mut self, adt: &AnalysableDataType) {
10+
let dt = &adt.definition_data_type;
11+
if self.index.has_data_type(&dt.identifier, Some(adt.id)) {
12+
self.reporter.add(Diagnose::new(
13+
dt.identifier.clone(),
14+
adt.original_definition.clone(),
15+
DiagnosticKind::DuplicateDataTypeIdentifier {
16+
identifier: dt.identifier.clone(),
17+
},
18+
));
19+
}
20+
21+
if dt.variant == 0 {
22+
self.reporter.add(Diagnose::new(
23+
dt.identifier.clone(),
24+
adt.original_definition.clone(),
25+
DiagnosticKind::ForbiddenVariant,
26+
));
27+
}
28+
29+
let mut detected: Vec<String> = vec![];
30+
for optional_rule in &dt.rules {
31+
if let Some(config) = &optional_rule.config {
32+
match config {
33+
Config::ContainsKey(rule) => {
34+
if let Some(dti) = &rule.data_type_identifier {
35+
self.walk_data_type_identifier(adt, dti, &mut detected);
36+
} else {
37+
self.null_field("definition_data_type_contains_key_rule".into(), adt);
38+
}
39+
}
40+
Config::ContainsType(rule) => {
41+
if let Some(dti) = &rule.data_type_identifier {
42+
self.walk_data_type_identifier(adt, dti, &mut detected);
43+
} else {
44+
self.null_field("definition_data_type_contains_type_rule".into(), adt);
45+
}
46+
}
47+
Config::ItemOfCollection(rule) => {
48+
if rule.items.is_empty() {
49+
self.null_field(
50+
"definition_data_type_item_of_collection_rule".into(),
51+
adt,
52+
);
53+
}
54+
}
55+
Config::NumberRange(_) | Config::Regex(_) => {}
56+
Config::InputTypes(rule) => {
57+
if rule.input_types.is_empty() {
58+
self.null_field("definition_data_type_input_types_rule".into(), adt);
59+
}
60+
for input in &rule.input_types {
61+
if let Some(dti) = &input.data_type_identifier {
62+
self.walk_data_type_identifier(adt, dti, &mut detected);
63+
} else {
64+
self.reporter.add(Diagnose::new(
65+
dt.identifier.clone(),
66+
adt.original_definition.clone(),
67+
DiagnosticKind::UndefinedDataTypeIdentifier {
68+
identifier: dt.identifier.clone(),
69+
},
70+
));
71+
}
72+
}
73+
}
74+
Config::ReturnType(rule) => {
75+
if let Some(dti) = &rule.data_type_identifier {
76+
self.walk_data_type_identifier(adt, dti, &mut detected);
77+
} else {
78+
self.null_field("definition_data_type_return_type_rule".into(), adt);
79+
}
80+
}
81+
Config::ParentType(rule) => {
82+
if let Some(dti) = &rule.parent_type {
83+
self.walk_data_type_identifier(adt, dti, &mut detected);
84+
} else {
85+
self.null_field("definition_data_type_parent_type_rule".into(), adt);
86+
}
87+
}
88+
}
89+
} else {
90+
self.null_field("rule".into(), adt);
91+
}
92+
}
93+
94+
for key in dt.generic_keys.iter().filter(|k| !detected.contains(k)) {
95+
self.reporter.add(Diagnose::new(
96+
dt.identifier.clone(),
97+
adt.original_definition.clone(),
98+
DiagnosticKind::UnusedGenericKey { key: key.clone() },
99+
));
100+
}
101+
for key in detected
102+
.into_iter()
103+
.filter(|k| !dt.generic_keys.contains(k))
104+
{
105+
self.reporter.add(Diagnose::new(
106+
dt.identifier.clone(),
107+
adt.original_definition.clone(),
108+
DiagnosticKind::UndefinedGenericKey { key },
109+
));
110+
}
111+
112+
if dt.name.is_empty() {
113+
self.reporter.add(Diagnose::new(
114+
dt.identifier.clone(),
115+
adt.original_definition.clone(),
116+
DiagnosticKind::UndefinedTranslation {
117+
translation_field: "name".into(),
118+
},
119+
));
120+
}
121+
}
122+
123+
fn walk_data_type_identifier(
124+
&mut self,
125+
adt: &AnalysableDataType,
126+
dti: &DataTypeIdentifier,
127+
acc: &mut Vec<String>,
128+
) {
129+
if let Some(t) = &dti.r#type {
130+
match t {
131+
Type::DataTypeIdentifier(identifier) => {
132+
if !self.data_type_identifier_exists(identifier, Some(adt.id)) {
133+
self.reporter.add(Diagnose::new(
134+
adt.definition_data_type.identifier.clone(),
135+
adt.original_definition.clone(),
136+
DiagnosticKind::UndefinedDataTypeIdentifier {
137+
identifier: identifier.clone(),
138+
},
139+
));
140+
}
141+
}
142+
Type::GenericType(generic) => {
143+
if !self
144+
.data_type_identifier_exists(&generic.data_type_identifier, Some(adt.id))
145+
{
146+
self.reporter.add(Diagnose::new(
147+
adt.definition_data_type.identifier.clone(),
148+
adt.original_definition.clone(),
149+
DiagnosticKind::UndefinedDataTypeIdentifier {
150+
identifier: generic.data_type_identifier.clone(),
151+
},
152+
));
153+
}
154+
if generic.generic_mappers.is_empty() {
155+
self.reporter.add(Diagnose::new(
156+
adt.definition_data_type.identifier.clone(),
157+
adt.original_definition.clone(),
158+
DiagnosticKind::EmptyGenericMapper,
159+
));
160+
}
161+
for mapper in &generic.generic_mappers {
162+
if adt
163+
.definition_data_type
164+
.generic_keys
165+
.contains(&mapper.target)
166+
{
167+
acc.push(mapper.target.clone());
168+
}
169+
for source in &mapper.source {
170+
self.walk_data_type_identifier(adt, source, acc);
171+
}
172+
}
173+
}
174+
Type::GenericKey(key) => acc.push(key.clone()),
175+
}
176+
} else {
177+
self.null_field("data_type".into(), adt);
178+
}
179+
}
180+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
use crate::analyser::core::{AnalysableFlowType, Analyser};
2+
use crate::diagnostics::diagnose::Diagnose;
3+
use crate::diagnostics::kinds::DiagnosticKind;
4+
5+
impl Analyser {
6+
pub fn analyse_flow_type(&mut self, aft: &AnalysableFlowType) {
7+
let flow = &aft.flow_type;
8+
let name = flow.identifier.clone();
9+
let original = aft.original_definition.clone();
10+
11+
if flow.name.is_empty() {
12+
self.reporter.add(Diagnose::new(
13+
name.clone(),
14+
original.clone(),
15+
DiagnosticKind::UndefinedTranslation {
16+
translation_field: "name".into(),
17+
},
18+
));
19+
}
20+
if flow.description.is_empty() {
21+
self.reporter.add(Diagnose::new(
22+
name.clone(),
23+
original.clone(),
24+
DiagnosticKind::UndefinedTranslation {
25+
translation_field: "description".into(),
26+
},
27+
));
28+
}
29+
if flow.documentation.is_empty() {
30+
self.reporter.add(Diagnose::new(
31+
name.clone(),
32+
original.clone(),
33+
DiagnosticKind::UndefinedTranslation {
34+
translation_field: "documentation".into(),
35+
},
36+
));
37+
}
38+
39+
if let Some(identifier) = &flow.input_type_identifier {
40+
if !self.data_type_identifier_exists(identifier, None) {
41+
self.reporter.add(Diagnose::new(
42+
name.clone(),
43+
original.clone(),
44+
DiagnosticKind::UndefinedDataTypeIdentifier {
45+
identifier: identifier.clone(),
46+
},
47+
));
48+
}
49+
}
50+
if let Some(identifier) = &flow.return_type_identifier {
51+
if !self.data_type_identifier_exists(identifier, None) {
52+
self.reporter.add(Diagnose::new(
53+
name.clone(),
54+
original.clone(),
55+
DiagnosticKind::UndefinedDataTypeIdentifier {
56+
identifier: identifier.clone(),
57+
},
58+
));
59+
}
60+
}
61+
62+
for setting in &flow.settings {
63+
if setting.name.is_empty() {
64+
self.reporter.add(Diagnose::new(
65+
setting.identifier.clone(),
66+
original.clone(),
67+
DiagnosticKind::UndefinedTranslation {
68+
translation_field: "flow_setting.name".into(),
69+
},
70+
));
71+
}
72+
if setting.description.is_empty() {
73+
self.reporter.add(Diagnose::new(
74+
setting.identifier.clone(),
75+
original.clone(),
76+
DiagnosticKind::UndefinedTranslation {
77+
translation_field: "flow_setting.description".into(),
78+
},
79+
));
80+
}
81+
if !self.data_type_identifier_exists(&setting.data_type_identifier, None) {
82+
self.reporter.add(Diagnose::new(
83+
name.clone(),
84+
original.clone(),
85+
DiagnosticKind::UndefinedDataTypeIdentifier {
86+
identifier: setting.data_type_identifier.clone(),
87+
},
88+
));
89+
}
90+
}
91+
92+
if self.index.has_flow_type(&name, Some(aft.id)) {
93+
self.reporter.add(Diagnose::new(
94+
name.clone(),
95+
original.clone(),
96+
DiagnosticKind::DuplicateFlowTypeIdentifier { identifier: name },
97+
));
98+
}
99+
}
100+
}

0 commit comments

Comments
 (0)