use cairo_lang_debug::DebugWithDb;
use cairo_lang_defs::ids::{
NamedLanguageElementId, TopLevelLanguageElementId, TraitFunctionId, UnstableSalsaId,
};
use cairo_lang_diagnostics::{DiagnosticAdded, DiagnosticNote, Maybe};
use cairo_lang_proc_macros::{DebugWithDb, HeapSize, SemanticObject};
use cairo_lang_semantic::corelib::CorelibSemantic;
use cairo_lang_semantic::items::functions::{FunctionsSemantic, ImplGenericFunctionId};
use cairo_lang_semantic::items::imp::ImplLongId;
use cairo_lang_semantic::items::structure::StructSemantic;
use cairo_lang_semantic::{ConcreteTypeId, GenericArgumentId, TypeId, TypeLongId};
use cairo_lang_syntax::node::ast::ExprPtr;
use cairo_lang_syntax::node::kind::SyntaxKind;
use cairo_lang_syntax::node::{TypedStablePtr, ast};
use cairo_lang_utils::{Intern, define_short_id, extract_matches, try_extract_matches};
use defs::diagnostic_utils::StableLocation;
use defs::ids::{ExternFunctionId, FreeFunctionId};
use itertools::zip_eq;
use salsa::Database;
use semantic::items::functions::GenericFunctionId;
use semantic::substitution::{GenericSubstitution, SubstitutionRewriter};
use semantic::{ExprVar, Mutability};
use {cairo_lang_defs as defs, cairo_lang_semantic as semantic};
use crate::Location;
use crate::db::LoweringGroup;
use crate::ids::semantic::substitution::SemanticRewriter;
use crate::specialization::SpecializationArg;
#[derive(Clone, Debug, Hash, PartialEq, Eq, salsa::Update, HeapSize)]
pub enum FunctionWithBodyLongId<'db> {
Semantic(defs::ids::FunctionWithBodyId<'db>),
Generated { parent: defs::ids::FunctionWithBodyId<'db>, key: GeneratedFunctionKey<'db> },
}
define_short_id!(FunctionWithBodyId, FunctionWithBodyLongId<'db>);
impl<'db> FunctionWithBodyLongId<'db> {
pub fn base_semantic_function(
&self,
_db: &'db dyn Database,
) -> cairo_lang_defs::ids::FunctionWithBodyId<'db> {
match self {
FunctionWithBodyLongId::Semantic(id) => *id,
FunctionWithBodyLongId::Generated { parent, .. } => *parent,
}
}
pub fn to_concrete(&self, db: &'db dyn Database) -> Maybe<ConcreteFunctionWithBodyLongId<'db>> {
Ok(match self {
FunctionWithBodyLongId::Semantic(semantic) => ConcreteFunctionWithBodyLongId::Semantic(
semantic::ConcreteFunctionWithBodyId::from_generic(db, *semantic)?,
),
FunctionWithBodyLongId::Generated { parent, key } => {
ConcreteFunctionWithBodyLongId::Generated(GeneratedFunction {
parent: semantic::ConcreteFunctionWithBodyId::from_generic(db, *parent)?,
key: *key,
})
}
})
}
}
impl<'db> FunctionWithBodyId<'db> {
pub fn base_semantic_function(
&self,
db: &'db dyn Database,
) -> cairo_lang_defs::ids::FunctionWithBodyId<'db> {
self.long(db).base_semantic_function(db)
}
pub fn signature(&self, db: &'db dyn Database) -> Maybe<Signature<'db>> {
Ok(db.function_with_body_lowering(*self)?.signature.clone())
}
pub fn to_concrete(&self, db: &'db dyn Database) -> Maybe<ConcreteFunctionWithBodyId<'db>> {
Ok(self.long(db).to_concrete(db)?.intern(db))
}
}
pub trait SemanticFunctionWithBodyIdEx<'db> {
fn lowered(&self, db: &'db dyn Database) -> FunctionWithBodyId<'db>;
}
impl<'db> SemanticFunctionWithBodyIdEx<'db> for cairo_lang_defs::ids::FunctionWithBodyId<'db> {
fn lowered(&self, db: &'db dyn Database) -> FunctionWithBodyId<'db> {
FunctionWithBodyLongId::Semantic(*self).intern(db)
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, salsa::Update, HeapSize)]
pub enum ConcreteFunctionWithBodyLongId<'db> {
Semantic(semantic::ConcreteFunctionWithBodyId<'db>),
Generated(GeneratedFunction<'db>),
Specialized(SpecializedFunctionId<'db>),
}
define_short_id!(ConcreteFunctionWithBodyId, ConcreteFunctionWithBodyLongId<'db>);
pub enum GenericOrSpecialized<'db> {
Generic(FunctionWithBodyId<'db>),
Specialized(SpecializedFunctionId<'db>),
}
impl<'db> ConcreteFunctionWithBodyId<'db> {
pub fn is_panic_destruct_fn(&self, db: &'db dyn Database) -> Maybe<bool> {
match self.long(db) {
ConcreteFunctionWithBodyLongId::Semantic(semantic_func) => {
semantic_func.is_panic_destruct_fn(db)
}
ConcreteFunctionWithBodyLongId::Generated(GeneratedFunction {
parent: _,
key: GeneratedFunctionKey::TraitFunc(function, _),
}) => Ok(function == &db.core_info().panic_destruct_fn),
_ => Ok(false),
}
}
pub fn generic_or_specialized(&self, db: &'db dyn Database) -> GenericOrSpecialized<'db> {
self.long(db).clone().generic_or_specialized(db)
}
}
impl<'db> UnstableSalsaId for ConcreteFunctionWithBodyId<'db> {
fn get_internal_id(&self) -> salsa::Id {
self.as_intern_id()
}
}
impl<'db> ConcreteFunctionWithBodyLongId<'db> {
pub fn generic_or_specialized(self, db: &'db dyn Database) -> GenericOrSpecialized<'db> {
let long_id = match self {
ConcreteFunctionWithBodyLongId::Semantic(id) => {
FunctionWithBodyLongId::Semantic(id.function_with_body_id(db))
}
ConcreteFunctionWithBodyLongId::Generated(GeneratedFunction { parent, key }) => {
FunctionWithBodyLongId::Generated { parent: parent.function_with_body_id(db), key }
}
ConcreteFunctionWithBodyLongId::Specialized(specialized) => {
return GenericOrSpecialized::Specialized(specialized);
}
};
GenericOrSpecialized::Generic(long_id.intern(db))
}
pub fn substitution(&self, db: &'db dyn Database) -> Maybe<GenericSubstitution<'db>> {
match self {
ConcreteFunctionWithBodyLongId::Semantic(id) => id.substitution(db),
ConcreteFunctionWithBodyLongId::Generated(GeneratedFunction { parent, .. }) => {
parent.substitution(db)
}
ConcreteFunctionWithBodyLongId::Specialized(specialized) => {
specialized.long(db).base.substitution(db)
}
}
}
pub fn function_id(&self, db: &'db dyn Database) -> Maybe<FunctionId<'db>> {
let long_id = match self {
ConcreteFunctionWithBodyLongId::Semantic(id) => {
FunctionLongId::Semantic(id.function_id(db)?)
}
ConcreteFunctionWithBodyLongId::Generated(generated) => {
FunctionLongId::Generated(*generated)
}
ConcreteFunctionWithBodyLongId::Specialized(specialized) => {
FunctionLongId::Specialized(*specialized)
}
};
Ok(long_id.intern(db))
}
pub fn base_semantic_function(
&self,
db: &'db dyn Database,
) -> semantic::ConcreteFunctionWithBodyId<'db> {
match self {
ConcreteFunctionWithBodyLongId::Semantic(id) => *id,
ConcreteFunctionWithBodyLongId::Generated(generated) => generated.parent,
ConcreteFunctionWithBodyLongId::Specialized(specialized) => {
specialized.long(db).base.base_semantic_function(db)
}
}
}
pub fn full_path(&self, db: &dyn Database) -> String {
match self {
ConcreteFunctionWithBodyLongId::Semantic(semantic) => semantic.full_path(db),
ConcreteFunctionWithBodyLongId::Generated(generated) => generated.full_path(db),
ConcreteFunctionWithBodyLongId::Specialized(specialized) => {
specialized.long(db).full_path(db)
}
}
}
}
impl<'db> ConcreteFunctionWithBodyId<'db> {
pub fn from_semantic(
db: &'db dyn Database,
semantic: semantic::ConcreteFunctionWithBodyId<'db>,
) -> Self {
ConcreteFunctionWithBodyLongId::Semantic(semantic).intern(db)
}
pub fn substitution(&self, db: &'db dyn Database) -> Maybe<GenericSubstitution<'db>> {
self.long(db).substitution(db)
}
pub fn function_id(&self, db: &'db dyn Database) -> Maybe<FunctionId<'db>> {
self.long(db).function_id(db)
}
pub fn full_path(&self, db: &dyn Database) -> String {
self.long(db).full_path(db)
}
pub fn signature(&self, db: &'db dyn Database) -> Maybe<Signature<'db>> {
match self.generic_or_specialized(db) {
GenericOrSpecialized::Generic(id) => {
let generic_signature = id.signature(db)?;
self.substitution(db)?.substitute(db, generic_signature)
}
GenericOrSpecialized::Specialized(specialized) => specialized.long(db).signature(db),
}
}
pub fn from_no_generics_free(
db: &'db dyn Database,
free_function_id: FreeFunctionId<'db>,
) -> Option<Self> {
let semantic =
semantic::ConcreteFunctionWithBodyId::from_no_generics_free(db, free_function_id)?;
Some(ConcreteFunctionWithBodyLongId::Semantic(semantic).intern(db))
}
pub fn base_semantic_function(
&self,
db: &'db dyn Database,
) -> semantic::ConcreteFunctionWithBodyId<'db> {
self.long(db).base_semantic_function(db)
}
pub fn stable_location(&self, db: &'db dyn Database) -> Maybe<StableLocation<'db>> {
Ok(match self.long(db) {
ConcreteFunctionWithBodyLongId::Semantic(id) => id.stable_location(db),
ConcreteFunctionWithBodyLongId::Generated(generated) => match generated.key {
GeneratedFunctionKey::Loop(stable_ptr) => StableLocation::new(stable_ptr.untyped()),
GeneratedFunctionKey::TraitFunc(_, stable_location) => stable_location,
},
ConcreteFunctionWithBodyLongId::Specialized(specialized_function) => {
specialized_function.long(db).base.stable_location(db)?
}
})
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, salsa::Update, HeapSize)]
pub enum FunctionLongId<'db> {
Semantic(semantic::FunctionId<'db>),
Generated(GeneratedFunction<'db>),
Specialized(SpecializedFunctionId<'db>),
}
define_short_id!(FunctionId, FunctionLongId<'db>);
impl<'db> FunctionLongId<'db> {
pub fn body(&self, db: &'db dyn Database) -> Maybe<Option<ConcreteFunctionWithBodyId<'db>>> {
Ok(Some(match self {
FunctionLongId::Semantic(id) => {
let concrete_function = id.get_concrete(db);
if let GenericFunctionId::Impl(ImplGenericFunctionId { impl_id, function }) =
concrete_function.generic_function
&& let ImplLongId::GeneratedImpl(imp) = impl_id.long(db)
{
let concrete_trait = imp.concrete_trait(db);
let info = db.core_info();
assert!(
[info.destruct_fn, info.panic_destruct_fn, info.call_fn, info.call_once_fn]
.contains(&function)
);
let generic_args = concrete_trait.generic_args(db);
let Some(GenericArgumentId::Type(ty)) = generic_args.first() else {
unreachable!("Expected Generated Impl to have a type argument");
};
let TypeLongId::Closure(ty) = ty.long(db) else {
unreachable!("Expected Generated Impl to have a closure type argument");
};
let Some(parent) = ty.parent_function?.get_concrete(db).body(db)? else {
return Ok(None);
};
return Ok(Some(
GeneratedFunction {
parent,
key: GeneratedFunctionKey::TraitFunc(function, ty.params_location),
}
.body(db),
));
}
let Some(body) = concrete_function.body(db)? else {
return Ok(None);
};
ConcreteFunctionWithBodyLongId::Semantic(body).intern(db)
}
FunctionLongId::Generated(generated) => generated.body(db),
FunctionLongId::Specialized(specialized) => {
ConcreteFunctionWithBodyLongId::Specialized(*specialized).intern(db)
}
}))
}
pub fn signature(&self, db: &'db dyn Database) -> Maybe<Signature<'db>> {
match self {
FunctionLongId::Semantic(semantic) => Ok(EnrichedSemanticSignature::from_semantic(
db,
db.concrete_function_signature(*semantic)?,
)
.into()),
FunctionLongId::Generated(generated) => generated.body(db).signature(db),
FunctionLongId::Specialized(specialized) => specialized.long(db).signature(db),
}
}
pub fn full_path(&self, db: &dyn Database) -> String {
format!("{:?}", self.debug(db))
}
pub fn semantic_full_path(&self, db: &dyn Database) -> String {
match self {
FunctionLongId::Semantic(id) => id.full_path(db),
FunctionLongId::Generated(generated) => generated.parent.full_path(db),
FunctionLongId::Specialized(specialized) => specialized.long(db).full_path(db),
}
}
}
impl<'db> FunctionId<'db> {
pub fn body(&self, db: &'db dyn Database) -> Maybe<Option<ConcreteFunctionWithBodyId<'db>>> {
self.long(db).body(db)
}
pub fn signature(&self, db: &'db dyn Database) -> Maybe<Signature<'db>> {
self.long(db).signature(db)
}
pub fn full_path(&self, db: &dyn Database) -> String {
self.long(db).full_path(db)
}
pub fn semantic_full_path(&self, db: &dyn Database) -> String {
self.long(db).semantic_full_path(db)
}
pub fn get_extern(
&self,
db: &'db dyn Database,
) -> Option<(ExternFunctionId<'db>, Vec<GenericArgumentId<'db>>)> {
let semantic = try_extract_matches!(self.long(db), FunctionLongId::Semantic)?;
let concrete = semantic.get_concrete(db);
Some((
try_extract_matches!(concrete.generic_function, GenericFunctionId::Extern)?,
concrete.generic_args,
))
}
}
pub trait SemanticFunctionIdEx<'db> {
fn lowered(&self, db: &'db dyn Database) -> FunctionId<'db>;
}
impl<'db> SemanticFunctionIdEx<'db> for semantic::FunctionId<'db> {
fn lowered(&self, db: &'db dyn Database) -> FunctionId<'db> {
let ret = FunctionLongId::Semantic(*self).intern(db);
if let Ok(Some(body)) = ret.body(db)
&& let Ok(id) = body.function_id(db)
{
return id;
}
ret
}
}
impl<'a> DebugWithDb<'a> for FunctionLongId<'a> {
type Db = dyn Database;
fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'a dyn Database) -> std::fmt::Result {
match self {
FunctionLongId::Semantic(semantic) => write!(f, "{:?}", semantic.debug(db)),
FunctionLongId::Generated(generated) => write!(f, "{:?}", generated.debug(db)),
FunctionLongId::Specialized(specialized) => write!(f, "{:?}", specialized.debug(db)),
}
}
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, HeapSize, salsa::Update)]
pub enum GeneratedFunctionKey<'db> {
Loop(ExprPtr<'db>),
TraitFunc(TraitFunctionId<'db>, StableLocation<'db>),
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, salsa::Update, HeapSize)]
pub struct GeneratedFunction<'db> {
pub parent: semantic::ConcreteFunctionWithBodyId<'db>,
pub key: GeneratedFunctionKey<'db>,
}
impl<'db> GeneratedFunction<'db> {
pub fn body(&self, db: &'db dyn Database) -> ConcreteFunctionWithBodyId<'db> {
let long_id = ConcreteFunctionWithBodyLongId::Generated(*self);
long_id.intern(db)
}
pub fn full_path(&self, db: &dyn Database) -> String {
format!("{:?}", self.debug(db))
}
}
impl<'a> DebugWithDb<'a> for GeneratedFunction<'a> {
type Db = dyn Database;
fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'a dyn Database) -> std::fmt::Result {
match self.key {
GeneratedFunctionKey::Loop(expr_ptr) => {
let mut func_ptr = expr_ptr.untyped();
while !matches!(
func_ptr.kind(db),
SyntaxKind::FunctionWithBody | SyntaxKind::TraitItemFunction
) {
func_ptr = func_ptr.parent(db)
}
let span = expr_ptr.0.lookup(db).span(db);
let function_start = func_ptr.lookup(db).span(db).start.as_u32();
write!(
f,
"{:?}[{}-{}]",
self.parent.debug(db),
span.start.as_u32() - function_start,
span.end.as_u32() - function_start
)
}
GeneratedFunctionKey::TraitFunc(trait_func, loc) => {
let trait_id = trait_func.trait_id(db);
write!(
f,
"Generated `{}::{}` for {{closure@{:?}}}",
trait_id.full_path(db),
trait_func.name(db).long(db),
loc.debug(db),
)
}
}
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, salsa::Update, HeapSize)]
pub struct SpecializedFunction<'db> {
pub base: crate::ids::ConcreteFunctionWithBodyId<'db>,
pub args: Vec<SpecializationArg<'db>>,
}
define_short_id!(SpecializedFunctionId, SpecializedFunction<'db>);
impl<'db> SpecializedFunction<'db> {
pub fn full_path(&self, db: &dyn Database) -> String {
format!("{:?}", self.debug(db))
}
pub fn signature(&self, db: &'db dyn Database) -> Maybe<Signature<'db>> {
let mut base_sign = self.base.signature(db)?;
let mut params = vec![];
let mut stack = vec![];
for (param, arg) in zip_eq(base_sign.params.iter().rev(), self.args.iter().rev()) {
stack.push((param.clone(), arg));
}
while let Some((param, arg)) = stack.pop() {
match arg {
SpecializationArg::Const { .. } => {}
SpecializationArg::Snapshot(inner) => {
let desnap_ty = *extract_matches!(param.ty.long(db), TypeLongId::Snapshot);
stack.push((
LoweredParam { ty: desnap_ty, stable_ptr: param.stable_ptr },
inner.as_ref(),
));
}
SpecializationArg::Enum { variant, payload } => {
let lowered_param =
LoweredParam { ty: variant.ty, stable_ptr: param.stable_ptr };
stack.push((lowered_param, payload.as_ref()));
}
SpecializationArg::Array(ty, values) => {
for arg in values.iter().rev() {
let lowered_param = LoweredParam { ty: *ty, stable_ptr: param.stable_ptr };
stack.push((lowered_param, arg));
}
}
SpecializationArg::Struct(specialization_args) => {
let element_types: Vec<TypeId<'db>> = match param.ty.long(db) {
TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct)) => {
let Ok(members) = db.concrete_struct_members(*concrete_struct) else {
continue;
};
members.values().map(|member| member.ty).collect()
}
TypeLongId::Tuple(element_types) => element_types.clone(),
TypeLongId::FixedSizeArray { type_id, .. } => {
vec![*type_id; specialization_args.len()]
}
_ => unreachable!("Expected a struct, tuple, or fixed-size array type"),
};
for (elem_ty, arg) in
zip_eq(element_types.iter().rev(), specialization_args.iter().rev())
{
let lowered_param =
LoweredParam { ty: *elem_ty, stable_ptr: param.stable_ptr };
stack.push((lowered_param, arg));
}
}
SpecializationArg::NotSpecialized => {
params.push(param.clone());
}
}
}
base_sign.params = params;
Ok(base_sign)
}
}
impl<'a> DebugWithDb<'a> for SpecializedFunction<'a> {
type Db = dyn Database;
fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'a dyn Database) -> std::fmt::Result {
write!(f, "{}{{", self.base.full_path(db))?;
for arg in self.args.iter() {
write!(f, "{:?}, ", arg.debug(db))?;
}
write!(f, "}}")
}
}
#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, SemanticObject, Hash, salsa::Update)]
#[debug_db(dyn Database)]
pub struct EnrichedSemanticSignature<'db> {
pub params: Vec<semantic::ExprVarMemberPath<'db>>,
pub extra_rets: Vec<semantic::ExprVarMemberPath<'db>>,
pub return_type: semantic::TypeId<'db>,
pub implicits: Vec<semantic::TypeId<'db>>,
#[dont_rewrite]
pub panicable: bool,
#[dont_rewrite]
#[hide_field_debug_with_db]
pub location: LocationId<'db>,
}
impl<'db> EnrichedSemanticSignature<'db> {
pub fn from_semantic(db: &'db dyn Database, value: &semantic::Signature<'db>) -> Self {
let semantic::Signature {
params,
return_type,
implicits,
panicable,
stable_ptr,
is_const: _,
} = value;
let ref_params = params
.iter()
.filter(|param| param.mutability == Mutability::Reference)
.map(|param| parameter_as_member_path(param.clone()))
.collect();
let params: Vec<semantic::ExprVarMemberPath<'_>> =
params.iter().cloned().map(parameter_as_member_path).collect();
Self {
params,
extra_rets: ref_params,
return_type: *return_type,
implicits: implicits.clone(),
panicable: *panicable,
location: LocationId::from_stable_location(
db,
StableLocation::new(stable_ptr.untyped()),
),
}
}
pub fn is_fully_concrete(&self, db: &dyn Database) -> bool {
self.params.iter().all(|param| param.ty().is_fully_concrete(db))
&& self.extra_rets.iter().all(|param| param.ty().is_fully_concrete(db))
&& self.return_type.is_fully_concrete(db)
&& self.implicits.iter().all(|ty| ty.is_fully_concrete(db))
}
}
semantic::add_rewrite!(<'a, 'b>, SubstitutionRewriter<'a, 'b>, DiagnosticAdded, EnrichedSemanticSignature<'a>);
#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, SemanticObject, Hash, salsa::Update)]
#[debug_db(dyn Database)]
pub struct LoweredParam<'db> {
pub ty: semantic::TypeId<'db>,
#[dont_rewrite]
pub stable_ptr: ast::ExprPtr<'db>,
}
semantic::add_rewrite!(<'a, 'b>, SubstitutionRewriter<'a, 'b>, DiagnosticAdded, LoweredParam<'a>);
#[derive(Clone, Debug, PartialEq, Eq, DebugWithDb, SemanticObject, Hash, salsa::Update)]
#[debug_db(dyn Database)]
pub struct Signature<'db> {
pub params: Vec<LoweredParam<'db>>, pub extra_rets: Vec<semantic::ExprVarMemberPath<'db>>,
pub return_type: semantic::TypeId<'db>,
pub implicits: Vec<semantic::TypeId<'db>>,
#[dont_rewrite]
pub panicable: bool,
#[dont_rewrite]
#[hide_field_debug_with_db]
pub location: LocationId<'db>,
}
semantic::add_rewrite!(<'a, 'b>, SubstitutionRewriter<'a, 'b>, DiagnosticAdded, Signature<'a>);
impl<'db> From<EnrichedSemanticSignature<'db>> for Signature<'db> {
fn from(signature: EnrichedSemanticSignature<'db>) -> Self {
Signature {
params: signature
.params
.iter()
.map(|param| LoweredParam { ty: param.ty(), stable_ptr: param.stable_ptr() })
.collect(),
extra_rets: signature.extra_rets,
return_type: signature.return_type,
implicits: signature.implicits,
panicable: signature.panicable,
location: signature.location,
}
}
}
pub(crate) fn parameter_as_member_path<'db>(
param: semantic::Parameter<'db>,
) -> semantic::ExprVarMemberPath<'db> {
let semantic::Parameter { id, ty, stable_ptr, .. } = param;
semantic::ExprVarMemberPath::Var(ExprVar {
var: semantic::VarId::Param(id),
ty,
stable_ptr: ast::ExprPtr(stable_ptr.0),
})
}
define_short_id!(LocationId, Location<'db>);
impl<'db> LocationId<'db> {
pub fn from_stable_location(
db: &'db dyn Database,
stable_location: StableLocation<'db>,
) -> LocationId<'db> {
Location::new(stable_location).intern(db)
}
pub fn with_note(&self, db: &'db dyn Database, note: DiagnosticNote<'db>) -> LocationId<'db> {
self.long(db).clone().with_note(note).intern(db)
}
pub fn with_auto_generation_note(
&self,
db: &'db dyn Database,
logic_name: &str,
) -> LocationId<'db> {
self.with_note(
db,
DiagnosticNote::text_only(format!(
"this error originates in auto-generated {logic_name} logic."
)),
)
}
}