diff --git a/vhdl_lang/src/analysis/declarative.rs b/vhdl_lang/src/analysis/declarative.rs index 621fdb24..5392aeee 100644 --- a/vhdl_lang/src/analysis/declarative.rs +++ b/vhdl_lang/src/analysis/declarative.rs @@ -507,9 +507,10 @@ impl<'a, 't> AnalyzeContext<'a, 't> { } Declaration::Attribute(ref mut attr) => match attr { Attribute::Declaration(ref mut attr_decl) => { - if let Some(typ) = as_fatal(self.resolve_type_mark( + if let Some(typ) = as_fatal(self.type_name( scope, - &mut attr_decl.type_mark, + attr_decl.type_mark.span, + &mut attr_decl.type_mark.item, diagnostics, ))? { scope.add( @@ -1088,7 +1089,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> { ) -> EvalResult> { match array_index { ArrayIndex::IndexSubtypeDefintion(ref mut type_mark) => self - .resolve_type_mark(scope, type_mark, diagnostics) + .type_name(scope, type_mark.span, &mut type_mark.item, diagnostics) .map(|typ| typ.base()), ArrayIndex::Discrete(ref mut drange) => self.drange_type(scope, drange, diagnostics), } diff --git a/vhdl_lang/src/analysis/design_unit.rs b/vhdl_lang/src/analysis/design_unit.rs index a4a734e1..69199be4 100644 --- a/vhdl_lang/src/analysis/design_unit.rs +++ b/vhdl_lang/src/analysis/design_unit.rs @@ -258,11 +258,8 @@ impl<'a, 't> AnalyzeContext<'a, 't> { if let Design::Entity(ref visibility, ref region) = primary.kind() { (visibility, region) } else { - let mut diagnostic = Diagnostic::new( - unit.ident_pos(self.ctx), - "Expected an entity", - ErrorCode::MismatchedKinds, - ); + let mut diagnostic = + Diagnostic::mismatched_kinds(unit.ident_pos(self.ctx), "Expected an entity"); if let Some(pos) = primary.decl_pos() { diagnostic.add_related(pos, format!("Found {}", primary.describe())) @@ -329,11 +326,8 @@ impl<'a, 't> AnalyzeContext<'a, 't> { Design::Package(ref visibility, ref region) | Design::UninstPackage(ref visibility, ref region) => (visibility, region), _ => { - let mut diagnostic = Diagnostic::new( - unit.ident_pos(self.ctx), - "Expected a package", - ErrorCode::MismatchedKinds, - ); + let mut diagnostic = + Diagnostic::mismatched_kinds(unit.ident_pos(self.ctx), "Expected a package"); if let Some(pos) = primary.decl_pos() { diagnostic.add_related(pos, format!("Found {}", primary.describe())) @@ -491,10 +485,9 @@ impl<'a, 't> AnalyzeContext<'a, 't> { Err(_) => { bail!( diagnostics, - Diagnostic::new( + Diagnostic::mismatched_kinds( &prefix.pos(self.ctx), "Invalid prefix of a selected name", - ErrorCode::MismatchedKinds ) ); } @@ -502,10 +495,9 @@ impl<'a, 't> AnalyzeContext<'a, 't> { UsedNames::AllWithin(..) => { bail!( diagnostics, - Diagnostic::new( + Diagnostic::mismatched_kinds( &prefix.pos(self.ctx), "'.all' may not be the prefix of a selected name", - ErrorCode::MismatchedKinds ) ); } @@ -545,11 +537,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> { | Name::External(..) => { bail!( diagnostics, - Diagnostic::new( - &name.pos(self.ctx), - "Invalid selected name", - ErrorCode::MismatchedKinds - ) + Diagnostic::mismatched_kinds(&name.pos(self.ctx), "Invalid selected name",) ); } } diff --git a/vhdl_lang/src/analysis/expression.rs b/vhdl_lang/src/analysis/expression.rs index 5391a541..776f6c96 100644 --- a/vhdl_lang/src/analysis/expression.rs +++ b/vhdl_lang/src/analysis/expression.rs @@ -585,7 +585,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> { ) -> EvalResult> { let QualifiedExpression { type_mark, expr } = qexpr; - match as_fatal(self.resolve_type_mark(scope, type_mark, diagnostics))? { + match as_fatal(self.type_name(scope, type_mark.span, &mut type_mark.item, diagnostics))? { Some(target_type) => { self.expr_pos_with_ttyp( scope, diff --git a/vhdl_lang/src/analysis/literals.rs b/vhdl_lang/src/analysis/literals.rs index a76c0c28..623be337 100644 --- a/vhdl_lang/src/analysis/literals.rs +++ b/vhdl_lang/src/analysis/literals.rs @@ -205,10 +205,9 @@ impl<'a, 't> AnalyzeContext<'a, 't> { )) } } - NamedEntities::Overloaded(_) => Err(Diagnostic::new( + NamedEntities::Overloaded(_) => Err(Diagnostic::mismatched_kinds( unit.item.pos(self.ctx), "Overloaded name may not be physical unit", - ErrorCode::MismatchedKinds, )), } } diff --git a/vhdl_lang/src/analysis/names.rs b/vhdl_lang/src/analysis/names.rs index 5365ca8e..8d39c720 100644 --- a/vhdl_lang/src/analysis/names.rs +++ b/vhdl_lang/src/analysis/names.rs @@ -399,14 +399,6 @@ impl<'a> ResolvedName<'a> { ResolvedName::Final(_) => None, } } - - /// Convenience function that returns `Some(name)` when self is an object name, else `None` - fn as_object_name(&self) -> Option> { - match self { - ResolvedName::ObjectName(oname) => Some(*oname), - _ => None, - } - } } /// Represents the result when resolving an attribute. @@ -505,10 +497,9 @@ impl<'a, 't> AnalyzeContext<'a, 't> { ) -> Result>, Diagnostic> { match name { ResolvedName::Library(_) | ResolvedName::Design(_) | ResolvedName::Type(_) => { - Err(Diagnostic::new( + Err(Diagnostic::mismatched_kinds( pos.pos(self.ctx), format!("{} cannot be used in an expression", name.describe_type()), - ErrorCode::MismatchedKinds, )) } ResolvedName::Final(ent) => match ent.actual_kind() { @@ -520,10 +511,9 @@ impl<'a, 't> AnalyzeContext<'a, 't> { Ok(Some(DisambiguatedType::Unambiguous(subtype.type_mark()))) } AnyEntKind::InterfaceFile(typ) => Ok(Some(DisambiguatedType::Unambiguous(*typ))), - _ => Err(Diagnostic::new( + _ => Err(Diagnostic::mismatched_kinds( pos.pos(self.ctx), format!("{} cannot be used in an expression", name.describe_type()), - ErrorCode::MismatchedKinds, )), }, ResolvedName::Overloaded(des, overloaded) => { @@ -555,10 +545,9 @@ impl<'a, 't> AnalyzeContext<'a, 't> { ) -> Result>, Diagnostic> { match name { ResolvedName::Library(_) | ResolvedName::Design(_) | ResolvedName::Type(_) => { - Err(Diagnostic::new( + Err(Diagnostic::mismatched_kinds( span.pos(self.ctx), format!("{} cannot be used in an expression", name.describe_type()), - ErrorCode::MismatchedKinds, )) } ResolvedName::Final(ent) => match ent.actual_kind() { @@ -566,10 +555,9 @@ impl<'a, 't> AnalyzeContext<'a, 't> { AnyEntKind::PhysicalLiteral(typ) => Ok(Some(*typ)), AnyEntKind::File(subtype) => Ok(Some(subtype.type_mark())), AnyEntKind::InterfaceFile(typ) => Ok(Some(*typ)), - _ => Err(Diagnostic::new( + _ => Err(Diagnostic::mismatched_kinds( span.pos(self.ctx), format!("{} cannot be used in an expression", name.describe_type()), - ErrorCode::MismatchedKinds, )), }, ResolvedName::Overloaded(des, overloaded) => { @@ -659,10 +647,9 @@ impl<'a, 't> AnalyzeContext<'a, 't> { } else { bail!( diagnostics, - Diagnostic::new( + Diagnostic::mismatched_kinds( expr_pos.pos(self.ctx), format!("{} cannot be used as a discrete range", typ.describe()), - ErrorCode::MismatchedKinds, ) ); }; @@ -1131,7 +1118,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> { Err(EvalError::Unknown) } AttributeDesignator::Type(attr) => self - .resolve_type_attribute_suffix(prefix, &attr, name_pos, diagnostics) + .resolve_type_attribute_suffix(prefix, prefix_pos, &attr, name_pos, diagnostics) .map(|typ| AttrResolveResult::Type(typ.base())), AttributeDesignator::Converse => { let view = self.resolve_view_ent(prefix, diagnostics, prefix_pos)?; @@ -1154,35 +1141,49 @@ impl<'a, 't> AnalyzeContext<'a, 't> { fn resolve_type_attribute_suffix( &self, prefix: &ResolvedName<'a>, + prefix_pos: TokenSpan, suffix: &TypeAttribute, pos: TokenSpan, diagnostics: &mut dyn DiagnosticHandler, ) -> EvalResult> { - // all type attribute suffixes require that the prefix be an object type - let Some(obj) = prefix.as_object_name() else { - diagnostics.add( - pos.pos(self.ctx), - format!( - "The {} attribute can only be used on objects, not {}", - suffix, - prefix.describe() - ), - ErrorCode::IllegalAttribute, - ); - return Err(EvalError::Unknown); + let typ = match prefix { + ResolvedName::Type(typ) => { + if *suffix == TypeAttribute::Element { + *typ + } else { + let diag = Diagnostic::illegal_attribute( + pos.pos(self.ctx), + format!( + "The {suffix} attribute can only be used on objects, not {}", + typ.describe() + ), + ) + .opt_related(typ.decl_pos(), "Defined here"); + bail!(diagnostics, diag); + } + } + ResolvedName::ObjectName(obj) => obj.type_mark(), + other => { + let diag = Diagnostic::mismatched_kinds( + pos.pos(self.ctx), + format!("Expected type, got {}", other.describe()), + ) + .opt_related(other.decl_pos(), "Defined here"); + bail!(diagnostics, diag); + } }; + match suffix { - TypeAttribute::Subtype => Ok(obj.type_mark()), + TypeAttribute::Subtype => Ok(typ), TypeAttribute::Element => { - if let Some((elem_type, _)) = obj.type_mark().array_type() { + if let Some((elem_type, _)) = typ.array_type() { Ok(elem_type) } else { - diagnostics.add( - pos.pos(self.ctx), - "The element attribute can only be used for array types", - ErrorCode::IllegalAttribute, + let diag = Diagnostic::illegal_attribute( + prefix_pos.pos(self.ctx), + format!("array type expected for '{suffix} attribute"), ); - Err(EvalError::Unknown) + bail!(diagnostics, diag); } } } @@ -1582,12 +1583,14 @@ impl<'a, 't> AnalyzeContext<'a, 't> { | ResolvedName::Overloaded { .. } | ResolvedName::Expression(_) | ResolvedName::Final(_) => { - diagnostics.add( - name_pos.pos(self.ctx), - format!("Expected type name, got {}", resolved.describe()), - ErrorCode::MismatchedKinds, + bail!( + diagnostics, + Diagnostic::mismatched_kinds( + name_pos.pos(self.ctx), + format!("Expected type, got {}", resolved.describe()) + ) + .opt_related(resolved.decl_pos(), "Defined here") ); - Err(EvalError::Unknown) } } } @@ -2104,10 +2107,9 @@ variable thevar : integer; ); check_diagnostics( diagnostics, - vec![Diagnostic::new( - code.s1("thevar'element"), - "The element attribute can only be used for array types", - ErrorCode::IllegalAttribute, + vec![Diagnostic::illegal_attribute( + code.s1("thevar"), + "array type expected for 'element attribute", )], ) } @@ -2115,7 +2117,7 @@ variable thevar : integer; #[test] fn element_type_attributes_on_non_object_types() { let test = TestSetup::new(); - test.declarative_part( + let declarative_code = test.declarative_part( " type my_type is array(natural range<>) of integer; ", @@ -2128,11 +2130,11 @@ type my_type is array(natural range<>) of integer; ); check_diagnostics( diagnostics, - vec![Diagnostic::new( + vec![Diagnostic::illegal_attribute( code.s1("my_type'subtype"), "The subtype attribute can only be used on objects, not array type 'my_type'", - ErrorCode::IllegalAttribute, - )], + ) + .related(declarative_code.s1("my_type"), "Defined here")], ) } @@ -2150,13 +2152,19 @@ variable x: integer; test.name_resolve(&code, None, &mut diagnostics), Err(EvalError::Unknown) ); + let integer_pos = test + .ctx(&Vec::new()) + .root + .find_standard_symbol("INTEGER") + .decl_pos() + .unwrap(); check_diagnostics( diagnostics, - vec![Diagnostic::new( + vec![Diagnostic::illegal_attribute( code.s1("x'subtype'subtype"), "The subtype attribute can only be used on objects, not integer type 'INTEGER'", - ErrorCode::IllegalAttribute, - )], + ) + .related(integer_pos, "Defined here")], ) } diff --git a/vhdl_lang/src/analysis/package_instance.rs b/vhdl_lang/src/analysis/package_instance.rs index b16c8022..893f249d 100644 --- a/vhdl_lang/src/analysis/package_instance.rs +++ b/vhdl_lang/src/analysis/package_instance.rs @@ -171,15 +171,13 @@ impl<'a, 't> AnalyzeContext<'a, 't> { if let Some(ent) = overloaded.get(&signature) { name.set_unique_reference(&ent); } else { - let mut diag = Diagnostic::new( + let mut diag = Diagnostic::mismatched_kinds( &assoc.actual.pos(self.ctx), format!( - "Cannot map '{}' to subprogram generic {}{}", - des, + "Cannot map '{des}' to subprogram generic {}{}", target.designator(), signature.key().describe() ), - ErrorCode::MismatchedKinds, ); diag.add_subprogram_candidates( diff --git a/vhdl_lang/src/analysis/range.rs b/vhdl_lang/src/analysis/range.rs index de290885..5c9cd5d4 100644 --- a/vhdl_lang/src/analysis/range.rs +++ b/vhdl_lang/src/analysis/range.rs @@ -256,7 +256,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> { let typ = match drange { DiscreteRange::Discrete(ref mut type_mark, ref mut range) => { let typ = self - .resolve_type_mark(scope, type_mark, diagnostics)? + .type_name(scope, type_mark.span, &mut type_mark.item, diagnostics)? .base(); if let Some(ref mut range) = range { self.range_with_ttyp(scope, typ.into(), range, diagnostics)?; @@ -366,7 +366,12 @@ impl<'a, 't> AnalyzeContext<'a, 't> { ) -> FatalResult { match drange { DiscreteRange::Discrete(ref mut type_mark, ref mut range) => { - let _ = as_fatal(self.resolve_type_mark(scope, type_mark, diagnostics))?; + let _ = as_fatal(self.name_resolve( + scope, + type_mark.span, + &mut type_mark.item, + diagnostics, + ))?; if let Some(ref mut range) = range { self.range_with_ttyp(scope, target_type, range, diagnostics)?; } @@ -464,10 +469,9 @@ mod tests { check_diagnostics( diagnostics, - vec![Diagnostic::new( + vec![Diagnostic::mismatched_kinds( code.s1("0.0 to 1.0"), "Non-discrete type universal_real cannot be used in discrete range", - ErrorCode::MismatchedKinds, )], ) } @@ -631,10 +635,9 @@ function myfun return arr_t; check_diagnostics( diagnostics, - vec![Diagnostic::new( + vec![Diagnostic::mismatched_kinds( code.s1("character"), "type 'CHARACTER' cannot be prefix of range attribute, array type or object is required", - ErrorCode::MismatchedKinds, )], ); } diff --git a/vhdl_lang/src/analysis/semantic.rs b/vhdl_lang/src/analysis/semantic.rs index 37fd1438..89c9e7d1 100644 --- a/vhdl_lang/src/analysis/semantic.rs +++ b/vhdl_lang/src/analysis/semantic.rs @@ -15,73 +15,6 @@ use crate::data::*; use crate::named_entity::*; impl<'a, 't> AnalyzeContext<'a, 't> { - pub fn resolve_type_mark( - &self, - scope: &Scope<'a>, - type_mark: &mut WithTokenSpan, - diagnostics: &mut dyn DiagnosticHandler, - ) -> EvalResult> { - let name = self.name_resolve( - scope, - type_mark.item.name.span, - &mut type_mark.item.name.item, - diagnostics, - )?; - - if let Some(attr) = &type_mark.item.attr { - let span = type_mark.item.name.suffix_pos(); - - let typ = match name { - ResolvedName::Type(typ) if *attr == TypeAttribute::Element => typ, - ResolvedName::ObjectName(obj) => obj.type_mark(), - other => { - let mut diag = Diagnostic::new( - type_mark.pos(self.ctx), - format!("Expected type, got {}", other.describe()), - ErrorCode::MismatchedKinds, - ); - if let Some(pos) = other.decl_pos() { - diag.add_related(pos, "Defined here"); - } - diagnostics.push(diag); - return Err(EvalError::Unknown); - } - }; - - match attr { - TypeAttribute::Subtype => Ok(typ), - TypeAttribute::Element => { - if let Some((elem_type, _)) = typ.array_type() { - Ok(elem_type) - } else { - diagnostics.add( - span.pos(self.ctx), - format!("array type expected for '{attr} attribute",), - ErrorCode::TypeMismatch, - ); - Err(EvalError::Unknown) - } - } - } - } else { - match name { - ResolvedName::Type(typ) => Ok(typ), - other => { - let mut diag = Diagnostic::new( - type_mark.pos(self.ctx), - format!("Expected type, got {}", other.describe()), - ErrorCode::MismatchedKinds, - ); - if let Some(pos) = other.decl_pos() { - diag.add_related(pos, "Defined here"); - } - diagnostics.push(diag); - Err(EvalError::Unknown) - } - } - } - } - pub fn choice_with_ttyp( &self, scope: &Scope<'a>, @@ -265,10 +198,9 @@ impl Diagnostic { impl<'a> ResolvedName<'a> { pub(super) fn kind_error(&self, pos: impl AsRef, expected: &str) -> Diagnostic { - let mut error = Diagnostic::new( + let mut error = Diagnostic::mismatched_kinds( pos, - format!("Expected {}, got {}", expected, self.describe()), - ErrorCode::MismatchedKinds, + format!("Expected {expected}, got {}", self.describe()), ); if let Some(decl_pos) = self.decl_pos() { error.add_related(decl_pos, "Defined here"); @@ -290,13 +222,12 @@ impl Diagnostic { named_entity: &AnyEnt, prefix: &SrcPos, ) -> Diagnostic { - Diagnostic::new( + Diagnostic::mismatched_kinds( prefix, capitalize(&format!( "{} may not be the prefix of a selected name", named_entity.describe(), )), - ErrorCode::MismatchedKinds, ) } diff --git a/vhdl_lang/src/analysis/subprogram.rs b/vhdl_lang/src/analysis/subprogram.rs index 6f1b71e5..d0e7eb89 100644 --- a/vhdl_lang/src/analysis/subprogram.rs +++ b/vhdl_lang/src/analysis/subprogram.rs @@ -117,8 +117,12 @@ impl<'a, 't> AnalyzeContext<'a, 't> { &mut fun.parameter_list, diagnostics, ); - let return_type = - self.resolve_type_mark(&subpgm_region, &mut fun.return_type, diagnostics); + let return_type = self.type_name( + &subpgm_region, + fun.return_type.span, + &mut fun.return_type.item, + diagnostics, + ); (Signature::new(params?, Some(return_type?)), generic_map) } SubprogramSpecification::Procedure(procedure) => { @@ -191,15 +195,15 @@ impl<'a, 't> AnalyzeContext<'a, 't> { ast::Signature::Function(ref mut args, ref mut ret) => { let args: Vec<_> = args .iter_mut() - .map(|arg| self.resolve_type_mark(scope, arg, diagnostics)) + .map(|arg| self.type_name(scope, arg.span, &mut arg.item, diagnostics)) .collect(); - let return_type = self.resolve_type_mark(scope, ret, diagnostics); + let return_type = self.type_name(scope, ret.span, &mut ret.item, diagnostics); (args, Some(return_type)) } ast::Signature::Procedure(args) => { let args: Vec<_> = args .iter_mut() - .map(|arg| self.resolve_type_mark(scope, arg, diagnostics)) + .map(|arg| self.type_name(scope, arg.span, &mut arg.item, diagnostics)) .collect(); (args, None) } diff --git a/vhdl_lang/src/analysis/tests/assignment_typecheck.rs b/vhdl_lang/src/analysis/tests/assignment_typecheck.rs index 759b4021..736e692b 100644 --- a/vhdl_lang/src/analysis/tests/assignment_typecheck.rs +++ b/vhdl_lang/src/analysis/tests/assignment_typecheck.rs @@ -34,15 +34,13 @@ end architecture; ); let expected = vec![ - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s("foo1", 2), "function foo1[return NATURAL] may not be the target of an assignment", - ErrorCode::MismatchedKinds, ), - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s("foo2", 2), "foo2[return enum_t] may not be the target of an assignment", - ErrorCode::MismatchedKinds, ), ]; @@ -70,10 +68,9 @@ end architecture; ", ); - let expected = vec![Diagnostic::new( + let expected = vec![Diagnostic::mismatched_kinds( code.s("foo'stable", 1), "Expression may not be the target of an assignment", - ErrorCode::MismatchedKinds, )]; let diagnostics = builder.analyze(); @@ -118,25 +115,21 @@ end architecture; ); let expected = vec![ - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s1("work.pkg.foo1(2)"), "Expression may not be the target of an assignment", - ErrorCode::MismatchedKinds, ), - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s1("foo2(2)"), "Expression may not be the target of an assignment", - ErrorCode::MismatchedKinds, ), - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s1("work.pkg.foo1(arg => 2)"), "Expression may not be the target of an assignment", - ErrorCode::MismatchedKinds, ), - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s1("foo2(arg => 2)"), "Expression may not be the target of an assignment", - ErrorCode::MismatchedKinds, ), ]; @@ -167,15 +160,13 @@ end architecture; ); let expected = vec![ - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s("foo1", 3), "constant 'foo1' may not be the target of an assignment", - ErrorCode::MismatchedKinds, ), - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s("foo2", 2), "alias 'foo2' of constant may not be the target of an assignment", - ErrorCode::MismatchedKinds, ), ]; @@ -300,15 +291,13 @@ end architecture; ); let expected = vec![ - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s("foo1", 2), "interface constant 'foo1' may not be the target of an assignment", - ErrorCode::MismatchedKinds, ), - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s("foo2", 2), "interface variable 'foo2' of mode in may not be the target of an assignment", - ErrorCode::MismatchedKinds, ), ]; @@ -348,25 +337,21 @@ end architecture; ); let expected = vec![ - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s("foo1", 2), "interface signal 'foo1' of mode out may not be the target of a variable assignment", - ErrorCode::MismatchedKinds, ), - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s("foo2", 2), "interface variable 'foo2' of mode out may not be the target of a signal assignment", - ErrorCode::MismatchedKinds, ), - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s("foo3", 2), "signal 'foo3' may not be the target of a variable assignment", - ErrorCode::MismatchedKinds, ), - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s("foo4", 2), "variable 'foo4' may not be the target of a signal assignment", - ErrorCode::MismatchedKinds, ), ]; @@ -391,10 +376,9 @@ end architecture; ", ); - let expected = vec![Diagnostic::new( + let expected = vec![Diagnostic::mismatched_kinds( code.s("foo", 2), "signal 'foo' of subtype 'NATURAL' cannot be indexed", - ErrorCode::MismatchedKinds, )]; let diagnostics = builder.analyze(); @@ -418,10 +402,9 @@ end architecture; ", ); - let expected = vec![Diagnostic::new( + let expected = vec![Diagnostic::mismatched_kinds( code.s("foo", 2), "signal 'foo' of subtype 'NATURAL' cannot be sliced", - ErrorCode::MismatchedKinds, )]; let diagnostics = builder.analyze(); diff --git a/vhdl_lang/src/analysis/tests/context_clause.rs b/vhdl_lang/src/analysis/tests/context_clause.rs index 1473e5a3..4f4fe014 100644 --- a/vhdl_lang/src/analysis/tests/context_clause.rs +++ b/vhdl_lang/src/analysis/tests/context_clause.rs @@ -438,10 +438,9 @@ end entity; check_diagnostics( diagnostics, - vec![Diagnostic::new( + vec![Diagnostic::mismatched_kinds( code.s("pkg", 2), "package 'pkg' does not denote a context declaration", - ErrorCode::MismatchedKinds, )], ) } @@ -471,30 +470,22 @@ end entity; check_diagnostics( diagnostics, vec![ - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s("libname", 2), "Context reference must be a selected name", - ErrorCode::MismatchedKinds, - ), - Diagnostic::new( - code.s1("work"), - "Use clause must be a selected name", - ErrorCode::MismatchedKinds, ), - Diagnostic::new( + Diagnostic::mismatched_kinds(code.s1("work"), "Use clause must be a selected name"), + Diagnostic::mismatched_kinds( code.s("libname", 3), "Use clause must be a selected name", - ErrorCode::MismatchedKinds, ), - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s1("work.pkg(0)"), "Use clause must be a selected name", - ErrorCode::MismatchedKinds, ), - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s1("work.ctx'range"), "Context reference must be a selected name", - ErrorCode::MismatchedKinds, ), ], ); @@ -616,15 +607,13 @@ end entity; check_diagnostics( diagnostics, vec![ - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s("work.all", 1), "'.all' may not be the prefix of a selected name", - ErrorCode::MismatchedKinds, ), - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s("work.all", 2), "'.all' may not be the prefix of a selected name", - ErrorCode::MismatchedKinds, ), ], ); @@ -651,15 +640,13 @@ end package; check_diagnostics( diagnostics, vec![ - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s("work.gpkg", 1), "Uninstantiated package 'gpkg' may not be the prefix of a selected name", - ErrorCode::MismatchedKinds, ), - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s("work.gpkg", 2), "Uninstantiated package 'gpkg' may not be the prefix of a selected name", - ErrorCode::MismatchedKinds, ), ], ); @@ -687,15 +674,13 @@ end package; check_diagnostics( diagnostics, vec![ - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s("work.pkg.enum_t", 1), "Type 'enum_t' may not be the prefix of a selected name", - ErrorCode::MismatchedKinds, ), - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s("work.pkg.const", 1), "Invalid prefix for selected name", - ErrorCode::MismatchedKinds, ), ], ); @@ -936,10 +921,9 @@ end package; let diagnostics = builder.analyze(); check_diagnostics( diagnostics, - vec![Diagnostic::new( + vec![Diagnostic::mismatched_kinds( code.s("work.pkg1.typ_t", 1), "Subtype 'typ_t' may not be the prefix of a selected name", - ErrorCode::MismatchedKinds, )], ); } diff --git a/vhdl_lang/src/analysis/tests/custom_attributes.rs b/vhdl_lang/src/analysis/tests/custom_attributes.rs index 9124303f..de08878a 100644 --- a/vhdl_lang/src/analysis/tests/custom_attributes.rs +++ b/vhdl_lang/src/analysis/tests/custom_attributes.rs @@ -186,10 +186,9 @@ end architecture; let diagnostics = builder.analyze(); check_diagnostics( diagnostics, - vec![Diagnostic::new( + vec![Diagnostic::mismatched_kinds( code.s1("std'myattr"), "library std may not be the prefix of a user defined attribute", - ErrorCode::MismatchedKinds, )], ); } diff --git a/vhdl_lang/src/analysis/tests/package_instance.rs b/vhdl_lang/src/analysis/tests/package_instance.rs index 72556b61..9186905f 100644 --- a/vhdl_lang/src/analysis/tests/package_instance.rs +++ b/vhdl_lang/src/analysis/tests/package_instance.rs @@ -66,15 +66,13 @@ end package; check_diagnostics( diagnostics, vec![ - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s1("work.pkg"), "'work.pkg' is not an uninstantiated generic package", - ErrorCode::MismatchedKinds, ), - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s1("work.pkg.const"), "'work.pkg.const' is not an uninstantiated generic package", - ErrorCode::MismatchedKinds, ), ], ); @@ -172,20 +170,17 @@ package bad_pkg2 is new work.gpkg check_diagnostics( diagnostics, vec![ - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s("16#bad#", 1), "Cannot map expression to type generic", - ErrorCode::MismatchedKinds, ), - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s1("natural"), "subtype 'NATURAL' cannot be used in an expression", - ErrorCode::MismatchedKinds, ), - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s1("=> work").s1("work"), - "Expected type name, got library libname", - ErrorCode::MismatchedKinds, + "Expected type, got library libname", ), ], ); @@ -295,10 +290,9 @@ end package; let diagnostics = builder.analyze(); check_diagnostics( diagnostics, - vec![Diagnostic::new( + vec![Diagnostic::mismatched_kinds( code.s1("character"), "Cannot map type 'CHARACTER' to subprogram generic", - ErrorCode::MismatchedKinds, )], ); } @@ -331,10 +325,9 @@ end package; let diagnostics = builder.analyze(); check_diagnostics( diagnostics, - vec![Diagnostic::new( + vec![Diagnostic::mismatched_kinds( code.sa("to_string => ", "my_to_string"), "Cannot map 'my_to_string' to subprogram generic to_string[INTEGER return STRING]", - ErrorCode::MismatchedKinds, ) .related( code.s1("my_to_string"), diff --git a/vhdl_lang/src/analysis/tests/resolves_names.rs b/vhdl_lang/src/analysis/tests/resolves_names.rs index 6fcfddb1..7b0a18d2 100644 --- a/vhdl_lang/src/analysis/tests/resolves_names.rs +++ b/vhdl_lang/src/analysis/tests/resolves_names.rs @@ -1732,10 +1732,9 @@ attribute bad of ram : signal is 0; let diagnostics = builder.analyze(); check_diagnostics( diagnostics, - vec![Diagnostic::new( + vec![Diagnostic::mismatched_kinds( code.s1("attribute bad").s1("bad"), "constant 'bad' is not an attribute", - ErrorCode::MismatchedKinds, )], ); } diff --git a/vhdl_lang/src/analysis/tests/resolves_type_mark.rs b/vhdl_lang/src/analysis/tests/resolves_type_mark.rs index 3973abc3..e27b3cfa 100644 --- a/vhdl_lang/src/analysis/tests/resolves_type_mark.rs +++ b/vhdl_lang/src/analysis/tests/resolves_type_mark.rs @@ -508,10 +508,6 @@ pub fn kind_error( expected: &str, got: &str, ) -> Diagnostic { - Diagnostic::new( - code.s(name, occ), - format!("Expected {expected}, got {got}"), - ErrorCode::MismatchedKinds, - ) - .related(code.s(name, occ_decl), "Defined here") + Diagnostic::mismatched_kinds(code.s(name, occ), format!("Expected {expected}, got {got}")) + .related(code.s(name, occ_decl), "Defined here") } diff --git a/vhdl_lang/src/analysis/tests/subprogram_arguments.rs b/vhdl_lang/src/analysis/tests/subprogram_arguments.rs index 19b7a45d..42f811e6 100644 --- a/vhdl_lang/src/analysis/tests/subprogram_arguments.rs +++ b/vhdl_lang/src/analysis/tests/subprogram_arguments.rs @@ -74,10 +74,9 @@ end architecture; code.s("subpgm", 1), "function subpgm[NATURAL return NATURAL] is not a procedure", ), - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s("thesig", 2), "signal 'thesig' of array type 'INTEGER_VECTOR' is not a procedure", - ErrorCode::MismatchedKinds, ), ], ); diff --git a/vhdl_lang/src/analysis/tests/subprogram_instance.rs b/vhdl_lang/src/analysis/tests/subprogram_instance.rs index 17ffc6ec..222138c6 100644 --- a/vhdl_lang/src/analysis/tests/subprogram_instance.rs +++ b/vhdl_lang/src/analysis/tests/subprogram_instance.rs @@ -47,10 +47,9 @@ function proc is new x; let diagnostics = builder.analyze(); assert_eq!( diagnostics, - vec![Diagnostic::new( + vec![Diagnostic::mismatched_kinds( code.s1("new x").s1("x"), "signal 'x' does not denote an uninstantiated subprogram", - ErrorCode::MismatchedKinds )] ) } @@ -247,10 +246,9 @@ procedure proc is new proc; check_diagnostics( builder.analyze(), - vec![Diagnostic::new( + vec![Diagnostic::mismatched_kinds( code.s1("procedure proc is new").s("proc", 2).pos(), "procedure proc[] does not denote an uninstantiated subprogram", - ErrorCode::MismatchedKinds, )], ); } diff --git a/vhdl_lang/src/analysis/tests/typecheck_expression.rs b/vhdl_lang/src/analysis/tests/typecheck_expression.rs index 0aedd508..1574eeed 100644 --- a/vhdl_lang/src/analysis/tests/typecheck_expression.rs +++ b/vhdl_lang/src/analysis/tests/typecheck_expression.rs @@ -446,10 +446,9 @@ constant bar : natural := foo(0); let diagnostics = builder.analyze(); check_diagnostics( diagnostics, - vec![Diagnostic::new( + vec![Diagnostic::mismatched_kinds( code.s("foo", 2), "constant 'foo' of subtype 'NATURAL' cannot be indexed", - ErrorCode::MismatchedKinds, )], ); } @@ -467,10 +466,9 @@ constant bar : natural := foo(0 to 0); let diagnostics = builder.analyze(); check_diagnostics( diagnostics, - vec![Diagnostic::new( + vec![Diagnostic::mismatched_kinds( code.s("foo", 2), "constant 'foo' of subtype 'NATURAL' cannot be sliced", - ErrorCode::MismatchedKinds, )], ); } @@ -668,15 +666,13 @@ subtype bad2_t is integer'element; "character literal does not match integer type 'INTEGER'", ErrorCode::TypeMismatch, ), - Diagnostic::new( + Diagnostic::illegal_attribute( code.s1("good2'element").s1("good2"), "array type expected for 'element attribute", - ErrorCode::TypeMismatch, ), - Diagnostic::new( + Diagnostic::illegal_attribute( code.s1("integer'element").s1("integer"), "array type expected for 'element attribute", - ErrorCode::TypeMismatch, ), ], ); @@ -938,20 +934,17 @@ constant bad3 : rec_t := (field | 0 => 0); check_diagnostics( diagnostics, vec![ - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s1("field(0)"), "Record aggregate choice must be a simple name", - ErrorCode::MismatchedKinds, ), - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s1("0 to 1"), "Record aggregate choice must be a simple name", - ErrorCode::MismatchedKinds, ), - Diagnostic::new( + Diagnostic::mismatched_kinds( code.s1("field | 0"), "Record aggregate choice must be a simple name", - ErrorCode::MismatchedKinds, ), ], ); diff --git a/vhdl_lang/src/analysis/tests/view_declarations.rs b/vhdl_lang/src/analysis/tests/view_declarations.rs index 521daea9..8795d9aa 100644 --- a/vhdl_lang/src/analysis/tests/view_declarations.rs +++ b/vhdl_lang/src/analysis/tests/view_declarations.rs @@ -246,10 +246,9 @@ end entity; let diag = builder.analyze(); check_diagnostics( diag, - vec![Diagnostic::new( + vec![Diagnostic::mismatched_kinds( code.s1("bit"), "type 'BIT' is not a view", - ErrorCode::MismatchedKinds, )], ); diff --git a/vhdl_lang/src/analysis/types.rs b/vhdl_lang/src/analysis/types.rs index b8573f22..3416e1b7 100644 --- a/vhdl_lang/src/analysis/types.rs +++ b/vhdl_lang/src/analysis/types.rs @@ -26,7 +26,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> { .. } = subtype_indication; - let base_type = self.resolve_type_mark(scope, type_mark, diagnostics)?; + let base_type = self.type_name(scope, type_mark.span, &mut type_mark.item, diagnostics)?; if let Some(constraint) = constraint { self.analyze_subtype_constraint( @@ -502,9 +502,12 @@ impl<'a, 't> AnalyzeContext<'a, 't> { self.source(), ); - if let Some(type_mark) = - as_fatal(self.resolve_type_mark(scope, type_mark, diagnostics))? - { + if let Some(type_mark) = as_fatal(self.type_name( + scope, + type_mark.span, + &mut type_mark.item, + diagnostics, + ))? { for ent in self.create_implicit_file_type_subprograms(file_type, type_mark) { unsafe { self.arena.add_implicit(file_type.id(), ent); diff --git a/vhdl_lang/src/ast.rs b/vhdl_lang/src/ast.rs index 110863be..78f0ddf5 100644 --- a/vhdl_lang/src/ast.rs +++ b/vhdl_lang/src/ast.rs @@ -271,7 +271,7 @@ pub enum Allocator { /// LRM 9.3.5 Qualified expressions #[derive(PartialEq, Debug, Clone)] pub struct QualifiedExpression { - pub type_mark: WithTokenSpan, + pub type_mark: WithTokenSpan, pub expr: WithTokenSpan, } @@ -317,7 +317,7 @@ pub enum Direction { /// | simple_expression direction simple_expression #[derive(PartialEq, Debug, Clone)] pub enum DiscreteRange { - Discrete(WithTokenSpan, Option), + Discrete(WithTokenSpan, Option), Range(Range), } @@ -368,17 +368,11 @@ pub enum ResolutionIndication { Unresolved, } -#[derive(PartialEq, Debug, Clone)] -pub struct TypeMark { - pub name: WithTokenSpan, - pub attr: Option, -} - /// LRM 6.3 Subtype declarations #[derive(PartialEq, Debug, Clone)] pub struct SubtypeIndication { pub resolution: ResolutionIndication, - pub type_mark: WithTokenSpan, + pub type_mark: WithTokenSpan, pub constraint: Option>, } @@ -387,7 +381,7 @@ pub struct SubtypeIndication { pub enum ArrayIndex { /// Unbounded /// {identifier} range <> - IndexSubtypeDefintion(WithTokenSpan), + IndexSubtypeDefintion(WithTokenSpan), /// Constraint Discrete(DiscreteRange), @@ -484,7 +478,7 @@ pub struct AliasDeclaration { #[derive(PartialEq, Debug, Clone)] pub struct AttributeDeclaration { pub ident: WithDecl, - pub type_mark: WithTokenSpan, + pub type_mark: WithTokenSpan, } /// LRM 7.2 Attribute specification @@ -592,7 +586,7 @@ pub enum TypeDefinition { /// LRM 5.4.2 Incomplete type declarations Incomplete(Reference), /// LRM 5.5 File types - File(WithTokenSpan), + File(WithTokenSpan), /// LRM 5.6 Protected types Protected(ProtectedTypeDeclaration), ProtectedBody(ProtectedTypeBody), @@ -668,7 +662,7 @@ pub struct FunctionSpecification { // The `parameter` token, if such a token exists pub param_tok: Option, pub parameter_list: Vec, - pub return_type: WithTokenSpan, + pub return_type: WithTokenSpan, } /// LRM 4.3 Subprogram bodies @@ -712,8 +706,8 @@ pub struct SubprogramInstantiation { /// LRM 4.5.3 Signatures #[derive(PartialEq, Debug, Clone)] pub enum Signature { - Function(Vec>, WithTokenSpan), - Procedure(Vec>), + Function(Vec>, WithTokenSpan), + Procedure(Vec>), } #[derive(PartialEq, Debug, Clone, TokenSpan)] diff --git a/vhdl_lang/src/ast/display.rs b/vhdl_lang/src/ast/display.rs index c0ff64a8..b7b15870 100644 --- a/vhdl_lang/src/ast/display.rs +++ b/vhdl_lang/src/ast/display.rs @@ -598,16 +598,6 @@ impl Display for SubtypeIndication { } } -impl Display for TypeMark { - fn fmt(&self, f: &mut Formatter<'_>) -> Result { - write!(f, "{}", self.name)?; - if let Some(attr) = self.attr { - write!(f, "'{attr}")?; - } - Ok(()) - } -} - impl Display for ArrayIndex { fn fmt(&self, f: &mut Formatter<'_>) -> Result { match self { diff --git a/vhdl_lang/src/ast/search.rs b/vhdl_lang/src/ast/search.rs index daaeba8c..4c379998 100644 --- a/vhdl_lang/src/ast/search.rs +++ b/vhdl_lang/src/ast/search.rs @@ -683,13 +683,6 @@ impl Search for SubtypeIndication { } } -impl Search for WithTokenSpan { - fn search(&self, ctx: &dyn TokenAccess, searcher: &mut impl Searcher) -> SearchResult { - return_if_finished!(searcher.search_with_pos(ctx, &self.pos(ctx))); - self.item.name.search(ctx, searcher) - } -} - impl Search for RangeConstraint { fn search(&self, ctx: &dyn TokenAccess, searcher: &mut impl Searcher) -> SearchResult { let RangeConstraint { diff --git a/vhdl_lang/src/data/error_codes.rs b/vhdl_lang/src/data/error_codes.rs index f06280de..3eb776a0 100644 --- a/vhdl_lang/src/data/error_codes.rs +++ b/vhdl_lang/src/data/error_codes.rs @@ -569,6 +569,14 @@ impl Diagnostic { pub fn internal(item: impl AsRef, msg: impl Into) -> Diagnostic { Self::new(item, msg, ErrorCode::Internal) } + + pub fn illegal_attribute(item: impl AsRef, msg: impl Into) -> Diagnostic { + Self::new(item, msg, ErrorCode::IllegalAttribute) + } + + pub fn mismatched_kinds(item: impl AsRef, msg: impl Into) -> Diagnostic { + Self::new(item, msg, ErrorCode::MismatchedKinds) + } } impl Display for ErrorCode { diff --git a/vhdl_lang/src/named_entity/types.rs b/vhdl_lang/src/named_entity/types.rs index 070c6952..59dc436b 100644 --- a/vhdl_lang/src/named_entity/types.rs +++ b/vhdl_lang/src/named_entity/types.rs @@ -234,13 +234,12 @@ impl<'a> TypeEnt<'a> { Type::Protected(region, _) => { if let Some(decl) = region.lookup_immediate(suffix.designator()) { match decl { - NamedEntities::Single(ent) => Err(Diagnostic::new( + NamedEntities::Single(ent) => Err(Diagnostic::mismatched_kinds( suffix.pos(ctx), format!( "Protected type selection must be a method, got {}", ent.describe() ), - ErrorCode::MismatchedKinds, )), NamedEntities::Overloaded(overloaded) => { Ok(TypedSelection::ProtectedMethod(overloaded.clone())) diff --git a/vhdl_lang/src/syntax/expression.rs b/vhdl_lang/src/syntax/expression.rs index 0a7a787c..0c2e6734 100644 --- a/vhdl_lang/src/syntax/expression.rs +++ b/vhdl_lang/src/syntax/expression.rs @@ -295,25 +295,23 @@ fn parse_allocator(ctx: &mut ParsingContext<'_>) -> ParseResult, name: WithTokenSpan, -) -> ParseResult> { +) -> ParseResult> { let pos = name.pos(ctx); - let name_span = name.span; let type_mark = name .try_map_into(|name| match name { Name::Attribute(attr) => { - if let Some(typattr) = attr.as_type() { - Some(TypeMark { + if attr.as_type().is_some() { + Some(Name::Attribute(Box::new(AttributeName { name: attr.name.try_map_into(name_to_selected_name)?, - attr: Some(typattr), - }) + attr: attr.attr, + expr: None, + signature: None, + }))) } else { None } } - _ => Some(TypeMark { - name: WithTokenSpan::from(name_to_selected_name(name)?, name_span), - attr: None, - }), + _ => Some(name_to_selected_name(name)?), }) .ok_or_else(|| Diagnostic::syntax_error(&pos, "Expected type mark"))?; diff --git a/vhdl_lang/src/syntax/names.rs b/vhdl_lang/src/syntax/names.rs index c0b26d46..6dfaca0a 100644 --- a/vhdl_lang/src/syntax/names.rs +++ b/vhdl_lang/src/syntax/names.rs @@ -51,7 +51,7 @@ pub fn parse_selected_name(ctx: &mut ParsingContext<'_>) -> ParseResult) -> ParseResult> { +pub fn parse_type_mark(ctx: &mut ParsingContext<'_>) -> ParseResult> { let name = parse_selected_name(ctx)?; parse_type_mark_starting_with_name(ctx, name) } @@ -59,7 +59,7 @@ pub fn parse_type_mark(ctx: &mut ParsingContext<'_>) -> ParseResult, name: WithTokenSpan, -) -> ParseResult> { +) -> ParseResult> { let state = ctx.stream.state(); let name_span = name.span; @@ -67,13 +67,16 @@ pub fn parse_type_mark_starting_with_name( // Example: signal sig0 : sig1'subtype; if ctx.stream.pop_if_kind(Tick).is_some() { if let Ok(attr) = ctx.stream.expect_attribute_designator() { - if let AttributeDesignator::Type(typattr) = attr.item { + let token = attr.token; + if let AttributeDesignator::Type(_) = attr.item { return Ok(WithTokenSpan { - item: TypeMark { + item: Name::Attribute(Box::new(AttributeName { name, - attr: Some(typattr), - }, - span: name_span.end_with(attr.token), + attr, + signature: None, + expr: None, + })), + span: name_span.end_with(token), }); } } @@ -81,10 +84,7 @@ pub fn parse_type_mark_starting_with_name( ctx.stream.set_state(state); }; - Ok(WithTokenSpan { - item: TypeMark { name, attr: None }, - span: name_span, - }) + Ok(name) } impl Name { @@ -875,13 +875,7 @@ mod tests { let code = Code::new("prefix"); let name = code.s1("prefix").name(); - assert_eq!( - code.with_stream(parse_type_mark), - WithTokenSpan { - span: name.span, - item: TypeMark { name, attr: None }, - } - ); + assert_eq!(code.with_stream(parse_type_mark), name,); } #[test] @@ -890,13 +884,7 @@ mod tests { assert_eq!( code.with_stream(parse_type_mark), - WithTokenSpan { - span: code.token_span(), - item: TypeMark { - name: code.s1("prefix").name(), - attr: Some(TypeAttribute::Subtype) - }, - } + code.s1("prefix'subtype").name() ); } @@ -906,13 +894,7 @@ mod tests { assert_eq!( code.with_stream(parse_type_mark), - WithTokenSpan { - span: code.token_span(), - item: TypeMark { - name: code.s1("prefix").name(), - attr: Some(TypeAttribute::Element) - }, - } + code.s1("prefix'element").name() ); } diff --git a/vhdl_lang/src/syntax/test.rs b/vhdl_lang/src/syntax/test.rs index 51ab6c5e..f65daf3f 100644 --- a/vhdl_lang/src/syntax/test.rs +++ b/vhdl_lang/src/syntax/test.rs @@ -604,7 +604,7 @@ impl Code { self.parse_ok_no_diagnostics(parse_ident_list) } - pub fn type_mark(&self) -> WithTokenSpan { + pub fn type_mark(&self) -> WithTokenSpan { self.parse_ok_no_diagnostics(parse_type_mark) }