Fix: discrete ranges can be used as choice statements (#354)

This commit is contained in:
Lukas Scheller 2024-12-09 10:19:40 +01:00 committed by GitHub
parent 5cf0e4c622
commit 7ed5e78ea8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
40 changed files with 377 additions and 225 deletions

View file

@ -12,7 +12,7 @@ use crate::ast::*;
use crate::data::*;
use crate::named_entity::*;
impl<'a, 't> AnalyzeContext<'a, 't> {
impl<'a> AnalyzeContext<'a, '_> {
// @TODO maybe make generic function for expression/waveform.
// wait until type checking to see if it makes sense
pub fn analyze_expr_assignment(
@ -55,7 +55,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
} in alternatives.iter_mut()
{
self.analyze_expression_for_target(scope, ttyp, item, diagnostics)?;
self.choice_with_ttyp(scope, ctyp, choices, diagnostics)?;
self.choices_with_ttyp(scope, ctyp, choices, diagnostics)?;
}
}
}
@ -105,7 +105,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
} in alternatives.iter_mut()
{
self.analyze_waveform(scope, ttyp, item, diagnostics)?;
self.choice_with_ttyp(scope, ctyp, choices, diagnostics)?;
self.choices_with_ttyp(scope, ctyp, choices, diagnostics)?;
}
}
}

View file

