diff --git a/libs/sqf/src/analyze/inspector/commands.rs b/libs/sqf/src/analyze/inspector/commands.rs index c8512ef0d..b70aedf67 100644 --- a/libs/sqf/src/analyze/inspector/commands.rs +++ b/libs/sqf/src/analyze/inspector/commands.rs @@ -167,16 +167,18 @@ impl Inspector { expected: var_types.iter().cloned().collect(), found: vec![default_value.clone()], span: element[1][0].1.clone(), - default: (element[2] - .first() - .map(|(_, s)| s.clone()) - .unwrap_or_default() - .start) - ..(element[2] - .last() + default: Some( + (element[2] + .first() .map(|(_, s)| s.clone()) .unwrap_or_default() - .end), + .start) + ..(element[2] + .last() + .map(|(_, s)| s.clone()) + .unwrap_or_default() + .end), + ), }); } var_types.insert(default_value); diff --git a/libs/sqf/src/analyze/inspector/game_value.rs b/libs/sqf/src/analyze/inspector/game_value.rs index e9a105de0..55c0440f9 100644 --- a/libs/sqf/src/analyze/inspector/game_value.rs +++ b/libs/sqf/src/analyze/inspector/game_value.rs @@ -4,6 +4,7 @@ use std::ops::Range; use arma3_wiki::model::{Arg, Call, Param, Value}; use indexmap::IndexSet; +#[allow(unused_imports)] use tracing::{trace, warn}; use crate::{Expression, parser::database::Database}; @@ -88,6 +89,8 @@ impl GameValue { let mut expected_lhs = IndexSet::new(); let mut expected_rhs = IndexSet::new(); + let mut lhs_matched_all = true; + let mut rhs_matched_all = true; for syntax in command.syntax() { // println!("syntax {:?}", syntax.call()); @@ -106,6 +109,7 @@ impl GameValue { rhs_arg, syntax.params(), ); + rhs_matched_all = rhs_matched_all && is_match; expected_rhs.extend(expected); is_match } @@ -125,6 +129,7 @@ impl GameValue { lhs_arg, syntax.params(), ); + lhs_matched_all = lhs_matched_all && is_match; expected_lhs.extend(expected); is_match }; @@ -135,6 +140,7 @@ impl GameValue { rhs_arg, syntax.params(), ); + rhs_matched_all = rhs_matched_all && is_match; expected_rhs.extend(expected); is_match }; @@ -144,9 +150,15 @@ impl GameValue { } } let value = &syntax.ret().0; - let game_value = Self::from_wiki_value(value); + let game_value = Self::from_wiki_value(value, NilSource::CommandReturn); // println!("match syntax {syntax:?}"); - return_types.insert(game_value); + return_types.extend(game_value); + } + if rhs_matched_all { + expected_rhs.clear(); + } + if lhs_matched_all { + expected_lhs.clear(); } // println!("lhs_set {lhs_set:?}"); // println!("rhs_set {rhs_set:?}"); @@ -169,43 +181,15 @@ impl GameValue { match arg { Arg::Item(name) => { let Some(param) = params.iter().find(|p| p.name() == name) else { - /// Varidic cmds which will be missing wiki param matches (only affects debug logging) - const WIKI_CMDS_IGNORE_MISSING_PARAM: &[&str] = &[ - "addResources", - "createTask", - "ctRemoveRows", - "format", - "formatText", - "getGraphValues", - "inAreaArray", - "inAreaArrayIndexes", - "insert", - "kbReact", - "kbTell", - "lineIntersects", - "lineIntersectsObjs", - "lineIntersectsSurfaces", - "lineIntersectsWith", - "param", - "params", - "ppEffectCreate", - "set3DENAttributes", - "set3DENMissionAttributes", - "setAttributes", - "setGroupId", - "setGroupIdGlobal", - "setPiPEffect", - "textLogFormat", - ]; if !WIKI_CMDS_IGNORE_MISSING_PARAM.contains(&cmd_name) { // warn!("cmd {cmd_name} - param {name} not found"); } return (true, IndexSet::new()); }; - let (is_match, expected_gv) = + let (is_match, expected_gvs) = Self::match_set_to_value(set, param.typ(), param.optional()); let mut expected = IndexSet::new(); - expected.insert(expected_gv); + expected.extend(expected_gvs); (is_match, expected) } Arg::Array(arg_array) => { @@ -214,6 +198,7 @@ impl GameValue { match s { Self::Anything | Self::Array(None, _) => true, Self::Array(Some(gv_array), _) => { + let mut expected_array_args = Vec::new(); // println!("{cmd_name}: array (gv: {}) expected (arg: {})", gv_array.len(), arg_array.len()); // note: some syntaxes take more than others for (index, arg) in arg_array.iter().enumerate() { @@ -224,7 +209,9 @@ impl GameValue { }; let (is_match, expected_gv) = Self::match_set_to_arg(cmd_name, &possible, arg, params); - expected.extend(expected_gv); + // Convert IndexSet to Vec<(GameValue, Range)> with dummy ranges. + expected_array_args + .push(expected_gv.into_iter().map(|gv| (gv, 0..0)).collect()); if !is_match { if let Arg::Array(args) = arg { // handle edge case on varidic cmds that take arrays (e.g. `createHashMapFromArray`) @@ -239,6 +226,7 @@ impl GameValue { } } // println!("array arg {index} no match {arg:?} in {s:?}"); + expected.insert(Self::Array(Some(expected_array_args), None)); return false; } } @@ -257,18 +245,22 @@ impl GameValue { #[must_use] /// checks if a set of `GameValues` matches a wiki Value (with optional flag) - /// returns the expected `GameValue` type + /// returns the expected `GameValue` types pub fn match_set_to_value( set: &IndexSet, right_wiki: &Value, optional: bool, - ) -> (bool, Self) { + ) -> (bool, IndexSet) { // println!("Checking {set:?} against {right_wiki:?} [O:{optional}]"); - let right = Self::from_wiki_value(right_wiki); + let right = Self::from_wiki_value(right_wiki, NilSource::CommandReturn); if optional && (set.is_empty() || set.iter().any(|gv| matches!(gv, Self::Nothing(_)))) { return (true, right); } - (set.iter().any(|gv| Self::match_values(gv, &right)), right) + ( + set.iter() + .any(|gv| right.iter().any(|r| Self::match_values(gv, r))), + right, + ) } #[must_use] @@ -292,12 +284,11 @@ impl GameValue { } std::mem::discriminant(left) == std::mem::discriminant(right) } - #[must_use] - /// Maps from Wiki:Value to Inspector:GameValue - pub fn from_wiki_value(value: &Value) -> Self { + /// Maps from `Wiki:Value` to Set of `GameValues` + pub fn from_wiki_value(value: &Value, nil_type: NilSource) -> IndexSet { match value { - Value::Anything | Value::EdenEntity => Self::Anything, + Value::Anything | Value::EdenEntity => IndexSet::from([Self::Anything]), Value::ArrayColor | Value::ArrayColorRgb | Value::ArrayColorRgba @@ -306,43 +297,51 @@ impl GameValue { | Value::ArrayUnknown | Value::ArrayUnsized { .. } | Value::Position // position is often too generic to match? - | Value::Waypoint => Self::Array(None, None), + | Value::Waypoint => IndexSet::from([Self::Array(None, None)]), // Value::Position3dAGLS => Self::Array(None, Some(ArrayType::PosAGLS)), - Value::Position3dAGLS | Value::Position3dAGL => Self::Array(None, Some(ArrayType::PosAGL)), // merge - Value::Position3dASL => Self::Array(None, Some(ArrayType::PosASL)), - Value::Position3DASLW => Self::Array(None, Some(ArrayType::PosASLW)), - Value::Position3dATL => Self::Array(None, Some(ArrayType::PosATL)), - Value::Boolean => Self::Boolean(None), - Value::Code => Self::Code(None), - Value::Config => Self::Config, - Value::Control => Self::Control, - Value::DiaryRecord => Self::DiaryRecord, - Value::Display => Self::Display, - Value::ForType => Self::ForType(None), - Value::Group => Self::Group, - Value::HashMapUnknown => Self::HashMap, - Value::IfType => Self::IfType, - Value::Location => Self::Location, - Value::Namespace => Self::Namespace, - Value::Nothing => Self::Nothing(NilSource::CommandReturn), - Value::Number => Self::Number(None), - Value::Object => Self::Object, - Value::ScriptHandle => Self::ScriptHandle, - Value::Side => Self::Side, - Value::String => Self::String(None), - Value::SwitchType => Self::SwitchType, - Value::StructuredText => Self::StructuredText, - Value::Task => Self::Task, - Value::TeamMember => Self::TeamMember, - Value::WhileType => Self::WhileType, - Value::WithType => Self::WithType, + Value::Position3dAGLS | Value::Position3dAGL => IndexSet::from([Self::Array(None, Some(ArrayType::PosAGL))]), // merge + Value::Position3dASL => IndexSet::from([Self::Array(None, Some(ArrayType::PosASL))]), + Value::Position3DASLW => IndexSet::from([Self::Array(None, Some(ArrayType::PosASLW))]), + Value::Position3dATL => IndexSet::from([Self::Array(None, Some(ArrayType::PosATL))]), + Value::Boolean => IndexSet::from([Self::Boolean(None)]), + Value::Code => IndexSet::from([Self::Code(None)]), + Value::Config => IndexSet::from([Self::Config]), + Value::Control => IndexSet::from([Self::Control]), + Value::DiaryRecord => IndexSet::from([Self::DiaryRecord]), + Value::Display => IndexSet::from([Self::Display]), + Value::ForType => IndexSet::from([Self::ForType(None)]), + Value::Group => IndexSet::from([Self::Group]), + Value::HashMapUnknown => IndexSet::from([Self::HashMap]), + Value::IfType => IndexSet::from([Self::IfType]), + Value::Location => IndexSet::from([Self::Location]), + Value::Namespace => IndexSet::from([Self::Namespace]), + Value::Nothing => IndexSet::from([Self::Nothing(nil_type)]), + Value::Number => IndexSet::from([Self::Number(None)]), + Value::Object => IndexSet::from([Self::Object]), + Value::ScriptHandle => IndexSet::from([Self::ScriptHandle]), + Value::Side => IndexSet::from([Self::Side]), + Value::String => IndexSet::from([Self::String(None)]), + Value::SwitchType => IndexSet::from([Self::SwitchType]), + Value::StructuredText => IndexSet::from([Self::StructuredText]), + Value::Task => IndexSet::from([Self::Task]), + Value::TeamMember => IndexSet::from([Self::TeamMember]), + Value::WhileType => IndexSet::from([Self::WhileType]), + Value::WithType => IndexSet::from([Self::WithType]), + Value::OneOf(vec) => { + let mut set = IndexSet::new(); + for (v, _) in vec { + set.extend(Self::from_wiki_value(v, nil_type.clone())); + } + set + } Value::Unknown => { + #[cfg(debug_assertions)] trace!("wiki has syntax with [unknown] type"); - Self::Anything + IndexSet::from([Self::Anything]) } _ => { warn!("wiki type [{value:?}] not matched"); - Self::Anything + IndexSet::from([Self::Anything]) } } } @@ -356,6 +355,7 @@ impl GameValue { Self::ForType(_) => Self::ForType(None), Self::Number(_) => Self::Number(None), Self::String(_) => Self::String(None), + Self::Nothing(_) => Self::Nothing(NilSource::Generic), _ => self.clone(), } } @@ -387,6 +387,38 @@ impl GameValue { } result.unwrap_or(Self::Anything) } + /// Converts a vector of `GameValues` to a string representation + /// if it only contains a single element which is an array, it will format the array contents + pub fn vec_to_string(vec: &[Self], depth: usize) -> String { + fn to_string_array(value: &GameValue, depth: usize) -> String { + if depth == 0 { + return format!("{value}"); + } + match value { + GameValue::Array(Some(vec), _) => { + let inner_types = vec.iter().map(|outer| { + let types = outer + .iter() + .map(|(gv, _)| to_string_array(gv, depth - 1)) + .collect::>(); + format!("({})", types.join(", ")) + }); + format!("{value}[{}]", inner_types.collect::>().join(", ")) + } + _ => format!("{value}"), + } + } + if vec.len() == 1 + && let Some(gv) = vec.first() + { + to_string_array(gv, depth) + } else { + vec.iter() + .map(Self::to_string) + .collect::>() + .join(", ") + } + } } impl std::fmt::Display for GameValue { @@ -398,10 +430,10 @@ impl std::fmt::Display for GameValue { f, "{}", match self { - Self::Anything => "Anything", + Self::Anything => "Any", Self::Array(_, _) => "Array", Self::Assignment => "Assignment", - Self::Boolean(_) => "Boolean", + Self::Boolean(_) => "Bool", Self::Code(_) => "Code", Self::Config => "Config", Self::Control => "Control", @@ -414,7 +446,7 @@ impl std::fmt::Display for GameValue { Self::Location => "Location", Self::Namespace => "Namespace", Self::Number(_) => "Number", - Self::Nothing(_) => "Nothing", + Self::Nothing(_) => "Nil", Self::Object => "Object", Self::ScriptHandle => "ScriptHandle", Self::Side => "Side", @@ -429,3 +461,32 @@ impl std::fmt::Display for GameValue { ) } } + +/// Varidic cmds which will be missing wiki param matches (only affects debug logging) +const WIKI_CMDS_IGNORE_MISSING_PARAM: &[&str] = &[ + "addResources", + "createTask", + "ctRemoveRows", + "format", + "formatText", + "getGraphValues", + "inAreaArray", + "inAreaArrayIndexes", + "insert", + "kbReact", + "kbTell", + "lineIntersects", + "lineIntersectsObjs", + "lineIntersectsSurfaces", + "lineIntersectsWith", + "param", + "params", + "ppEffectCreate", + "set3DENAttributes", + "set3DENMissionAttributes", + "setAttributes", + "setGroupId", + "setGroupIdGlobal", + "setPiPEffect", + "textLogFormat", +]; diff --git a/libs/sqf/src/analyze/inspector/issue.rs b/libs/sqf/src/analyze/inspector/issue.rs new file mode 100644 index 000000000..e14515a83 --- /dev/null +++ b/libs/sqf/src/analyze/inspector/issue.rs @@ -0,0 +1,159 @@ +use crate::analyze::inspector::{VarSource, game_value::GameValue}; +use std::{hash::Hash, ops::Range, vec}; + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub enum Issue { + InvalidArgs { + command: String, + span: Range, + variant: InvalidArgs, + }, + Undefined(String, Range, bool), + Unused(String, VarSource), + Shadowed(String, Range), + NotPrivate(String, Range), + CountArrayComparison(bool, Range, String), + InvalidReturnType { + variant: InvalidArgs, + }, +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub enum InvalidArgs { + TypeNotExpected { + expected: Vec, + found: Vec, + span: Range, + }, + DefaultDifferentType { + expected: Vec, + found: Vec, + span: Range, + default: Option>, + }, + NilResultUsed { + found: Vec, + span: Range, + }, + ExpectedDifferentTypeHeader { + expected: Vec, + found: Vec, + span: Range, + }, + InvalidReturnType { + expected: Vec, + found: Vec, + span: Range, + }, + FuncTypeNotExpected { + expected: Vec, + found: Vec, + span: Range, + }, +} + +impl InvalidArgs { + #[must_use] + pub fn note(&self) -> String { + let found = self.found_types(); + match self { + Self::TypeNotExpected { .. } | Self::FuncTypeNotExpected { .. } => { + format!( + "found {}\nexpected {}", + GameValue::vec_to_string(&found, 2), + GameValue::vec_to_string(&self.expected_types(), 2) + ) + } + _ => { + format!( + "found type{} was {}", + if self.found_types().len() > 1 { + "s" + } else { + "" + }, + GameValue::vec_to_string(&found, 2) + ) + } + } + } + + #[must_use] + pub fn message(&self, command: &str) -> String { + match self { + Self::TypeNotExpected { .. } | Self::FuncTypeNotExpected { .. } => { + format!("Invalid argument type for `{command}`") + } + Self::NilResultUsed { .. } => format!("Invalid argument (nil) for `{command}`"), + Self::DefaultDifferentType { default, .. } => { + if default.is_none() { + String::from( + "Default value is not an expected type for the parameter (from header)", + ) + } else { + String::from("Default value is not an expected type for the parameter") + } + } + Self::ExpectedDifferentTypeHeader { .. } => { + String::from("Expected value does not match (from Header)") + } + Self::InvalidReturnType { .. } => { + String::from("Invalid function return type (from Header)") + } + } + } + + #[must_use] + pub fn label_message(&self) -> String { + match self { + Self::NilResultUsed { .. } => String::from("expected non-nil value"), + Self::DefaultDifferentType { .. } + | Self::ExpectedDifferentTypeHeader { .. } + | Self::InvalidReturnType { .. } => { + format!( + "expected {}", + GameValue::vec_to_string(&self.expected_types(), 2) + ) + } + Self::TypeNotExpected { .. } | Self::FuncTypeNotExpected { .. } => { + String::from("type not expected") + } + } + } + + #[must_use] + pub fn found_types(&self) -> Vec { + match self { + Self::TypeNotExpected { found, .. } + | Self::DefaultDifferentType { found, .. } + | Self::ExpectedDifferentTypeHeader { found, .. } + | Self::NilResultUsed { found, .. } + | Self::InvalidReturnType { found, .. } + | Self::FuncTypeNotExpected { found, .. } => found.clone(), + } + } + + #[must_use] + pub fn expected_types(&self) -> Vec { + match self { + Self::NilResultUsed { .. } => vec![], + Self::TypeNotExpected { expected, .. } + | Self::DefaultDifferentType { expected, .. } + | Self::ExpectedDifferentTypeHeader { expected, .. } + | Self::InvalidReturnType { expected, .. } + | Self::FuncTypeNotExpected { expected, .. } => expected.clone(), + } + } + + #[must_use] + pub fn span(&self) -> Range { + match self { + Self::TypeNotExpected { span, .. } + | Self::DefaultDifferentType { span, .. } + | Self::ExpectedDifferentTypeHeader { span, .. } + | Self::InvalidReturnType { span, .. } + | Self::NilResultUsed { span, .. } + | Self::FuncTypeNotExpected { span, .. } => span.clone(), + } + } +} diff --git a/libs/sqf/src/analyze/inspector/mod.rs b/libs/sqf/src/analyze/inspector/mod.rs index 80ae0a27f..c20736aed 100644 --- a/libs/sqf/src/analyze/inspector/mod.rs +++ b/libs/sqf/src/analyze/inspector/mod.rs @@ -17,122 +17,8 @@ use tracing::warn; mod commands; mod external_functions; mod game_value; - -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub enum InvalidArgs { - TypeNotExpected { - expected: Vec, - found: Vec, - span: Range, - }, - DefaultDifferentType { - expected: Vec, - found: Vec, - span: Range, - default: Range, - }, - NilResultUsed { - found: Vec, - span: Range, - }, -} - -impl InvalidArgs { - #[must_use] - pub fn note(&self) -> String { - let found = self - .found_types() - .iter() - .map(GameValue::to_string) - .collect::>() - .join(", "); - format!( - "found type{} was {found}", - if self.found_types().len() > 1 { - "s" - } else { - "" - } - ) - } - - #[must_use] - pub fn message(&self, command: &str) -> String { - match self { - Self::TypeNotExpected { .. } => format!("Invalid argument type for `{command}`"), - Self::NilResultUsed { .. } => format!("Invalid argument (nil) for `{command}`"), - Self::DefaultDifferentType { .. } => { - String::from("Default value is not an expected type for the parameter") - } - } - } - - #[must_use] - pub fn label_message(&self) -> String { - match self { - Self::TypeNotExpected { .. } => format!( - "expected {}", - self.expected_types() - .iter() - .map(GameValue::to_string) - .collect::>() - .join(", ") - ), - Self::NilResultUsed { .. } => String::from("expected non-nil value"), - Self::DefaultDifferentType { .. } => { - format!( - "expected {}", - self.expected_types() - .iter() - .map(GameValue::to_string) - .collect::>() - .join(", ") - ) - } - } - } - - #[must_use] - pub fn found_types(&self) -> Vec { - match self { - Self::TypeNotExpected { found, .. } - | Self::DefaultDifferentType { found, .. } - | Self::NilResultUsed { found, .. } => found.clone(), - } - } - - #[must_use] - pub fn expected_types(&self) -> Vec { - match self { - Self::NilResultUsed { .. } => vec![], - Self::TypeNotExpected { expected, .. } - | Self::DefaultDifferentType { expected, .. } => expected.clone(), - } - } - - #[must_use] - pub fn span(&self) -> Range { - match self { - Self::TypeNotExpected { span, .. } - | Self::DefaultDifferentType { span, .. } - | Self::NilResultUsed { span, .. } => span.clone(), - } - } -} - -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub enum Issue { - InvalidArgs { - command: String, - span: Range, - variant: InvalidArgs, - }, - Undefined(String, Range, bool), - Unused(String, VarSource), - Shadowed(String, Range), - NotPrivate(String, Range), - CountArrayComparison(bool, Range, String), -} +mod issue; +pub use issue::{InvalidArgs, Issue}; #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub enum VarSource { @@ -583,9 +469,7 @@ impl Inspector { GameValue::from_cmd(expression, Some(&lhs_set), Some(&rhs_set), database); if cmd_set.is_empty() { // we must have invalid args - if expected_lhs.difference(&lhs_set).count() != 0 - && !expected_lhs.contains(&GameValue::Anything) - { + if !expected_lhs.is_empty() { self.errors.insert(Issue::InvalidArgs { command: debug_type.clone(), span: source.clone(), @@ -596,9 +480,7 @@ impl Inspector { }, }); } - if expected_rhs.difference(&rhs_set).count() != 0 - && !expected_rhs.contains(&GameValue::Anything) - { + if !expected_rhs.is_empty() { self.errors.insert(Issue::InvalidArgs { command: debug_type.clone(), span: source.clone(), diff --git a/libs/sqf/src/analyze/lints/s12_invalid_args.rs b/libs/sqf/src/analyze/lints/s12_invalid_args.rs index 8a24276d4..9320f3133 100644 --- a/libs/sqf/src/analyze/lints/s12_invalid_args.rs +++ b/libs/sqf/src/analyze/lints/s12_invalid_args.rs @@ -136,7 +136,7 @@ impl CodeS12InvalidArgs { fn generate_processed(mut self, processed: &Processed) -> Self { let diag = Diagnostic::from_code_processed(&self, self.variant.span(), processed); if let Some(mut diag) = diag { - if let InvalidArgs::DefaultDifferentType { default, .. } = &self.variant { + if let InvalidArgs::DefaultDifferentType { default: Some(default), .. } = &self.variant { let map = processed .mapping(default.start) .expect("mapping should exist"); diff --git a/libs/sqf/tests/snapshots/inspector__tests__main.snap b/libs/sqf/tests/snapshots/inspector__tests__main.snap index 745e64294..2ab6c669b 100644 --- a/libs/sqf/tests/snapshots/inspector__tests__main.snap +++ b/libs/sqf/tests/snapshots/inspector__tests__main.snap @@ -2,4 +2,4 @@ source: libs/sqf/tests/inspector.rs expression: "(issues.len(), issues)" --- -(23, [InvalidArgs { command: "[B:setFuel]", span: 31..38, variant: TypeNotExpected { expected: [Object], found: [Anything], span: 29..30 } }, InvalidArgs { command: "[B:setFuel]", span: 31..38, variant: TypeNotExpected { expected: [Number(None)], found: [Boolean(Some(Boolean(true, 39..43)))], span: 39..43 } }, Undefined("_test2", 46..52, false), NotPrivate("_test3", 113..119), Shadowed("_test5", 267..273), InvalidArgs { command: "[B:addPublicVariableEventHandler]", span: 319..348, variant: TypeNotExpected { expected: [String(None)], found: [Array(Some([]), None)], span: 316..318 } }, Undefined("_test8", 887..893, false), InvalidArgs { command: "[B:ctrlSetText]", span: 1098..1109, variant: TypeNotExpected { expected: [Control], found: [Anything], span: 1096..1097 } }, InvalidArgs { command: "[B:ctrlSetText]", span: 1098..1109, variant: TypeNotExpected { expected: [String(None)], found: [Object], span: 1110..1117 } }, Undefined("_test9", 1498..1504, false), Unused("_test10", ForLoop(1752..1761)), InvalidArgs { command: "[B:drawIcon]", span: 1992..2000, variant: TypeNotExpected { expected: [Control], found: [Anything, Config], span: 1983..1989 } }, InvalidArgs { command: "[B:setGusts]", span: 2434..2442, variant: TypeNotExpected { expected: [Number(None)], found: [Number(Some(Number(FloatOrd(60.0), 2431..2433)))], span: 2431..2433 } }, InvalidArgs { command: "[B:setGusts]", span: 2434..2442, variant: TypeNotExpected { expected: [Number(None)], found: [String(None), String(Some(String("abc", 2401..2406, DoubleQuote)))], span: 2443..2454 } }, Undefined("_test12", 2537..2544, false), CountArrayComparison(true, 2724..2742, "_test14"), InvalidArgs { command: "[U:case]", span: 3448..3452, variant: NilResultUsed { found: [Nothing(CommandReturn)], span: 3453..3460 } }, InvalidArgs { command: "[B:forEach]", span: 3512..3519, variant: TypeNotExpected { expected: [Array(None, None), HashMap], found: [Number(Some(Number(FloatOrd(5.0), 3504..3505)))], span: 3520..3527 } }, InvalidArgs { command: "[U:hashValue]", span: 3637..3646, variant: NilResultUsed { found: [Assignment], span: 3647..3654 } }, InvalidArgs { command: "[U:sin]", span: 3774..3777, variant: TypeNotExpected { expected: [Number(None)], found: [Object, Display], span: 3774..3777 } }, Unused("_test4", Assignment(223..229, 232..237)), Unused("_test11", Params(1802..1811)), Undefined("_test13", 2610..2617, true)]) +(21, [InvalidArgs { command: "[B:setFuel]", span: 31..38, variant: TypeNotExpected { expected: [Number(None)], found: [Boolean(Some(Boolean(true, 39..43)))], span: 39..43 } }, Undefined("_test2", 46..52, false), NotPrivate("_test3", 113..119), Shadowed("_test5", 267..273), InvalidArgs { command: "[B:addPublicVariableEventHandler]", span: 319..348, variant: TypeNotExpected { expected: [String(None)], found: [Array(Some([]), None)], span: 316..318 } }, InvalidArgs { command: "[B:addPublicVariableEventHandler]", span: 319..348, variant: TypeNotExpected { expected: [Anything, Array(None, None)], found: [Code(Some(Code(Statements { content: [], source: "}", span: 350..351, issues: [] })))], span: 350..351 } }, Undefined("_test8", 887..893, false), InvalidArgs { command: "[B:ctrlSetText]", span: 1098..1109, variant: TypeNotExpected { expected: [String(None)], found: [Object], span: 1110..1117 } }, Undefined("_test9", 1498..1504, false), Unused("_test10", ForLoop(1752..1761)), InvalidArgs { command: "[B:drawIcon]", span: 1992..2000, variant: TypeNotExpected { expected: [Array(Some([[(String(None), 0..0)], [(Array(None, None), 0..0)], [(Anything, 0..0)], [(Number(None), 0..0)], [(Number(None), 0..0)], [(Number(None), 0..0)], [(String(None), 0..0)]]), None)], found: [Array(Some([[(String(Some(String("#(rgb,1,1,1)color(1,1,1,1)", 2008..2036, DoubleQuote))), 2008..2036)], [(Array(Some([[(Number(Some(Number(FloatOrd(0.0), 2043..2044))), 2043..2044)], [(Number(Some(Number(FloatOrd(1.0), 2045..2046))), 2045..2046)], [(Number(Some(Number(FloatOrd(0.0), 2047..2048))), 2047..2048)], [(Number(Some(Number(FloatOrd(1.0), 2049..2050))), 2049..2050)]]), None), 2043..2050)], [(Object, 2057..2063)], [(Number(Some(Number(FloatOrd(0.0), 2069..2070))), 2069..2070)], [(Number(Some(Number(FloatOrd(0.0), 2076..2077))), 2076..2077)], [(Number(Some(Number(FloatOrd(0.0), 2083..2084))), 2083..2084)], [(Number(Some(Number(FloatOrd(5555.0), 2090..2094))), 2090..2094)]]), None)], span: 2007..2095 } }, InvalidArgs { command: "[B:setGusts]", span: 2434..2442, variant: TypeNotExpected { expected: [Number(None)], found: [String(None), String(Some(String("abc", 2401..2406, DoubleQuote)))], span: 2443..2454 } }, Undefined("_test12", 2537..2544, false), CountArrayComparison(true, 2724..2742, "_test14"), InvalidArgs { command: "[U:case]", span: 3448..3452, variant: NilResultUsed { found: [Nothing(CommandReturn)], span: 3453..3460 } }, InvalidArgs { command: "[B:forEach]", span: 3512..3519, variant: TypeNotExpected { expected: [Array(None, None), HashMap], found: [Number(Some(Number(FloatOrd(5.0), 3504..3505)))], span: 3520..3527 } }, InvalidArgs { command: "[U:hashValue]", span: 3637..3646, variant: NilResultUsed { found: [Assignment], span: 3647..3654 } }, InvalidArgs { command: "[U:sin]", span: 3774..3777, variant: TypeNotExpected { expected: [Number(None)], found: [Object, Display], span: 3774..3777 } }, Unused("_test4", Assignment(223..229, 232..237)), Unused("_test11", Params(1802..1811)), Undefined("_test13", 2610..2617, true)]) diff --git a/libs/sqf/tests/snapshots/inspector__tests__variadic.snap b/libs/sqf/tests/snapshots/inspector__tests__variadic.snap index eb15385c9..a05d3d275 100644 --- a/libs/sqf/tests/snapshots/inspector__tests__variadic.snap +++ b/libs/sqf/tests/snapshots/inspector__tests__variadic.snap @@ -2,4 +2,4 @@ source: libs/sqf/tests/inspector.rs expression: "(issues.len(), issues)" --- -(7, [InvalidArgs { command: "[U:createHashMapFromArray]", span: 684..706, variant: TypeNotExpected { expected: [Array(None, None)], found: [Object], span: 684..706 } }, InvalidArgs { command: "[U:params]", span: 1050..1056, variant: TypeNotExpected { expected: [String(None), Array(None, None)], found: [Object], span: 1067..1074 } }, InvalidArgs { command: "[U:params]", span: 1077..1083, variant: TypeNotExpected { expected: [Array(None, None)], found: [String(Some(String("", 1099..1101, DoubleQuote)))], span: 1099..1101 } }, InvalidArgs { command: "[U:params]", span: 1105..1111, variant: DefaultDifferentType { expected: [Boolean(None)], found: [Object], span: 1123..1130, default: 1133..1138 } }, Unused("_var1", Params(1058..1065)), Unused("_var2", Params(1086..1093)), Unused("_var3", Params(1114..1121))]) +(7, [InvalidArgs { command: "[U:createHashMapFromArray]", span: 684..706, variant: TypeNotExpected { expected: [Array(None, None)], found: [Object], span: 684..706 } }, InvalidArgs { command: "[U:params]", span: 1050..1056, variant: TypeNotExpected { expected: [String(None), Array(None, None)], found: [Object], span: 1067..1074 } }, InvalidArgs { command: "[U:params]", span: 1077..1083, variant: TypeNotExpected { expected: [Array(None, None)], found: [String(Some(String("", 1099..1101, DoubleQuote)))], span: 1099..1101 } }, InvalidArgs { command: "[U:params]", span: 1105..1111, variant: DefaultDifferentType { expected: [Boolean(None)], found: [Object], span: 1123..1130, default: Some(1133..1138) } }, Unused("_var1", Params(1058..1065)), Unused("_var2", Params(1086..1093)), Unused("_var3", Params(1114..1121))]) diff --git a/libs/sqf/tests/snapshots/lints__simple_s12_invalid_args.snap b/libs/sqf/tests/snapshots/lints__simple_s12_invalid_args.snap index 517b7a008..f489d5ed9 100644 --- a/libs/sqf/tests/snapshots/lints__simple_s12_invalid_args.snap +++ b/libs/sqf/tests/snapshots/lints__simple_s12_invalid_args.snap @@ -8,7 +8,7 @@ expression: "lint(stringify! (s12_invalid_args), false).0" 7 │ params [["_var3", objNull, [false, 0]]]; │ ^^^^^^^ -------- expected types │ │ - │ expected Boolean, Number + │ expected Bool, Number │ = note: found type was Object @@ -17,33 +17,37 @@ expression: "lint(stringify! (s12_invalid_args), false).0" ┌─ s12_invalid_args.sqf:3:26 │ 3 │ (vehicle player) setFuel true; // bad args: takes number 0-1 - │ ^^^^ expected Number + │ ^^^^ type not expected │ - = note: found type was Boolean + = note: found Bool + expected Number warning[L-S12]: Invalid argument type for `[B:setVariable]` ┌─ s12_invalid_args.sqf:9:20 │ 9 │ player setVariable 0; - │ ^ expected Array + │ ^ type not expected │ - = note: found type was Number + = note: found Number + expected Array warning[L-S12]: Invalid argument type for `[U:params]` ┌─ s12_invalid_args.sqf:5:18 │ 5 │ params ["_var1", objNull]; - │ ^^^^^^^ expected String, Array + │ ^^^^^^^ type not expected │ - = note: found type was Object + = note: found Object + expected String, Array warning[L-S12]: Invalid argument type for `[U:params]` ┌─ s12_invalid_args.sqf:6:23 │ 6 │ params [["_var2", "", ""]]; - │ ^^ expected Array + │ ^^ type not expected │ - = note: found type was String + = note: found String + expected Array