Skip to content

Commit

Permalink
introduce separator checker
Browse files Browse the repository at this point in the history
(refs: #702)
  • Loading branch information
taichi-ishitani committed Dec 9, 2024
1 parent a72bc2a commit 5f0e6a1
Show file tree
Hide file tree
Showing 5 changed files with 221 additions and 0 deletions.
26 changes: 26 additions & 0 deletions crates/analyzer/src/analyzer_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,22 @@ pub enum AnalyzerError {
#[label("Error location")]
error_location: SourceSpan,
},

#[diagnostic(
severity(Error),
code(wrong_seperator),
help("replace valid separator \"{valid_separator}\""),
url("")
)]
#[error("separator \"{separator}\" can't be used at here")]
WrongSeparator {
separator: String,
valid_separator: String,
#[source_code]
input: NamedSource<String>,
#[label("Error location")]
error_location: SourceSpan,
},
}

impl AnalyzerError {
Expand Down Expand Up @@ -1678,4 +1694,14 @@ impl AnalyzerError {
error_location: token.into(),
}
}

pub fn wrong_seperator(separator: &str, source: &str, token: &TokenRange) -> Self {
let valid_separator = if separator == "." { "::" } else { "." };
AnalyzerError::WrongSeparator {
separator: separator.to_string(),
valid_separator: valid_separator.to_string(),
input: AnalyzerError::named_source(source, token),
error_location: token.into(),
}
}
}
6 changes: 6 additions & 0 deletions crates/analyzer/src/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub mod check_modport;
pub mod check_msb_lsb;
pub mod check_number;
pub mod check_proto;
pub mod check_separator;
pub mod check_statement;
pub mod check_type;
pub mod check_unsafe;
Expand All @@ -31,6 +32,7 @@ use check_modport::*;
use check_msb_lsb::*;
use check_number::*;
use check_proto::*;
use check_separator::*;
use check_statement::*;
use check_type::*;
use check_unsafe::*;
Expand Down Expand Up @@ -97,6 +99,7 @@ impl<'a> Pass1Handlers<'a> {
}