@ -84,7 +84,7 @@ impl<'a> ResolvedFormal<'a> {
}
}
impl<'a, 't> AnalyzeContext<'a, 't> {
impl<'a> AnalyzeContext<'a, '_> {
fn resolve_formal(
&self,
formal_region: &FormalRegion<'a>,

View file

@ -14,7 +14,7 @@ use crate::named_entity::*;
use crate::{HasTokenSpan, TokenSpan};
use analyze::*;
impl<'a, 't> AnalyzeContext<'a, 't> {
impl<'a> AnalyzeContext<'a, '_> {
pub fn analyze_concurrent_part(
&self,
scope: &Scope<'a>,
@ -188,7 +188,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
ref mut item,
span: _,
} = alternative;
self.choice_with_ttyp(scope, ctyp, choices, diagnostics)?;
self.choices_with_ttyp(scope, ctyp, choices, diagnostics)?;
let nested = scope.nested();
self.analyze_generate_body(&nested, parent, item, src_span, diagnostics)?;
}

View file

@ -134,7 +134,7 @@ impl Declaration {
}
}
impl<'a, 't> AnalyzeContext<'a, 't> {
impl<'a> AnalyzeContext<'a, '_> {
pub fn analyze_declarative_part(
&self,
scope: &Scope<'a>,

View file

@ -14,7 +14,7 @@ use crate::named_entity::*;
use crate::HasTokenSpan;
use analyze::*;
impl<'a, 't> AnalyzeContext<'a, 't> {
impl<'a> AnalyzeContext<'a, '_> {
pub fn analyze_primary_unit(
&self,
unit: &mut AnyPrimaryUnit,

View file

@ -69,7 +69,7 @@ pub(super) struct TypeMatcher<'c, 'a, 't> {
context: &'c AnalyzeContext<'a, 't>,
}
impl<'c, 'a, 't> TypeMatcher<'c, 'a, 't> {
impl<'a> TypeMatcher<'_, 'a, '_> {
// Returns true if the expression types is possible given the target type
pub fn is_possible(&self, types: &ExpressionType<'a>, ttyp: BaseType<'a>) -> bool {
if types.match_type(ttyp) {
@ -213,6 +213,22 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
self.any_matcher().can_be_target_type(typ, ttyp)
}
pub fn check_type_mismatch(
&self,
typ: TypeEnt<'a>,
ttyp: TypeEnt<'a>,
pos: TokenSpan,
diagnostics: &mut dyn DiagnosticHandler,
) {
if !self.can_be_target_type(typ, ttyp.base()) {
diagnostics.push(Diagnostic::type_mismatch(
&pos.pos(self.ctx),
&typ.describe(),
ttyp,
));
}
}
pub fn expr_unknown_ttyp(
&self,
scope: &Scope<'a>,
@ -684,7 +700,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
"Ambiguous use of implicit boolean conversion ??",
ErrorCode::AmbiguousCall,
);
diag.add_type_candididates("Could be", implicit_bool_types);
diag.add_type_candidates("Could be", implicit_bool_types);
diagnostics.push(diag);
}
@ -697,7 +713,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
),
ErrorCode::AmbiguousExpression,
);
diag.add_type_candididates(
diag.add_type_candidates(
"Implicit boolean conversion operator ?? is not defined for",
types,
);
@ -741,13 +757,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
if let Some(type_mark) =
as_fatal(self.analyze_qualified_expression(scope, qexpr, diagnostics))?
{
if !self.can_be_target_type(type_mark, target_base.base()) {
diagnostics.push(Diagnostic::type_mismatch(
&span.pos(self.ctx),
&type_mark.describe(),
target_type,
));
}
self.check_type_mismatch(type_mark, target_type, span, diagnostics);
}
}
Expression::Binary(ref mut op, ref mut left, ref mut right) => {
@ -772,14 +782,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
))? {
Some(Disambiguated::Unambiguous(overloaded)) => {
let op_type = overloaded.return_type().unwrap();
if !self.can_be_target_type(op_type, target_type.base()) {
diagnostics.push(Diagnostic::type_mismatch(
&span.pos(self.ctx),
&op_type.describe(),
target_type,
));
}
self.check_type_mismatch(op_type, target_type, span, diagnostics);
}
Some(Disambiguated::Ambiguous(candidates)) => {
diagnostics.push(Diagnostic::ambiguous_op(
@ -815,14 +818,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
))? {
Some(Disambiguated::Unambiguous(overloaded)) => {
let op_type = overloaded.return_type().unwrap();
if !self.can_be_target_type(op_type, target_type.base()) {
diagnostics.push(Diagnostic::type_mismatch(
&span.pos(self.ctx),
&op_type.describe(),
target_type,
));
}
self.check_type_mismatch(op_type, target_type, span, diagnostics);
}
Some(Disambiguated::Ambiguous(candidates)) => {
diagnostics.push(Diagnostic::ambiguous_op(

View file

@ -14,7 +14,7 @@ use crate::data::*;
use crate::named_entity::*;
use crate::TokenSpan;
impl<'a, 't> AnalyzeContext<'a, 't> {
impl<'a> AnalyzeContext<'a, '_> {
/// Analyze a string literal or expanded bit-string literal for type-matching
fn analyze_string_literal(
&self,

View file

@ -112,7 +112,7 @@ pub struct ReadGuard<'a, T, R> {
guard: RwLockReadGuard<'a, AnalysisState<T, R>>,
}
impl<'a, T, R> ReadGuard<'a, T, R> {
impl<T, R> ReadGuard<'_, T, R> {
pub fn result(&self) -> &R {
self.guard.result.as_ref().unwrap()
}
@ -122,7 +122,7 @@ impl<'a, T, R> ReadGuard<'a, T, R> {
}
}
impl<'a, T, R> std::ops::Deref for ReadGuard<'a, T, R> {
impl<T, R> std::ops::Deref for ReadGuard<'_, T, R> {
type Target = T;
fn deref(&self) -> &Self::Target {
@ -148,7 +148,7 @@ impl<'a, T, R> WriteGuard<'a, T, R> {
}
}
impl<'a, T, R> std::ops::Deref for WriteGuard<'a, T, R> {
impl<T, R> std::ops::Deref for WriteGuard<'_, T, R> {
type Target = T;
fn deref(&self) -> &Self::Target {
@ -156,7 +156,7 @@ impl<'a, T, R> std::ops::Deref for WriteGuard<'a, T, R> {
}
}
impl<'a, T, R> std::ops::DerefMut for WriteGuard<'a, T, R> {
impl<T, R> std::ops::DerefMut for WriteGuard<'_, T, R> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.guard.data
}

View file

@ -292,46 +292,49 @@ impl<'a> ResolvedName<'a> {
/// This is used in contexts where the type is not relevant
/// Such as when assigning to a constant
pub fn describe(&self) -> String {
use ResolvedName::*;
match self {
ResolvedName::Library(sym) => format!("library {sym}"),
ResolvedName::Design(ent) => ent.describe(),
ResolvedName::Type(ent) => ent.describe(),
ResolvedName::Overloaded(des, name) => {
Library(sym) => format!("library {sym}"),
Design(ent) => ent.describe(),
Type(ent) => ent.describe(),
Overloaded(des, name) => {
if let Some(ent) = name.as_unique() {
ent.describe()
} else {
format!("Overloaded name {des}")
}
}
ResolvedName::ObjectName(oname) => oname.base.describe(),
ResolvedName::Final(ent) => ent.describe(),
ResolvedName::Expression(DisambiguatedType::Unambiguous(_)) => "Expression".to_owned(),
ResolvedName::Expression(_) => "Ambiguous expression".to_owned(),
ObjectName(oname) => oname.base.describe(),
Final(ent) => ent.describe(),
Expression(DisambiguatedType::Unambiguous(_)) => "Expression".to_owned(),
Expression(_) => "Ambiguous expression".to_owned(),
}
}
pub fn decl_pos(&self) -> Option<&SrcPos> {
use ResolvedName::*;
match self {
ResolvedName::Library(_) => None,
ResolvedName::Design(design) => design.decl_pos(),
ResolvedName::Type(typ) => typ.decl_pos(),
ResolvedName::Overloaded(_, names) => names.as_unique().and_then(|it| it.decl_pos()),
ResolvedName::ObjectName(name) => match name.base {
Library(_) => None,
Design(design) => design.decl_pos(),
Type(typ) => typ.decl_pos(),
Overloaded(_, names) => names.as_unique().and_then(|it| it.decl_pos()),
ObjectName(name) => match name.base {
ObjectBase::Object(ent) => ent.decl_pos(),
ObjectBase::DeferredConstant(ent) | ObjectBase::ObjectAlias(_, ent) => {
ent.decl_pos()
}
ObjectBase::ExternalName(_) => None,
},
ResolvedName::Expression(_) | ResolvedName::Final(_) => None,
Expression(_) | Final(_) => None,
}
}
fn type_mark(&self) -> Option<TypeEnt<'a>> {
use ResolvedName::*;
match self {
ResolvedName::Type(typ) => Some(*typ),
ResolvedName::ObjectName(oname) => Some(oname.type_mark()),
ResolvedName::Expression(DisambiguatedType::Unambiguous(typ)) => Some(*typ),
Type(typ) => Some(*typ),
ObjectName(oname) => Some(oname.type_mark()),
Expression(DisambiguatedType::Unambiguous(typ)) => Some(*typ),
_ => None,
}
}
@ -380,21 +383,22 @@ impl<'a> ResolvedName<'a> {
Err(EvalError::Unknown)
}
// The actual underlying entity
/// Returns the entity that this resolved name represents.
/// For example, when referencing a constant from some other context like
/// `work.some_pkg.some_constant`, the `ResolvedName` is an `ObjectName`.
/// The underlying entity is then the entity representing `some_constant`.
fn as_actual_entity(&self) -> Option<EntRef<'a>> {
use ResolvedName::*;
match self {
ResolvedName::ObjectName(oname) => match oname.base {
ObjectName(oname) => match oname.base {
ObjectBase::Object(obj) => Some(*obj),
ObjectBase::ObjectAlias(obj, _) => Some(*obj),
ObjectBase::DeferredConstant(ent) => Some(ent),
ObjectBase::ExternalName(_) => None,
},
ResolvedName::Type(typ) => Some((*typ).into()),
ResolvedName::Design(des) => Some((*des).into()),
ResolvedName::Library(..) => None,
ResolvedName::Overloaded(_, _) => None,
ResolvedName::Expression(_) => None,
ResolvedName::Final(_) => None,
Type(typ) => Some((*typ).into()),
Design(des) => Some((*des).into()),
Library(..) | Overloaded(..) | Expression(_) | Final(_) => None,
}
}
}
@ -469,7 +473,7 @@ pub fn as_type_conversion(
None
}
impl<'a, 't> AnalyzeContext<'a, 't> {
impl<'a> AnalyzeContext<'a, '_> {
fn name_to_type(
&self,
pos: TokenSpan,
@ -517,7 +521,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
}
}
fn name_to_unambiguous_type(
pub(crate) fn name_to_unambiguous_type(
&self,
span: TokenSpan,
name: &ResolvedName<'a>,
@ -570,9 +574,12 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
/// An array type may be sliced with a type name
/// For the parser this looks like a call or indexed name
/// Example:
///
/// # Example:
/// ```vhdl
/// subtype sub_t is natural range 0 to 1;
/// arr(sub_t) := (others => 0);
/// ```
fn assoc_as_discrete_range_type(
&self,
scope: &Scope<'a>,
@ -1067,14 +1074,18 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
Err(EvalError::Unknown)
}
}
AttributeDesignator::Range(_) => {
diagnostics.add(
name_pos.pos(self.ctx),
"Range cannot be used as an expression",
ErrorCode::MismatchedKinds,
);
Err(EvalError::Unknown)
}
AttributeDesignator::Range(_) => match prefix {
ResolvedName::Type(typ) => Ok(ResolvedName::Type(*typ)),
ResolvedName::ObjectName(oname) => Ok(ResolvedName::Type(oname.type_mark())),
_ => {
diagnostics.add(
name_pos.pos(self.ctx),
format!("Range attribute cannot be used on {}", prefix.describe()),
ErrorCode::MismatchedKinds,
);
Err(EvalError::Unknown)
}
},
AttributeDesignator::Type(attr) => self
.resolve_type_attribute_suffix(prefix, prefix_pos, &attr, name_pos, diagnostics)
.map(|typ| ResolvedName::Type(typ.base().into())),
@ -1708,27 +1719,36 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
false,
diagnostics,
))? {
// @TODO target_type already used above, functions could probably be simplified
match self.name_to_unambiguous_type(span, &resolved, ttyp, name.suffix_reference_mut())
{
Ok(Some(type_mark)) => {
if !self.can_be_target_type(type_mark, ttyp.base()) {
diagnostics.push(Diagnostic::type_mismatch(
&span.pos(self.ctx),
&resolved.describe_type(),
ttyp,
));
}
}
Ok(None) => {}
Err(diag) => {
diagnostics.push(diag);
}
}
self.check_resolved_name_type(span, &resolved, ttyp, name, diagnostics);
}
Ok(())
}
pub fn check_resolved_name_type(
&self,
span: TokenSpan,
resolved: &ResolvedName<'a>,
ttyp: TypeEnt<'a>,
name: &mut Name,
diagnostics: &mut dyn DiagnosticHandler,
) {
match self.name_to_unambiguous_type(span, resolved, ttyp, name.suffix_reference_mut()) {
Ok(Some(type_mark)) => {
if !self.can_be_target_type(type_mark, ttyp.base()) {
diagnostics.push(Diagnostic::type_mismatch(
&span.pos(self.ctx),
&resolved.describe_type(),
ttyp,
));
}
}
Ok(None) => {}
Err(diag) => {
diagnostics.push(diag);
}
}
}
/// Analyze an indexed name where the prefix entity is already known
/// Returns the type of the array element
pub fn analyze_indexed_name(
@ -3075,30 +3095,6 @@ variable thevar : integer;
)
}
#[test]
fn range_attribute() {
let test = TestSetup::new();
test.declarative_part(
"
variable thevar : integer_vector(0 to 1);
",
);
let code = test.snippet("thevar'range");
let mut diagnostics = Vec::new();
assert_eq!(
test.name_resolve(&code, None, &mut diagnostics),
Err(EvalError::Unknown)
);
check_diagnostics(
diagnostics,
vec![Diagnostic::new(
code,
"Range cannot be used as an expression",
ErrorCode::MismatchedKinds,
)],
)
}
#[test]
fn name_attributes() {
let test = TestSetup::new();

View file

@ -186,7 +186,7 @@ impl<'a> AsRef<OverloadedEnt<'a>> for ResolvedCall<'a> {
}
}
impl<'a, 't> AnalyzeContext<'a, 't> {
impl<'a> AnalyzeContext<'a, '_> {
/// Typecheck one overloaded call where the exact subprogram is known
pub fn check_call(
&self,

View file

@ -23,7 +23,7 @@ use crate::named_entity::*;
use crate::Diagnostic;
use crate::NullDiagnostics;
impl<'a, 't> AnalyzeContext<'a, 't> {
impl<'a> AnalyzeContext<'a, '_> {
pub fn generic_package_instance(
&self,
scope: &Scope<'a>,

View file

@ -18,7 +18,7 @@ use crate::data::error_codes::ErrorCode;
use crate::data::*;
use crate::named_entity::*;
impl<'a, 't> AnalyzeContext<'a, 't> {
impl<'a> AnalyzeContext<'a, '_> {
pub fn range_unknown_typ(
&self,
scope: &Scope<'a>,

View file

@ -14,32 +14,66 @@ use crate::data::error_codes::ErrorCode;
use crate::data::*;
use crate::named_entity::*;
impl<'a, 't> AnalyzeContext<'a, 't> {
pub fn choice_with_ttyp(
impl<'a> AnalyzeContext<'a, '_> {
pub fn choices_with_ttyp(
&self,
scope: &Scope<'a>,
ttyp: Option<TypeEnt<'a>>,
choices: &mut [WithTokenSpan<Choice>],
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult {
for choice in choices.iter_mut() {
match choice.item {
Choice::Expression(ref mut expr) => {
if let Some(ttyp) = ttyp {
self.expr_pos_with_ttyp(scope, ttyp, choice.span, expr, diagnostics)?;
} else {
self.expr_pos_unknown_ttyp(scope, choice.span, expr, diagnostics)?;
choices
.iter_mut()
.try_for_each(|choice| self.choice_with_ttyp(scope, ttyp, choice, diagnostics))
}
pub fn choice_with_ttyp(
&self,
scope: &Scope<'a>,
ttyp: Option<TypeEnt<'a>>,
choice: &mut WithTokenSpan<Choice>,
diagnostics: &mut dyn DiagnosticHandler,
) -> FatalResult {
match choice.item {
Choice::Expression(ref mut expr) => {
// Check any names like `typ'range` before checking for any expressions
if let Expression::Name(name) = expr {
if let Some(resolved_name) =
as_fatal(self.name_resolve(scope, choice.span, name, diagnostics))?
{
match resolved_name {
ResolvedName::Type(typ) => {
if let Some(ttyp) = ttyp {
self.check_type_mismatch(typ, ttyp, choice.span, diagnostics);
}
}
_ => {
if let Some(ttyp) = ttyp {
self.check_resolved_name_type(
choice.span,
&resolved_name,
ttyp,
name,
diagnostics,
);
}
}
}
}
} else if let Some(ttyp) = ttyp {
self.expr_pos_with_ttyp(scope, ttyp, choice.span, expr, diagnostics)?;
} else {
self.expr_pos_unknown_ttyp(scope, choice.span, expr, diagnostics)?;
}
Choice::DiscreteRange(ref mut drange) => {
if let Some(ttyp) = ttyp {
self.drange_with_ttyp(scope, ttyp, drange, diagnostics)?;
} else {
self.drange_unknown_type(scope, drange, diagnostics)?;
}
}
Choice::Others => {}
}
Choice::DiscreteRange(ref mut drange) => {
if let Some(ttyp) = ttyp {
self.drange_with_ttyp(scope, ttyp, drange, diagnostics)?;
} else {
self.drange_unknown_type(scope, drange, diagnostics)?;
}
}
Choice::Others => {}
}
Ok(())
}
@ -180,7 +214,7 @@ impl Diagnostic {
}
}
pub fn add_type_candididates<'a>(
pub fn add_type_candidates<'a>(
&mut self,
prefix: &str,
candidates: impl IntoIterator<Item = BaseType<'a>>,
@ -196,7 +230,7 @@ impl Diagnostic {
}
}
impl<'a> ResolvedName<'a> {
impl ResolvedName<'_> {
pub(super) fn kind_error(&self, pos: impl AsRef<SrcPos>, expected: &str) -> Diagnostic {
let mut error = Diagnostic::mismatched_kinds(
pos,

View file

@ -13,7 +13,7 @@ use crate::HasTokenSpan;
use analyze::*;
use target::AssignmentType;
impl<'a, 't> AnalyzeContext<'a, 't> {
impl<'a> AnalyzeContext<'a, '_> {
pub fn define_labels_for_sequential_part(
&self,
scope: &Scope<'a>,
@ -252,7 +252,7 @@ impl<'a, 't> AnalyzeContext<'a, 't> {
item,
span: _,
} = alternative;
self.choice_with_ttyp(scope, ctyp, choices, diagnostics)?;
self.choices_with_ttyp(scope, ctyp, choices, diagnostics)?;
self.analyze_sequential_part(scope, parent, item, diagnostics)?;
}
}

View file

@ -169,7 +169,7 @@ impl StandardTypes {
}
}
impl<'a, 't> AnalyzeContext<'a, 't> {
impl<'a> AnalyzeContext<'a, '_> {
fn ident(&self, name: &str) -> Designator {
Designator::Identifier(self.root.symbol_utf8(name))
}

View file

@ -15,7 +15,7 @@ use analyze::*;
use itertools::Itertools;
use vhdl_lang::TokenSpan;
impl<'a, 't> AnalyzeContext<'a, 't> {
impl<'a> AnalyzeContext<'a, '_> {
fn subprogram_header(
&self,
scope: &Scope<'a>,

View file

@ -12,7 +12,7 @@ use vhdl_lang::TokenSpan;
/// examples:
/// target <= 1;
/// target(0).elem := 1
impl<'a, 't> AnalyzeContext<'a, 't> {
impl<'a> AnalyzeContext<'a, '_> {
pub fn resolve_target(
&self,
scope: &Scope<'a>,

View file

@ -0,0 +1,83 @@
use crate::analysis::tests::{check_no_diagnostics, LibraryBuilder};
use crate::data::ErrorCode;
use crate::syntax::test::check_diagnostics;
use crate::Diagnostic;
#[test]
fn allows_ranges_with_case_statements() {
let mut builder = LibraryBuilder::new();
builder.code(
"libname",
"
entity test is
end entity;
architecture rtl of test is
type t_my_enum is (A1, A2, A3);
subtype t_my_enum_range is t_my_enum range A1 to A2;
signal my_sig_sub : t_my_enum;
begin
process
begin
case my_sig_sub is
when t_my_enum'range => null;
when t_my_enum_range => null;
when t_my_enum_range'range => null;
when A1 to A2 => null;
when t_my_enum => null;
when others => null;
end case;
end process;
end architecture;
",
);
let diagnostics = builder.analyze();
check_no_diagnostics(&diagnostics);
}
#[test]
fn disallows_ranges_with_wrong_type() {
let mut builder = LibraryBuilder::new();
let code = builder.code(
"libname",
"
entity test is
end entity;
architecture rtl of test is
type t_my_enum is (A1, A2, A3);
type t_my_other_enum is (B1, B2, B3);
subtype t_my_other_enum_range is t_my_other_enum range B1 to B2;
signal my_sig_sub : t_my_enum;
begin
process
begin
case my_sig_sub is
when t_my_other_enum'range => null;
when t_my_other_enum_range => null;
when others => null;
end case;
end process;
end architecture;
",
);
check_diagnostics(
builder.analyze(),
vec![
Diagnostic::new(
code.s1("t_my_other_enum'range"),
"type 't_my_other_enum' does not match type 't_my_enum'",
ErrorCode::TypeMismatch,
),
Diagnostic::new(
code.s("t_my_other_enum_range", 2),
"subtype 't_my_other_enum_range' does not match type 't_my_enum'",
ErrorCode::TypeMismatch,
),
],
);
}

View file

@ -262,7 +262,7 @@ fn single(name: &str) -> NameHierarchy {
}
}
impl<'a> From<EntHierarchy<'a>> for NameHierarchy {
impl From<EntHierarchy<'_>> for NameHierarchy {
fn from(ent: EntHierarchy<'_>) -> Self {
NameHierarchy {
name: if matches!(ent.ent.designator(), Designator::Anonymous(_)) {

View file

@ -11,6 +11,7 @@ mod context_clause;
mod custom_attributes;
mod declarations;
mod deferred_constant;
mod discrete_ranges;
mod hierarchy;
mod homographs;
mod implicit;

View file

@ -2185,3 +2185,18 @@ end package;
);
check_no_diagnostics(&builder.analyze())
}
#[test]
fn attribute_after_range() {
let mut builder = LibraryBuilder::new();
builder.code(
"libname",
r#"
package foo is
constant x: bit_vector(12 downto 0) := "0000000000000";
constant y: natural := x'range'high;
end package;
"#,
);
check_no_diagnostics(&builder.analyze())
}

View file

@ -12,7 +12,7 @@ use crate::named_entity::{Signature, *};
use crate::HasTokenSpan;
use analyze::*;
impl<'a, 't> AnalyzeContext<'a, 't> {
impl<'a> AnalyzeContext<'a, '_> {
pub fn resolve_subtype_indication(
&self,
scope: &Scope<'a>,

View file

@ -469,7 +469,7 @@ impl<T> WithDecl<T> {
}
impl<T> WithDecl<WithToken<T>> {
pub fn pos<'a>(&'a self, ctx: &'a dyn TokenAccess) -> &SrcPos {
pub fn pos<'a>(&'a self, ctx: &'a dyn TokenAccess) -> &'a SrcPos {
self.tree.pos(ctx)
}
}
@ -759,7 +759,7 @@ pub enum Signature {
#[derive(PartialEq, Debug, Clone, TokenSpan)]
pub enum SubprogramSpecification {
Procedure(ProcedureSpecification),
Procedure(Box<ProcedureSpecification>),
Function(FunctionSpecification),
}

View file

@ -1,6 +1,7 @@
use crate::ast::token_range::WithTokenSpan;
use crate::ast::{LabeledConcurrentStatement, LabeledSequentialStatement, WithDecl};
use crate::{HasTokenSpan, TokenId};
use std::ops::Deref;
use vhdl_lang::ast::WithToken;
use vhdl_lang::TokenSpan;
@ -54,6 +55,19 @@ impl<T> HasTokenSpan for WithTokenSpan<T> {
}
}
impl<T> HasTokenSpan for Box<T>
where
T: HasTokenSpan,
{
fn get_start_token(&self) -> TokenId {
self.deref().get_start_token()
}
fn get_end_token(&self) -> TokenId {
self.deref().get_end_token()
}
}
impl HasTokenSpan for LabeledConcurrentStatement {
fn get_start_token(&self) -> TokenId {
if let Some(label) = &self.label.tree {

View file

@ -1603,7 +1603,7 @@ impl<'a> ItemAtCursor<'a> {
}
}
impl<'a> Searcher for ItemAtCursor<'a> {
impl Searcher for ItemAtCursor<'_> {
fn search_with_pos(&mut self, _ctx: &dyn TokenAccess, pos: &SrcPos) -> SearchState {
// cursor is the gap between character cursor and cursor + 1
// Thus cursor will match character cursor and cursor + 1
@ -1722,7 +1722,7 @@ impl<'a> FormatDeclaration<'a> {
}
}
impl<'a> Searcher for FormatDeclaration<'a> {
impl Searcher for FormatDeclaration<'_> {
fn search_decl(&mut self, _ctx: &dyn TokenAccess, decl: FoundDeclaration<'_>) -> SearchState {
let id = if let Some(id) = decl.ent_id() {
id
@ -1780,7 +1780,7 @@ impl<'a> FindAllReferences<'a> {
}
}
impl<'a> Searcher for FindAllReferences<'a> {
impl Searcher for FindAllReferences<'_> {
fn search_decl(&mut self, ctx: &dyn TokenAccess, decl: FoundDeclaration<'_>) -> SearchState {
if let Some(id) = decl.ent_id() {
let other = self.root.get_ent(id);
@ -1813,7 +1813,7 @@ impl<'a> Searcher for FindAllReferences<'a> {
}
}
impl<'a> FoundDeclaration<'a> {
impl FoundDeclaration<'_> {
fn end_ident_pos(&self) -> Option<TokenId> {
match &self.ast {
DeclarationItem::InterfaceObject(_) => None,
@ -1863,7 +1863,7 @@ impl SubprogramSpecification {
}
}
impl<'a> HasEntityId for FoundDeclaration<'a> {
impl HasEntityId for FoundDeclaration<'_> {
fn ent_id(&self) -> Option<EntityId> {
self.ent_id_ref().get()
}

View file

@ -120,7 +120,7 @@ pub trait HasIdent {
&self.ident().item
}
fn ident_pos<'a>(&'a self, ctx: &'a dyn TokenAccess) -> &SrcPos {
fn ident_pos<'a>(&'a self, ctx: &'a dyn TokenAccess) -> &'a SrcPos {
self.ident().pos(ctx)
}
}

View file

@ -45,7 +45,7 @@ impl<'a> CompletionSearcher<'a> {
}
}
impl<'a> CompletionSearcher<'a> {
impl CompletionSearcher<'_> {
/// Add entity instantiation completions that are visible from within an architecture body
fn add_entity_instantiations(&mut self, body: &ArchitectureBody) {
let Some(ent_id) = body.ident.decl.get() else {
@ -59,7 +59,7 @@ impl<'a> CompletionSearcher<'a> {
}
}
impl<'a> Searcher for CompletionSearcher<'a> {
impl Searcher for CompletionSearcher<'_> {
fn search_decl(&mut self, ctx: &dyn TokenAccess, decl: FoundDeclaration<'_>) -> SearchState {
let ent_id = match &decl.ast {
DeclarationItem::Entity(ent_decl) => {

View file

@ -74,7 +74,7 @@ impl<'a> MapAspectSearcher<'a> {
}
}
impl<'a> Searcher for MapAspectSearcher<'a> {
impl Searcher for MapAspectSearcher<'_> {
/// Visit an instantiation statement extracting completions for ports or generics.
fn search_decl(&mut self, ctx: &dyn TokenAccess, decl: FoundDeclaration<'_>) -> SearchState {
match &decl.ast {

View file

@ -107,7 +107,7 @@ pub trait DiagnosticHandler {
fn push(&mut self, diagnostic: Diagnostic);
}
impl<'a> dyn DiagnosticHandler + 'a {
impl dyn DiagnosticHandler + '_ {
pub fn add(&mut self, item: impl AsRef<SrcPos>, msg: impl Into<String>, code: ErrorCode) {
self.push(Diagnostic::new(item, msg, code))
}

View file

@ -43,7 +43,7 @@ impl<'a> DeadCodeSearcher<'a> {
}
}
impl<'a> Searcher for DeadCodeSearcher<'a> {
impl Searcher for DeadCodeSearcher<'_> {
fn search_pos_with_ref(
&mut self,
_ctx: &dyn TokenAccess,

View file

@ -204,13 +204,13 @@ impl<'a> AnyEntKind<'a> {
}
}
impl<'a> std::fmt::Debug for AnyEntKind<'a> {
impl std::fmt::Debug for AnyEntKind<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.describe())
}
}
impl<'a> std::fmt::Debug for AnyEnt<'a> {
impl std::fmt::Debug for AnyEnt<'_> {
// We need a custom debug implementation for AnyEnt to avoid stack overflow on circular references
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let AnyEnt {
@ -636,19 +636,19 @@ impl<'a> AnyEnt<'a> {
}
}
impl<'a> std::cmp::PartialEq for AnyEnt<'a> {
impl std::cmp::PartialEq for AnyEnt<'_> {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl<'a> std::hash::Hash for AnyEnt<'a> {
impl std::hash::Hash for AnyEnt<'_> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id().hash(state)
}
}
impl<'a> Eq for AnyEnt<'a> {}
impl Eq for AnyEnt<'_> {}
/// This trait is implemented for Ast-nodes which declare named entities
pub trait HasEntityId {

View file

@ -41,7 +41,7 @@ impl Default for ArenaId {
pub struct LocalId(u32);
/// Arena allocators used to store named entities
///
/// Local arena used for single design unit in a separate thread
struct LocalArena {
pub id: ArenaId,

View file

@ -13,6 +13,7 @@ use crate::ast::WithRef;
use crate::named_entity::visibility::Visibility;
use crate::Diagnostic;
#[derive(Clone)]
pub enum Design<'a> {
Entity(Visibility<'a>, Region<'a>),
/// A VHDL architecture.
@ -37,7 +38,7 @@ pub enum Design<'a> {
Context(Region<'a>),
}
impl<'a> Design<'a> {
impl Design<'_> {
pub fn describe(&self) -> &'static str {
use Design::*;
match self {

View file

@ -7,6 +7,7 @@ use super::*;
use crate::ast::Designator;
use std::fmt::{Debug, Formatter};
#[derive(Clone)]
pub enum Overloaded<'a> {
/// A subprogram declaration (meaning: without implementation).
SubprogramDecl(Signature<'a>),
@ -25,7 +26,7 @@ pub enum Overloaded<'a> {
Alias(OverloadedEnt<'a>),
}
impl<'a> Debug for Overloaded<'a> {
impl Debug for Overloaded<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Overloaded::UninstSubprogramDecl(..) => {

View file

@ -14,6 +14,7 @@ use fnv::FnvHashSet;
use super::{Arena, EntRef, Related};
#[derive(Clone)]
pub enum Type<'a> {
// Some types have an optional list of implicit declarations
// Use Weak reference since implicit declaration typically reference the type itself
@ -45,7 +46,7 @@ pub enum UniversalType {
Integer,
}
impl<'a> Type<'a> {
impl Type<'_> {
pub fn describe(&self) -> &'static str {
match self {
Type::Alias(..) => "alias",

View file

@ -112,7 +112,7 @@ impl<'a> Visibility<'a> {
}
pub fn all_in_region(&self) -> impl Iterator<Item = &VisibleRegion<'a>> {
return self.all_in_regions.iter();
self.all_in_regions.iter()
}
pub fn visible(&self) -> impl Iterator<Item = EntRef<'a>> + '_ {

View file

@ -284,7 +284,7 @@ impl Project {
&'a self,
library_name: &Symbol,
source: &Source,
) -> Vec<(EntHierarchy<'a>, &Vec<Token>)> {
) -> Vec<(EntHierarchy<'a>, &'a Vec<Token>)> {
self.root.document_symbols(library_name, source)
}

View file

@ -192,12 +192,14 @@ pub fn parse_subprogram_specification(
}))
} else {
let end_token = ctx.stream.get_last_token_id();
Ok(SubprogramSpecification::Procedure(ProcedureSpecification {
designator: designator.into(),
header,
parameter_list,
span: TokenSpan::new(start_token, end_token),
}))
Ok(SubprogramSpecification::Procedure(Box::new(
ProcedureSpecification {
designator: designator.into(),
header,
parameter_list,
span: TokenSpan::new(start_token, end_token),
},
)))
}
}
@ -310,16 +312,18 @@ procedure foo;
code.with_stream_no_diagnostics(parse_subprogram_declaration),
SubprogramDeclaration {
span: code.token_span(),
specification: SubprogramSpecification::Procedure(ProcedureSpecification {
designator: code
.s1("foo")
.ident()
.map_into(SubprogramDesignator::Identifier)
.into(),
header: None,
parameter_list: None,
span: code.s1("procedure foo").token_span(),
})
specification: SubprogramSpecification::Procedure(Box::new(
ProcedureSpecification {
designator: code
.s1("foo")
.ident()
.map_into(SubprogramDesignator::Identifier)
.into(),
header: None,
parameter_list: None,
span: code.s1("procedure foo").token_span(),
}
))
}
);
}
@ -442,20 +446,22 @@ procedure foo(foo : natural);
code.with_stream_no_diagnostics(parse_subprogram_declaration),
SubprogramDeclaration {
span: code.token_span(),
specification: SubprogramSpecification::Procedure(ProcedureSpecification {
designator: code
.s1("foo")
.ident()
.map_into(SubprogramDesignator::Identifier)
.into(),
header: None,
parameter_list: Some(InterfaceList {
interface_type: InterfaceType::Parameter,
items: vec![code.s1("foo : natural").parameter()],
span: code.between("(", ")").token_span()
}),
span: code.between("procedure", "natural)").token_span(),
})
specification: SubprogramSpecification::Procedure(Box::new(
ProcedureSpecification {
designator: code
.s1("foo")
.ident()
.map_into(SubprogramDesignator::Identifier)
.into(),
header: None,
parameter_list: Some(InterfaceList {
interface_type: InterfaceType::Parameter,
items: vec![code.s1("foo : natural").parameter()],
span: code.between("(", ")").token_span()
}),
span: code.between("procedure", "natural)").token_span(),
}
))
}
);
}
@ -814,18 +820,20 @@ procedure my_proc
decl,
SubprogramDeclaration {
span: code.token_span(),
specification: SubprogramSpecification::Procedure(ProcedureSpecification {
designator: code
.s1("my_proc")
.ident()
.map_into(SubprogramDesignator::Identifier)
.into(),
header: code
.s1("generic (x: natural := 4; y: real := 4)")
.subprogram_header(),
parameter_list: None,
span: code.between("procedure", "4)").token_span(),
})
specification: SubprogramSpecification::Procedure(Box::new(
ProcedureSpecification {
designator: code
.s1("my_proc")
.ident()
.map_into(SubprogramDesignator::Identifier)
.into(),
header: code
.s1("generic (x: natural := 4; y: real := 4)")
.subprogram_header(),
parameter_list: None,
span: code.between("procedure", "4)").token_span(),
}
))
}
);
}
@ -844,19 +852,21 @@ procedure my_proc
decl,
SubprogramDeclaration {
span: code.token_span(),
specification: SubprogramSpecification::Procedure(ProcedureSpecification {
designator: code
.s1("my_proc")
.ident()
.map_into(SubprogramDesignator::Identifier)
.into(),
header: code
.s1("generic (x: natural := 4; y: real := 4)
specification: SubprogramSpecification::Procedure(Box::new(
ProcedureSpecification {
designator: code
.s1("my_proc")
.ident()
.map_into(SubprogramDesignator::Identifier)
.into(),
header: code
.s1("generic (x: natural := 4; y: real := 4)
generic map (x => 42)")
.subprogram_header(),
parameter_list: None,
span: code.between("procedure", "42)").token_span(),
})
.subprogram_header(),
parameter_list: None,
span: code.between("procedure", "42)").token_span(),
}
))
}
);
}

View file

@ -446,7 +446,7 @@ impl TokenId {
TokenId(idx)
}
pub fn pos<'a>(&'a self, ctx: &'a dyn TokenAccess) -> &SrcPos {
pub fn pos<'a>(&'a self, ctx: &'a dyn TokenAccess) -> &'a SrcPos {
ctx.get_pos(*self)
}
}

View file

@ -291,7 +291,7 @@ impl<'a> TokenStream<'a> {
}
}
impl<'a> TokenAccess for TokenStream<'a> {
impl TokenAccess for TokenStream<'_> {
fn get_token(&self, id: TokenId) -> Option<&Token> {
self.tokens[self.token_offset.get()..].get_token(id)
}