Skip to content

Commit db858bf

Browse files
authored
Merge pull request #2851 from mvanhorn/fix/2543-for-bare-expression-error
fix(analyzer): report an error for a bare for-loop range
2 parents 75cf640 + 95dbbcc commit db858bf

5 files changed

Lines changed: 128 additions & 2 deletions

File tree

crates/analyzer/src/analyzer_error.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,21 @@ pub enum AnalyzerError {
707707
token_source: TokenSource,
708708
},
709709

710+
#[diagnostic(
711+
severity(Error),
712+
code(invalid_for_range),
713+
help("use a range with `..` or `..=`, e.g. `0..N`"),
714+
url("https://doc.veryl-lang.org/book/07_appendix/02_semantic_error.html#{}", self.code().unwrap())
715+
)]
716+
#[error("for-loop range must use `..` or `..=`; a bare expression is not a valid range")]
717+
InvalidForRange {
718+
#[source_code]
719+
input: MultiSources,
720+
#[label("Error location")]
721+
error_location: SourceSpan,
722+
token_source: TokenSource,
723+
},
724+
710725
#[diagnostic(
711726
severity(Error),
712727
code(invalid_for_step),
@@ -1982,6 +1997,7 @@ impl AnalyzerError {
19821997
AnalyzerError::InvalidEmbedIdentifier { input, .. } => input,
19831998
AnalyzerError::InvalidEnumVariant { input, .. } => input,
19841999
AnalyzerError::InvalidFactor { input, .. } => input,
2000+
AnalyzerError::InvalidForRange { input, .. } => input,
19852001
AnalyzerError::InvalidForStep { input, .. } => input,
19862002
AnalyzerError::InvalidIdentifier { input, .. } => input,
19872003
AnalyzerError::InvalidImport { input, .. } => input,
@@ -2109,6 +2125,7 @@ impl AnalyzerError {
21092125
AnalyzerError::InvalidRangeAssign { token_source, .. } => *token_source,
21102126
AnalyzerError::NonConstantSelectWidth { token_source, .. } => *token_source,
21112127
AnalyzerError::InvalidStatement { token_source, .. } => *token_source,
2128+
AnalyzerError::InvalidForRange { token_source, .. } => *token_source,
21122129
AnalyzerError::InvalidForStep { token_source, .. } => *token_source,
21132130
AnalyzerError::InvalidTbUsage { token_source, .. } => *token_source,
21142131
AnalyzerError::MissingTbPort { token_source, .. } => *token_source,
@@ -2564,6 +2581,13 @@ impl AnalyzerError {
25642581
token_source: token.source(),
25652582
}
25662583
}
2584+
pub fn invalid_for_range(token: &TokenRange) -> Self {
2585+
AnalyzerError::InvalidForRange {
2586+
input: source(token),
2587+
error_location: token.into(),
2588+
token_source: token.source(),
2589+
}
2590+
}
25672591
pub fn invalid_for_step(cause: InvalidForStepKind, token: &TokenRange) -> Self {
25682592
AnalyzerError::InvalidForStep {
25692593
cause,

crates/analyzer/src/handlers/check_statement.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::analyzer_error::AnalyzerError;
22
use crate::attribute::Attribute;
33
use crate::attribute_table;
44
use veryl_parser::ParolError;
5+
use veryl_parser::token_range::TokenRange;
56
use veryl_parser::veryl_grammar_trait::*;
67
use veryl_parser::veryl_walker::{Handler, HandlerPoint};
78

@@ -116,14 +117,30 @@ impl VerylGrammarTrait for CheckStatement {
116117
Ok(())
117118
}
118119

119-
fn for_statement(&mut self, _arg: &ForStatement) -> Result<(), ParolError> {
120+
fn for_statement(&mut self, arg: &ForStatement) -> Result<(), ParolError> {
120121
match self.point {
121-
HandlerPoint::Before => self.statement_depth_in_loop += 1,
122+
HandlerPoint::Before => {
123+
self.statement_depth_in_loop += 1;
124+
if arg.range.range_opt.is_none() {
125+
let token: TokenRange = arg.range.as_ref().into();
126+
self.errors.push(AnalyzerError::invalid_for_range(&token));
127+
}
128+
}
122129
HandlerPoint::After => self.statement_depth_in_loop -= 1,
123130
}
124131
Ok(())
125132
}
126133

134+
fn generate_for_declaration(&mut self, arg: &GenerateForDeclaration) -> Result<(), ParolError> {
135+
if let HandlerPoint::Before = self.point
136+
&& arg.range.range_opt.is_none()
137+
{
138+
let token: TokenRange = arg.range.as_ref().into();
139+
self.errors.push(AnalyzerError::invalid_for_range(&token));
140+
}
141+
Ok(())
142+
}
143+
127144
fn always_ff_declaration(&mut self, _arg: &AlwaysFfDeclaration) -> Result<(), ParolError> {
128145
match self.point {
129146
HandlerPoint::Before => {

crates/analyzer/src/tests.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1898,6 +1898,67 @@ fn invalid_statement() {
18981898
assert!(matches!(errors[0], AnalyzerError::InvalidStatement { .. }));
18991899
}
19001900

1901+
#[test]
1902+
fn invalid_for_range() {
1903+
let code = r#"
1904+
module ModuleA {
1905+
always_comb {
1906+
for i in 4 {
1907+
}
1908+
}
1909+
}
1910+
"#;
1911+
1912+
let errors = analyze(code);
1913+
assert!(
1914+
errors
1915+
.iter()
1916+
.any(|e| matches!(e, AnalyzerError::InvalidForRange { .. })),
1917+
"{errors:?}"
1918+
);
1919+
1920+
let code = r#"
1921+
module ModuleA {
1922+
for i in 4 :blk {
1923+
}
1924+
}
1925+
"#;
1926+
1927+
let errors = analyze(code);
1928+
assert!(
1929+
errors
1930+
.iter()
1931+
.any(|e| matches!(e, AnalyzerError::InvalidForRange { .. })),
1932+
"{errors:?}"
1933+
);
1934+
1935+
let code = r#"
1936+
module ModuleA (
1937+
o: output logic<32>,
1938+
) {
1939+
always_comb {
1940+
var acc: logic<32>;
1941+
acc = 0;
1942+
for i in 0..4 {
1943+
acc += i;
1944+
}
1945+
for j in 0..=4 {
1946+
acc += j;
1947+
}
1948+
o = acc;
1949+
}
1950+
}
1951+
"#;
1952+
1953+
let errors = analyze(code);
1954+
assert!(
1955+
!errors
1956+
.iter()
1957+
.any(|e| matches!(e, AnalyzerError::InvalidForRange { .. })),
1958+
"{errors:?}"
1959+
);
1960+
}
1961+
19011962
#[test]
19021963
fn invalid_modport_item() {
19031964
let code = r#"
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
source: crates/tests/src/lib.rs
3+
expression: out
4+
---
5+
invalid_for_range (https://doc.veryl-lang.org/book/07_appendix/02_semantic_error.html#invalid_for_range)
6+
7+
× for-loop range must use `..` or `..=`; a bare expression is not a valid range
8+
╭─[../../testcases/error/invalid_for_range.veryl:5:18]
9+
4a = 0;
10+
5for i in 4 {
11+
· ┬
12+
· ╰── Error location
13+
6 │ a += i;
14+
╰────
15+
help: use a range with `..` or `..=`, e.g. `0..N`
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
module invalid_for_range {
2+
var a: logic<32>;
3+
always_comb {
4+
a = 0;
5+
for i in 4 {
6+
a += i;
7+
}
8+
}
9+
}

0 commit comments

Comments
 (0)