pub struct Pass2Handlers<'a> {
check_separator: CheckSeparator<'a>,
check_enum: CheckEnum<'a>,
check_modport: CheckModport<'a>,
check_function: CheckFunction<'a>,
Expand All @@ -114,6 +117,7 @@ pub struct Pass2Handlers<'a> {
impl<'a> Pass2Handlers<'a> {
pub fn new(text: &'a str, _build_opt: &'a Build, _lint_opt: &'a Lint) -> Self {
Self {
check_separator: CheckSeparator::new(text),
check_enum: CheckEnum::new(text),
check_modport: CheckModport::new(text),
check_function: CheckFunction::new(text),
Expand All @@ -131,6 +135,7 @@ impl<'a> Pass2Handlers<'a> {

pub fn get_handlers(&mut self) -> Vec<&mut dyn Handler> {
vec![
&mut self.check_separator as &mut dyn Handler,
&mut self.check_enum as &mut dyn Handler,
&mut self.check_modport as &mut dyn Handler,
&mut self.check_function as &mut dyn Handler,
Expand All @@ -148,6 +153,7 @@ impl<'a> Pass2Handlers<'a> {

pub fn get_errors(&mut self) -> Vec<AnalyzerError> {
let mut ret = Vec::new();
ret.append(&mut self.check_separator.errors);
ret.append(&mut self.check_enum.errors);
ret.append(&mut self.check_modport.errors);
ret.append(&mut self.check_function.errors);
Expand Down
95 changes: 95 additions & 0 deletions crates/analyzer/src/handlers/check_separator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use crate::analyzer_error::AnalyzerError;
use crate::symbol::{SymbolId, SymbolKind};
use crate::symbol_table;
use veryl_parser::veryl_grammar_trait::*;
use veryl_parser::veryl_token::Token;
use veryl_parser::veryl_walker::{Handler, HandlerPoint};
use veryl_parser::ParolError;

#[derive(Default)]
pub struct CheckSeparator<'a> {
pub errors: Vec<AnalyzerError>,
text: &'a str,
point: HandlerPoint,
}

impl<'a> CheckSeparator<'a> {
pub fn new(text: &'a str) -> Self {
Self {
text,
..Default::default()
}
}

fn check_separator(
&mut self,
full_path: &mut Vec<SymbolId>,
check_dot_separator: bool,
separator_token: &Token,
) {
let preceed_symbol = if let Some(symbol_id) = full_path.pop() {
symbol_table::get(symbol_id).unwrap()
} else {
return;
};
let this_symbol = if let Some(symbol_id) = full_path.last() {
symbol_table::get(*symbol_id).unwrap()
} else {
// length of `full_path` may be shorter than length of actual path
// if the type of the preceed symbol is defined in the SV namespace.
return;
};
let expect_dot_separator = if let SymbolKind::Function(_) = this_symbol.kind {
matches!(
preceed_symbol.kind,
SymbolKind::Instance(_) // member function of interface
)
} else {
matches!(
this_symbol.kind,
SymbolKind::Variable(_) // member variable of instance
| SymbolKind::StructMember(_)
| SymbolKind::UnionMember(_)
| SymbolKind::ModportVariableMember(_)
| SymbolKind::ModportFunctionMember(_)
)
};

if expect_dot_separator != check_dot_separator {
self.errors.push(AnalyzerError::wrong_seperator(
&separator_token.to_string(),
self.text,
&separator_token.into(),
));
}
}
}

impl Handler for CheckSeparator<'_> {
fn set_point(&mut self, p: HandlerPoint) {
self.point = p;
}
}

impl VerylGrammarTrait for CheckSeparator<'_> {
fn expression_identifier(&mut self, arg: &ExpressionIdentifier) -> Result<(), ParolError> {
if let HandlerPoint::Before = self.point {
if let Ok(symbol) = symbol_table::resolve(arg) {
let mut full_path: Vec<_> = symbol.full_path.into_iter().rev().collect();

for x in &arg.scoped_identifier.scoped_identifier_list {
self.check_separator(
&mut full_path,
false,
&x.colon_colon.colon_colon_token.token,
);
}

for x in &arg.expression_identifier_list0 {
self.check_separator(&mut full_path, true, &x.dot.dot_token.token);
}
}
}
Ok(())
}
}
90 changes: 90 additions & 0 deletions crates/analyzer/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2792,3 +2792,93 @@ fn unresolvable_generic_argument() {
AnalyzerError::UnresolvableGenericArgument { .. }
));
}

#[test]
fn wrong_seperator() {
let code = r#"
package A {
enum B {
C,
}
}
module Module {
var _a: A::B;
always_comb {
_a = A.B.C;
}
}
"#;

let errors = analyze(code);
assert!(matches!(errors[0], AnalyzerError::WrongSeparator { .. }));

let code = r#"
package A {
enum B {
C,
}
}
module Module {
var _a: A::B;
always_comb {
_a = A::B.C;
}
}
"#;

let errors = analyze(code);
assert!(matches!(errors[0], AnalyzerError::WrongSeparator { .. }));

let code = r#"
module Module {
struct B {
b: logic,
}
var _a: B;
always_comb {
_a::b = '0;
}
}
"#;

let errors = analyze(code);
assert!(matches!(errors[0], AnalyzerError::WrongSeparator { .. }));

let code = r#"
interface B {
var c: logic;
modport mp {
c: input,
}
}
module Module (
b: modport B::mp
) {
var _a: logic;
always_comb {
_a = b::c;
}
}
"#;

let errors = analyze(code);
assert!(matches!(errors[0], AnalyzerError::WrongSeparator { .. }));

let code = r#"
interface A {
var b: logic;
}
module Module {
inst a: A;
always_comb {
a::b = 0;
}
}
"#;

let errors = analyze(code);
assert!(matches!(errors[0], AnalyzerError::WrongSeparator { .. }));
}
4 changes: 4 additions & 0 deletions crates/analyzer/src/var_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,10 @@ impl TryFrom<&ExpressionIdentifier> for VarRefPath {
};

path_items.push(full_path.pop().unwrap());
for _x in &arg.scoped_identifier.scoped_identifier_list {
path_items.push(full_path.pop().unwrap());
}

for x in &arg.expression_identifier_list {
path_items.push(VarRefPathItem::from(&*x.select));
}
Expand Down

0 comments on commit 5f0e6a1

Please sign in to comment.