mirror of
https://github.com/VHDL-LS/rust_hdl.git
synced 2025-08-04 02:48:28 +00:00
Fix: discrete ranges can be used as choice statements (#354)
This commit is contained in:
parent
5cf0e4c622
commit
7ed5e78ea8
40 changed files with 377 additions and 225 deletions
|
@ -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)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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)?;
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)?;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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>,
|
||||
|
|
83
vhdl_lang/src/analysis/tests/discrete_ranges.rs
Normal file
83
vhdl_lang/src/analysis/tests/discrete_ranges.rs
Normal 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,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
|
@ -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(_)) {
|
||||
|
|
|
@ -11,6 +11,7 @@ mod context_clause;
|
|||
mod custom_attributes;
|
||||
mod declarations;
|
||||
mod deferred_constant;
|
||||
mod discrete_ranges;
|
||||
mod hierarchy;
|
||||
mod homographs;
|
||||
mod implicit;
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(..) => {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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>> + '_ {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
))
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue