use std::iter::repeat;
use cairo_lang_debug::DebugWithDb;
use cairo_lang_defs as defs;
use cairo_lang_defs::ids::{NamedLanguageElementId, TopLevelLanguageElementId, TraitFunctionId};
use cairo_lang_diagnostics::{DiagnosticAdded, DiagnosticNote, Maybe, MaybeAsRef};
use cairo_lang_proc_macros::{DebugWithDb, HeapSize, SemanticObject};
use cairo_lang_semantic as semantic;
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, TypeLongId};
use cairo_lang_syntax::node::ast::ExprPtr;
use cairo_lang_syntax::node::helpers::generated_function_path;
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 crate::db::LoweringGroup;
use crate::ids::semantic::substitution::SemanticRewriter;
use crate::specialization::SpecializationArg;
use crate::{Location, LoweringStage};
#[derive(Clone, Debug, Hash, PartialEq, Eq, salsa::SalsaValue, 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<&'db Signature<'db>> {
Ok(&db.function_with_body_lowering(*self)?.signature)
}
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::SalsaValue, 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> 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 {
format!("{:?}", self.debug(db))
}
}
impl<'db> DebugWithDb<'db> for ConcreteFunctionWithBodyLongId<'db> {
type Db = dyn Database;
fn fmt(&self, f: &mut std::fmt::Formatter<'_>, db: &'db dyn Database) -> std::fmt::Result {
match self {
ConcreteFunctionWithBodyLongId::Semantic(semantic) => {
write!(f, "{:?}", semantic.debug(db))
}
ConcreteFunctionWithBodyLongId::Generated(generated) => {
write!(f, "{:?}", generated.debug(db))
}
ConcreteFunctionWithBodyLongId::Specialized(specialized) => {
write!(f, "{:?}", specialized.debug(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,
stage: LoweringStage,
) -> Maybe<&'db Signature<'db>> {
if stage != LoweringStage::Monomorphized {
return Ok(&db.lowered_body(*self, stage)?.signature);
}
monomorphized_signature(db, *self).maybe_as_ref()
}
pub fn generic(&self, db: &'db dyn Database) -> Option<FunctionWithBodyId<'db>> {
match self.generic_or_specialized(db) {
GenericOrSpecialized::Generic(id) => Some(id),
GenericOrSpecialized::Specialized(_) => None,
}
}
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)?
}
})
}
}
#[salsa::tracked(returns(ref))]
fn monomorphized_signature<'db>(
db: &'db dyn Database,
function: ConcreteFunctionWithBodyId<'db>,
) -> Maybe<Signature<'db>> {
match function.generic_or_specialized(db) {
GenericOrSpecialized::Generic(id) => {
let generic_signature = id.signature(db)?.clone();
function.substitution(db)?.substitute(db, generic_signature)
}
GenericOrSpecialized::Specialized(specialized) => {
let function = specialized.long(db);
let mut base_sign = function.base.signature(db, LoweringStage::Monomorphized)?.clone();
let mut params = vec![];
let mut stack: Vec<_> =
zip_eq(base_sign.params.into_iter().rev(), function.args.iter().rev()).collect();
while let Some((param_ty, arg)) = stack.pop() {
match arg {
SpecializationArg::Const { .. } => {}
SpecializationArg::Snapshot(inner) => {
let desnap_ty = *extract_matches!(param_ty.long(db), TypeLongId::Snapshot);
stack.push((desnap_ty, inner.as_ref()));
}
SpecializationArg::Enum { variant, payload } => {
stack.push((variant.ty, payload.as_ref()));
}
SpecializationArg::Array(ty, values) => {
stack.extend(repeat(*ty).zip(values.iter().rev()));
}
SpecializationArg::Struct(args) => match param_ty.long(db) {
TypeLongId::Concrete(ConcreteTypeId::Struct(concrete_struct)) => {
let Ok(members) = db.concrete_struct_members(*concrete_struct) else {
continue;
};
let tys = members.values().map(|m| m.ty);
stack.extend(zip_eq(tys.rev(), args.iter().rev()));
}
TypeLongId::Tuple(tys) => {
stack.extend(zip_eq(tys.iter().rev().copied(), args.iter().rev()));
}
TypeLongId::FixedSizeArray { type_id, .. } => {
stack.extend(repeat(*type_id).zip(args.iter().rev()));
}
_ => unreachable!("Expected a struct, tuple, or fixed-size array type"),
},
SpecializationArg::NotSpecialized => {
params.push(param_ty);
}
}
}
base_sign.params = params;
Ok(base_sign)
}
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, salsa::SalsaValue, 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,
stage: LoweringStage,
) -> Maybe<&'db Signature<'db>> {
if let Some(body) = self.body(db)? {
body.signature(db, stage)
} else if let FunctionLongId::Semantic(semantic) = self {
semantic_function_signature(db, *semantic).maybe_as_ref()
} else {
unreachable!(
"Generated and specialized functions always have a body: {}",
self.full_path(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),
}
}
}
#[salsa::tracked(returns(ref))]
fn semantic_function_signature<'db>(
db: &'db dyn Database,
function: semantic::FunctionId<'db>,
) -> Maybe<Signature<'db>> {
Ok(EnrichedSemanticSignature::from_semantic(db, db.concrete_function_signature(function)?)
.into())
}
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,
stage: LoweringStage,
) -> Maybe<&'db Signature<'db>> {
self.long(db).signature(db, stage)
}
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::SalsaValue)]
pub enum GeneratedFunctionKey<'db> {
Loop(ExprPtr<'db>),
TraitFunc(TraitFunctionId<'db>, StableLocation<'db>),
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, salsa::SalsaValue, 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) => {
write!(
f,
"{:?}::{}",
self.parent.debug(db),
generated_function_path(db, expr_ptr.0.lookup(db))
)
}
GeneratedFunctionKey::TraitFunc(trait_func, loc) => {
let trait_id = trait_func.trait_id(db);
write!(
f,
"Generated `{}::{}` for {:?}::{}",
trait_id.full_path(db),
trait_func.name(db).long(db),
self.parent.debug(db),
generated_function_path(db, loc.syntax_node(db)),
)
}
}
}
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, salsa::SalsaValue, 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))
}
}
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::SalsaValue)]
#[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::SalsaValue)]
#[debug_db(dyn Database)]
pub struct Signature<'db> {
pub params: Vec<semantic::TypeId<'db>>,
pub extra_rets: Vec<semantic::TypeId<'db>>,
pub return_type: semantic::TypeId<'db>,
pub declared_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| param.ty()).collect(),
extra_rets: signature.extra_rets.iter().map(|param| param.ty()).collect(),
return_type: signature.return_type,
declared_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."
)),
)
}
}