use crate::AnalyzerError;
use crate::generic_inference_table;
use crate::namespace::Namespace;
use crate::namespace_table;
use crate::symbol::{Direction, GenericMap, Symbol, SymbolKind};
use crate::symbol_path::{GenericSymbol, GenericSymbolPath, SymbolPath, SymbolPathNamespace};
use crate::symbol_table;
use crate::symbol_table::{ResolveError, ResolveErrorCause};
use std::cell::RefCell;
use veryl_parser::token_range::TokenRange;
use veryl_parser::veryl_grammar_trait::{
ExpressionIdentifier, GenericArgIdentifier, HierarchicalIdentifier, Identifier,
InstParameterItem, InstPortItem, ModportItem, ScopedIdentifier, StructConstructorItem,
};
use veryl_parser::veryl_token::{Token, TokenSource, is_anonymous_text};
#[derive(Clone, Debug)]
pub enum ReferenceCandidate {
Identifier {
arg: Identifier,
namespace: Namespace,
},
HierarchicalIdentifier {
arg: HierarchicalIdentifier,
namespace: Namespace,
},
ScopedIdentifier {
arg: ScopedIdentifier,
namespace: Namespace,
in_import_declaration: bool,
},
ExpressionIdentifier {
arg: ExpressionIdentifier,
namespace: Namespace,
},
GenericArgIdentifier {
arg: GenericArgIdentifier,
namespace: Namespace,
},
ModportItem {
arg: ModportItem,
namespace: Namespace,
},
InstParameterItem {
arg: InstParameterItem,
namespace: Namespace,
},
InstPortItem {
arg: InstPortItem,
namespace: Namespace,
},
StructConstructorItem {
arg: StructConstructorItem,
r#type: ExpressionIdentifier,
},
NamedArgument {
arg: ExpressionIdentifier,
function: ExpressionIdentifier,
},
}
impl From<&Identifier> for ReferenceCandidate {
fn from(value: &Identifier) -> Self {
Self::Identifier {
arg: value.clone(),
namespace: namespace_table::get_default(),
}
}
}
impl From<&HierarchicalIdentifier> for ReferenceCandidate {
fn from(value: &HierarchicalIdentifier) -> Self {
Self::HierarchicalIdentifier {
arg: value.clone(),
namespace: namespace_table::get_default(),
}
}
}
impl From<(&ScopedIdentifier, bool)> for ReferenceCandidate {
fn from(value: (&ScopedIdentifier, bool)) -> Self {
Self::ScopedIdentifier {
arg: value.0.clone(),
namespace: namespace_table::get_default(),
in_import_declaration: value.1,
}
}
}
impl From<&ExpressionIdentifier> for ReferenceCandidate {
fn from(value: &ExpressionIdentifier) -> Self {
Self::ExpressionIdentifier {
arg: value.clone(),
namespace: namespace_table::get_default(),
}
}
}
impl From<&GenericArgIdentifier> for ReferenceCandidate {
fn from(value: &GenericArgIdentifier) -> Self {
Self::GenericArgIdentifier {
arg: value.clone(),
namespace: namespace_table::get_default(),
}
}
}
impl From<&ModportItem> for ReferenceCandidate {
fn from(value: &ModportItem) -> Self {
Self::ModportItem {
arg: value.clone(),
namespace: namespace_table::get_default(),
}
}
}
impl From<&InstParameterItem> for ReferenceCandidate {
fn from(value: &InstParameterItem) -> Self {
Self::InstParameterItem {
arg: value.clone(),
namespace: namespace_table::get_default(),
}
}
}
impl From<&InstPortItem> for ReferenceCandidate {
fn from(value: &InstPortItem) -> Self {
Self::InstPortItem {
arg: value.clone(),
namespace: namespace_table::get_default(),
}
}
}
#[derive(Default, Debug)]
pub struct ReferenceTable {
candidates: Vec<ReferenceCandidate>,
errors: Vec<AnalyzerError>,
}
impl ReferenceTable {
pub fn new() -> Self {
Self::default()
}
pub fn add(&mut self, cand: ReferenceCandidate) {
self.candidates.push(cand);
}
fn push_resolve_error(
&mut self,
err: ResolveError,
token: &TokenRange,
generics_token: Option<&Token>,
struct_token: Option<&Token>,
) {
if let Some(last_found) = err.last_found {
let name = last_found.token.to_string();
match err.cause {
ResolveErrorCause::NotFound(not_found) => {
let is_generic_if = if let SymbolKind::Port(ref port) = last_found.kind {
port.direction == Direction::Interface
} else {
false
};
if !is_generic_if {
let member = format!("{not_found}");
self.errors
.push(AnalyzerError::unknown_member(&name, &member, token));
}
}
ResolveErrorCause::Private => {
if matches!(last_found.kind, SymbolKind::Namespace) {
self.errors
.push(AnalyzerError::private_namespace(&name, token));
} else {
self.errors
.push(AnalyzerError::private_member(&name, token));
}
}
ResolveErrorCause::Invisible => {
self.errors
.push(AnalyzerError::invisible_identifier(&name, token));
}
}
} else if let ResolveErrorCause::NotFound(not_found) = err.cause {
let name = format!("{not_found}");
if let Some(generics_token) = generics_token {
self.errors
.push(AnalyzerError::unresolvable_generic_expression(
&name,
token,
&generics_token.into(),
));
} else if is_anonymous_text(not_found) {
} else if let Some(struct_token) = struct_token {
self.errors.push(AnalyzerError::unknown_member(
&struct_token.text.to_string(),
&name,
token,
));
} else {
self.errors
.push(AnalyzerError::undefined_identifier(&name, token));
}
} else {
unreachable!();
}
}
fn check_pacakge_reference(&mut self, symbol: &Symbol, token_range: &TokenRange) {
if !matches!(symbol.kind, SymbolKind::Package(_)) {
return;
}
let base_token = token_range.end;
let package_token = symbol.token;
if let (
TokenSource::File {
path: package_file, ..
},
TokenSource::File {
path: base_file, ..
},
) = (package_token.source, base_token.source)
{
let referecne_before_definition = package_file == base_file
&& (package_token.line > base_token.line
|| package_token.line == base_token.line
&& package_token.column > base_token.column);
if referecne_before_definition {
self.errors.push(AnalyzerError::referring_before_definition(
&package_token.to_string(),
token_range,
));
}
}
}
fn generic_symbol_path(
&mut self,
path: &GenericSymbolPath,
namespace: &Namespace,
in_import_declaration: bool,
generics_token: Option<&Token>,
generic_maps: Option<&Vec<GenericMap>>,
) {
let mut path = path.clone();
let mut resolved_generic_maps = vec![];
let orig_len = path.len();
path.resolve_imported(namespace, generic_maps);
let prefix_len = path.len() - orig_len;
for i in prefix_len..path.len() {
let base_path = path.base_path(i);
match symbol_table::resolve((&base_path, namespace)) {
Ok(symbol) => {
self.check_pacakge_reference(&symbol.found, &path.range);
symbol_table::add_reference(symbol.found.id, &path.paths[0].base);
let params = symbol.found.generic_parameters();
let mut inference_attempted_failed = false;
if i + 1 == path.paths.len() {
use generic_inference_table::InferredApply;
match generic_inference_table::apply_inferred_args(&mut path, &symbol.found)
{
InferredApply::Missing => inference_attempted_failed = true,
InferredApply::Applied | InferredApply::NotApplicable => {}
}
}
let n_args = path.paths[i].arguments.len();
if in_import_declaration
&& !params.is_empty()
&& matches!(
symbol.found.kind,
SymbolKind::Function(_) | SymbolKind::Struct(_) | SymbolKind::Union(_)
)
{
if n_args != 0 {
self.errors.push(AnalyzerError::invalid_import(&path.range))
}
continue;
}
let match_artiy = if params.len() > n_args {
params[n_args].1.default_value.is_some()
} else {
params.len() == n_args
};
if !match_artiy {
if inference_attempted_failed {
self.errors.push(AnalyzerError::generic_inference_failed(
&path.paths[i].base.to_string(),
&path.range,
));
} else {
self.errors.push(AnalyzerError::mismatch_generics_arity(
&path.paths[i].base.to_string(),
params.len(),
n_args,
&path.range,
));
}
continue;
}
if (params.len() + n_args) == 0 {
continue;
}
let target_symbol = symbol.found;
let mut args: Vec<_> = path.paths[i].arguments.drain(0..).collect();
for param in params.iter().skip(n_args) {
args.push(param.1.default_value.as_ref().unwrap().clone());
}
for arg in args.iter_mut() {
arg.unalias();
if !target_symbol.is_global_function() {
arg.append_namespace_path(namespace, &target_symbol.namespace);
}
}
path.paths[i].arguments.append(&mut args);
for arg in &path.paths[i].arguments {
self.generic_symbol_path(arg, namespace, false, None, None);
}
if path.is_generic_reference() {
Self::add_generic_reference(&target_symbol, namespace, &path, i);
} else {
Self::insert_generic_instance(
&path,
i,
namespace,
&target_symbol,
&mut resolved_generic_maps,
None,
);
}
}
Err(err) => {
let single_path = path.paths.len() == 1;
if single_path && !path.is_resolvable() {
return;
}
self.push_resolve_error(err, &path.range, generics_token, None);
}
}
}
}
fn insert_generic_instance(
path: &GenericSymbolPath,
ith: usize,
namespace: &Namespace,
target: &Symbol,
generic_maps: &mut Vec<GenericMap>,
affiliation_symbol: Option<&Symbol>,
) {
let instance_path = &path.paths[ith];
let Some((token, symbol)) =
Self::create_generic_instance(instance_path, target, namespace, affiliation_symbol)
else {
return;
};
if let Some(ref id) = symbol_table::insert(&token, symbol.clone()) {
symbol_table::add_generic_instance(target.id, *id);
}
generic_maps.push(GenericMap {
id: None,
map: target.generic_table(&instance_path.arguments),
});
Self::insert_subordinate_generic_instances(namespace, target, &symbol, generic_maps);
}
fn create_generic_instance(
symbol: &GenericSymbol,
target: &Symbol,
namespace: &Namespace,
affiliation_symbol: Option<&Symbol>,
) -> Option<(Token, Symbol)> {
if !target.is_global_function() {
symbol.get_generic_instance(target, affiliation_symbol)
} else if let Some(affiliation_symbol) = affiliation_symbol {
if affiliation_symbol.is_component(true) {
symbol.get_generic_instance(target, Some(affiliation_symbol))
} else {
symbol.get_generic_instance(
target,
affiliation_symbol.get_parent_component().as_ref(),
)
}
} else if let Some(namespace_symbol) = namespace.get_symbol() {
if namespace_symbol.is_component(true) {
symbol.get_generic_instance(target, Some(&namespace_symbol))
} else {
symbol
.get_generic_instance(target, namespace_symbol.get_parent_component().as_ref())
}
} else {
symbol.get_generic_instance(target, None)
}
}
fn insert_subordinate_generic_instances(
namespace: &Namespace,
target: &Symbol,
inst_symbol: &Symbol,
generic_maps: &[GenericMap],
) {
let is_global_func = target.is_global_function();
let mut subordinate_paths = if !is_global_func {
target.generic_references()
} else if let Some(paths) = symbol_table::get_reference_functions(target.id) {
paths
} else {
return;
};
if subordinate_paths.is_empty() {
return;
}
let target_namespace = &target.inner_namespace();
for path in &mut subordinate_paths {
if path.paths[0].base.text == target.token.text {
continue;
}
path.apply_map(generic_maps);
path.unalias();
path.append_namespace_path(namespace, &target.namespace);
if let Ok(path_symbol) = symbol_table::resolve((&path.generic_path(), target_namespace))
{
let ith = path.len() - 1;
if is_global_func {
Self::insert_generic_instance(
path,
ith,
namespace,
&path_symbol.found,
&mut generic_maps.to_vec(),
None,
);
} else {
Self::insert_generic_instance(
path,
ith,
target_namespace,
&path_symbol.found,
&mut generic_maps.to_vec(),
Some(inst_symbol),
);
}
}
}
}
fn add_generic_reference(
symbol: &Symbol,
namespace: &Namespace,
path: &GenericSymbolPath,
ith: usize,
) {
fn get_parent_generic_component(namespace: &Namespace) -> Option<Symbol> {
let target = namespace.get_symbol()?;
if target.has_generic_paramters() {
Some(target)
} else {
get_parent_generic_component(&target.namespace)
}
}
let mut namespace = namespace.clone();
namespace.strip_anonymous_path();
let mut target = if let Some(target) = get_parent_generic_component(&namespace)
&& !target.is_global_function()
{
target
} else {
return;
};
let path = path.slice(ith);
let generic_maps = target.generic_maps();
for map in generic_maps {
let affiliation_symbol = map.id.map(|id| symbol_table::get(id).unwrap());
let mut path = path.clone();
let ith = path.len() - 1;
let mut maps = vec![map];
path.apply_map(&maps);
path.append_namespace_path(&namespace, &symbol.namespace);
Self::insert_generic_instance(
&path,
ith,
&namespace,
symbol,
&mut maps,
affiliation_symbol.as_ref(),
);
}
let kind = match target.kind {
SymbolKind::Function(mut x) => {
x.generic_references.push(path);
SymbolKind::Function(x)
}
SymbolKind::Module(mut x) => {
x.generic_references.push(path);
SymbolKind::Module(x)
}
SymbolKind::Interface(mut x) => {
x.generic_references.push(path);
SymbolKind::Interface(x)
}
SymbolKind::Package(mut x) => {
x.generic_references.push(path);
SymbolKind::Package(x)
}
SymbolKind::Struct(mut x) => {
x.generic_references.push(path);
SymbolKind::Struct(x)
}
SymbolKind::Union(mut x) => {
x.generic_references.push(path);
SymbolKind::Union(x)
}
_ => return,
};
target.kind = kind;
symbol_table::update(target);
}
fn check_simple_identifier(
&mut self,
path: &SymbolPathNamespace,
default_namespace: Option<&Namespace>,
token: &TokenRange,
struct_token: Option<&Token>,
) {
if let Some(default_namespace) = default_namespace {
namespace_table::set_default(&default_namespace.paths);
}
match symbol_table::resolve(path) {
Ok(symbol) => {
for id in symbol.full_path {
symbol_table::add_reference(id, &token.beg);
}
}
Err(err) => {
self.push_resolve_error(err, token, None, struct_token);
}
}
}
fn check_array_member_access(
&mut self,
path: &GenericSymbolPath,
namespace: &Namespace,
selects_per_component: &[usize],
) {
for i in 0..path.len().saturating_sub(1) {
let base_path = path.base_path(i);
if let Ok(symbol) = symbol_table::resolve((&base_path, namespace))
&& let Some(r#type) = symbol.found.kind.get_type()
{
let array_dims = r#type.array.len();
let applied_selects = selects_per_component.get(i).copied().unwrap_or(0);
if applied_selects < array_dims {
let member_name = if i + 1 < path.len() {
path.paths[i + 1].base.to_string()
} else {
String::new()
};
self.errors.push(AnalyzerError::member_access_on_array(
&symbol.found.token.to_string(),
&member_name,
array_dims,
&path.range,
));
return;
}
}
}
}
fn check_complex_identifier(
&mut self,
path: &GenericSymbolPath,
default_namespace: &Namespace,
token: &Token,
in_import_declaration: bool,
) {
namespace_table::set_default(&default_namespace.paths);
let namespace = namespace_table::get(token.id).unwrap();
self.generic_symbol_path(path, &namespace, in_import_declaration, None, None);
}
pub fn apply(&mut self) -> Vec<AnalyzerError> {
symbol_table::suppress_cache_clear();
let candidates: Vec<_> = self.candidates.drain(0..).collect();
for x in &candidates {
match x {
ReferenceCandidate::Identifier { arg, namespace } => {
self.check_simple_identifier(&arg.into(), Some(namespace), &arg.into(), None);
}
ReferenceCandidate::HierarchicalIdentifier { arg, namespace } => {
let token = arg.identifier.identifier_token.token;
let path: GenericSymbolPath = arg.into();
self.check_complex_identifier(&path, namespace, &token, false);
if !arg.hierarchical_identifier_list0.is_empty() {
let ns = namespace_table::get(token.id).unwrap_or(namespace.clone());
let mut selects = vec![0usize; path.len()];
selects[0] = arg.hierarchical_identifier_list.len();
for (j, member) in arg.hierarchical_identifier_list0.iter().enumerate() {
if 1 + j < selects.len() {
selects[1 + j] = member.hierarchical_identifier_list0_list.len();
}
}
self.check_array_member_access(&path, &ns, &selects);
}
}
ReferenceCandidate::ScopedIdentifier {
arg,
namespace,
in_import_declaration,
} => {
let token = arg.identifier().token;
self.check_complex_identifier(
&arg.into(),
namespace,
&token,
*in_import_declaration,
);
}
ReferenceCandidate::ExpressionIdentifier { arg, namespace } => {
let token = arg.scoped_identifier.identifier().token;
let path: GenericSymbolPath = arg.into();
self.check_complex_identifier(&path, namespace, &token, false);
if !arg.expression_identifier_list0.is_empty() {
let ns = namespace_table::get(token.id).unwrap_or(namespace.clone());
let scoped_len = arg.scoped_identifier.scoped_identifier_list.len() + 1;
let mut selects = vec![0usize; path.len()];
if scoped_len <= selects.len() {
selects[scoped_len - 1] = arg.expression_identifier_list.len();
}
for (j, member) in arg.expression_identifier_list0.iter().enumerate() {
if scoped_len + j < selects.len() {
selects[scoped_len + j] =
member.expression_identifier_list0_list.len();
}
}
self.check_array_member_access(&path, &ns, &selects);
}
}
ReferenceCandidate::GenericArgIdentifier { arg, namespace } => {
let token = arg.scoped_identifier.identifier().token;
self.check_complex_identifier(&arg.into(), namespace, &token, false);
}
ReferenceCandidate::ModportItem { arg, namespace } => {
let mut path: SymbolPathNamespace = arg.identifier.as_ref().into();
path.pop_namespace();
self.check_simple_identifier(&path, Some(namespace), &arg.into(), None);
}
ReferenceCandidate::InstParameterItem { arg, namespace } => {
if arg.inst_parameter_item_opt.is_none() {
let identifier = arg.identifier.as_ref();
self.check_simple_identifier(
&identifier.into(),
Some(namespace),
&identifier.into(),
None,
);
}
}
ReferenceCandidate::InstPortItem { arg, namespace } => {
if arg.inst_port_item_opt.is_none() {
let identifier = arg.identifier.as_ref();
self.check_simple_identifier(
&identifier.into(),
Some(namespace),
&identifier.into(),
None,
);
}
}
ReferenceCandidate::StructConstructorItem { arg, r#type } => {
if let Ok(symbol) = symbol_table::resolve(r#type)
&& !matches!(symbol.found.kind, SymbolKind::SystemVerilog)
{
let identifier = arg.identifier.as_ref();
let namespace = Self::get_struct_namespace(&symbol.found);
let path: SymbolPathNamespace = (identifier, &namespace).into();
self.check_simple_identifier(
&path,
None,
&identifier.into(),
Some(&symbol.found.token),
);
}
}
ReferenceCandidate::NamedArgument { arg, function } => {
if let Ok(symbol) = symbol_table::resolve(function) {
let func_symbol =
if let SymbolKind::ModportFunctionMember(x) = &symbol.found.kind {
symbol_table::get(x.function).unwrap()
} else {
(*symbol.found).clone()
};
let namespace = func_symbol.inner_namespace();
let path: SymbolPath = arg.into();
let path: SymbolPathNamespace = (&path, &namespace).into();
self.check_simple_identifier(
&path,
None,
&arg.into(),
Some(&func_symbol.token),
);
}
}
}
}
symbol_table::resume_cache_clear();
self.errors.drain(0..).collect()
}
fn get_struct_namespace(symbol: &Symbol) -> Namespace {
match &symbol.kind {
SymbolKind::TypeDef(x) => {
let namespace = Some(&symbol.namespace);
if let Some((_, Some(symbol))) = x.r#type.trace_user_defined(namespace) {
return Self::get_struct_namespace(&symbol);
}
}
SymbolKind::ProtoTypeDef(x) => {
if let Some(r#type) = &x.r#type {
let namespace = Some(&symbol.namespace);
if let Some((_, Some(symbol))) = r#type.trace_user_defined(namespace) {
return Self::get_struct_namespace(&symbol);
}
}
}
_ => {}
}
symbol.inner_namespace()
}
}
thread_local!(static REFERENCE_TABLE: RefCell<ReferenceTable> = RefCell::new(ReferenceTable::new()));
pub fn add(cand: ReferenceCandidate) {
REFERENCE_TABLE.with(|f| f.borrow_mut().add(cand))
}
pub fn apply() -> Vec<AnalyzerError> {
REFERENCE_TABLE.with(|f| f.borrow_mut().apply())
}