Skip to content

Commit 26d7d0d

Browse files
authored
fix: improve unresolved reference validation in array access (#1432)
1 parent 2572529 commit 26d7d0d

File tree

3 files changed

+115
-3
lines changed

3 files changed

+115
-3
lines changed

src/resolver/tests/resolve_expressions_tests.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5786,3 +5786,43 @@ fn reference_to_alloca_nested_resolved() {
57865786
unreachable!("Must be an assignment");
57875787
}
57885788
}
5789+
5790+
#[test]
5791+
fn reference_inside_array_access_index_is_resolved() {
5792+
let id_provider = IdProvider::default();
5793+
let (unit, mut index) = index_with_ids(
5794+
"
5795+
FUNCTION main : DINT
5796+
VAR
5797+
arr : ARRAY[1..5] OF DINT;
5798+
idx : DINT;
5799+
END_VAR
5800+
5801+
arr[idx];
5802+
END_FUNCTION
5803+
",
5804+
id_provider.clone(),
5805+
);
5806+
5807+
let annotations = annotate_with_ids(&unit, &mut index, id_provider);
5808+
let stmt = &unit.implementations[0].statements[0];
5809+
if let AstNode {
5810+
stmt: AstStatement::ReferenceExpr(ReferenceExpr { access: ReferenceAccess::Index(idx), .. }),
5811+
..
5812+
} = stmt
5813+
{
5814+
insta::assert_debug_snapshot!(annotations.get(idx).unwrap(), @r###"
5815+
Variable {
5816+
resulting_type: "DINT",
5817+
qualified_name: "main.idx",
5818+
constant: false,
5819+
argument_type: ByVal(
5820+
Local,
5821+
),
5822+
auto_deref: None,
5823+
}
5824+
"###);
5825+
} else {
5826+
unreachable!("Expected a reference expression")
5827+
}
5828+
}

src/validation/statement.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ fn validate_access_index<T: AnnotationMap>(
436436
}
437437
}
438438
AstStatement::ReferenceExpr(_) => {
439-
let ref_type = context.annotations.get_type_or_void(access_index, context.index);
439+
let Some(ref_type) = context.annotations.get_type(access_index, context.index) else { return };
440440
if !ref_type.get_type_information().is_int() {
441441
validator.push_diagnostic(
442442
Diagnostic::new(format!("Invalid type {} for direct variable access. Only variables of Integer types are allowed", ref_type.get_name()))
@@ -557,7 +557,11 @@ fn visit_array_access<T: AnnotationMap>(
557557
access: &AstNode,
558558
context: &ValidationContext<T>,
559559
) {
560-
let target_type = context.annotations.get_type_or_void(reference, context.index).get_type_information();
560+
let Some(target_type) =
561+
context.annotations.get_type(reference, context.index).map(|it| it.get_type_information())
562+
else {
563+
return;
564+
};
561565

562566
match target_type {
563567
DataTypeInformation::Array { dimensions, .. } => match access.get_stmt() {
@@ -615,6 +619,7 @@ fn validate_array_access<T: AnnotationMap>(
615619
dimension_index: usize,
616620
context: &ValidationContext<T>,
617621
) {
622+
visit_statement(validator, access, context);
618623
if let AstStatement::Literal(AstLiteral::Integer(value)) = access.get_stmt() {
619624
if let Some(dimension) = dimensions.get(dimension_index) {
620625
if let Ok(range) = dimension.get_range(context.index) {
@@ -631,7 +636,11 @@ fn validate_array_access<T: AnnotationMap>(
631636
}
632637
}
633638
} else {
634-
let type_info = context.annotations.get_type_or_void(access, context.index).get_type_information();
639+
let Some(type_info) =
640+
context.annotations.get_type(access, context.index).map(|it| it.get_type_information())
641+
else {
642+
return;
643+
};
635644
if !type_info.is_int() {
636645
validator.push_diagnostic(
637646
Diagnostic::new(format!(

src/validation/tests/statement_validation_tests.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1997,3 +1997,66 @@ fn validate_property_call_with_braces() {
19971997
│ ^^^^^^^^^^^^^^^ Properties cannot be called like functions. Remove `()`
19981998
"###);
19991999
}
2000+
2001+
#[test]
2002+
fn unresolved_reference_in_array_access() {
2003+
let diagnostics = parse_and_validate_buffered(
2004+
"
2005+
PROGRAM mainProg
2006+
VAR
2007+
foo: ARRAY[0..1] OF DINT;
2008+
val: DINT;
2009+
END_VAR
2010+
// OK
2011+
foo[val];
2012+
// unresolved reference
2013+
foo[bar];
2014+
END_PROGRAM
2015+
",
2016+
);
2017+
2018+
assert_snapshot!(diagnostics, @r###"
2019+
error[E048]: Could not resolve reference to bar
2020+
┌─ <internal>:10:17
2021+
2022+
10 │ foo[bar];
2023+
│ ^^^ Could not resolve reference to bar
2024+
"###);
2025+
}
2026+
2027+
#[test]
2028+
fn unresolved_qualified_reference_in_array_access() {
2029+
let diagnostics = parse_and_validate_buffered(
2030+
"
2031+
FUNCTION_BLOCK fb
2032+
VAR
2033+
val: DINT;
2034+
END_VAR
2035+
END_FUNCTION_BLOCK
2036+
PROGRAM mainProg
2037+
VAR
2038+
foo: ARRAY[0..1] OF DINT;
2039+
instance: fb;
2040+
END_VAR
2041+
// OK
2042+
foo[fb.val];
2043+
// unresolved reference
2044+
foo[fb.bar];
2045+
END_PROGRAM
2046+
",
2047+
);
2048+
2049+
assert_snapshot!(diagnostics, @r###"
2050+
warning[E049]: Illegal access to private member fb.val
2051+
┌─ <internal>:13:20
2052+
2053+
13 │ foo[fb.val];
2054+
│ ^^^ Illegal access to private member fb.val
2055+
2056+
error[E048]: Could not resolve reference to bar
2057+
┌─ <internal>:15:20
2058+
2059+
15 │ foo[fb.bar];
2060+
│ ^^^ Could not resolve reference to bar
2061+
"###);
2062+
}

0 commit comments

Comments
 (0)