rune/compile/
meta.rs

1//! Compiler metadata for Rune.
2
3use core::fmt;
4
5use crate as rune;
6use crate::alloc::borrow::Cow;
7use crate::alloc::path::Path;
8use crate::alloc::prelude::*;
9use crate::alloc::{self, Box, Vec};
10use crate::ast;
11use crate::ast::{Span, Spanned};
12use crate::compile::attrs::Parser;
13#[cfg(feature = "doc")]
14use crate::compile::meta;
15use crate::compile::{self, ItemId, Location, MetaInfo, ModId, Pool, Visibility};
16use crate::module::{DocFunction, ModuleItemCommon};
17use crate::parse::ResolveContext;
18use crate::runtime::{Call, FieldMap, Protocol};
19use crate::{Hash, Item, ItemBuf};
20
21/// A meta reference to an item being compiled.
22#[derive(Debug, TryClone, Clone, Copy)]
23#[try_clone(copy)]
24#[non_exhaustive]
25pub struct MetaRef<'a> {
26    /// If the meta comes from the context or not.
27    pub context: bool,
28    /// The hash of a meta item.
29    pub hash: Hash,
30    /// The item being described.
31    pub item: &'a Item,
32    /// The kind of the item.
33    pub kind: &'a Kind,
34    /// The source of the meta.
35    pub source: Option<&'a SourceMeta>,
36}
37
38/// Information on a compile sourc.
39#[derive(Debug, TryClone)]
40#[non_exhaustive]
41pub struct SourceMeta {
42    /// The location of the compile source.
43    pub location: Location,
44    /// The optional path where the meta is declared.
45    pub path: Option<Box<Path>>,
46}
47
48/// Doc content for a compiled item.
49#[derive(Debug, TryClone, Clone, Copy, Spanned)]
50#[try_clone(copy)]
51pub(crate) struct Doc {
52    #[rune(span)]
53    pub(crate) span: Span,
54    /// The string content of the doc comment.
55    pub(crate) doc_string: ast::LitStr,
56}
57
58impl Doc {
59    pub(crate) fn collect_from(
60        cx: ResolveContext<'_>,
61        attrs: &mut Parser,
62        attributes: &[ast::Attribute],
63    ) -> compile::Result<Vec<Doc>> {
64        let docs = attrs
65            .parse_all::<crate::compile::attrs::Doc>(cx, attributes)?
66            .map(|result| {
67                result.map(|(span, doc)| Doc {
68                    span: span.span(),
69                    doc_string: doc.doc_string,
70                })
71            })
72            .try_collect::<compile::Result<_>>()??;
73
74        Ok(docs)
75    }
76}
77
78/// Metadata about a compiled unit.
79#[derive(Debug, TryClone)]
80#[non_exhaustive]
81pub(crate) struct Meta {
82    /// If the meta comes from the context or not.
83    pub(crate) context: bool,
84    /// Hash of the private metadata.
85    pub(crate) hash: Hash,
86    /// The item of the returned compile meta.
87    pub(crate) item_meta: ItemMeta,
88    /// The kind of the compile meta.
89    pub(crate) kind: Kind,
90    /// The source of the meta.
91    pub(crate) source: Option<SourceMeta>,
92    /// Hash parameters for meta.
93    pub(crate) parameters: Hash,
94}
95
96impl Meta {
97    /// Get the [Meta] which describes metadata.
98    pub(crate) fn info(&self, pool: &Pool) -> alloc::Result<MetaInfo> {
99        MetaInfo::new(&self.kind, self.hash, Some(pool.item(self.item_meta.item)))
100    }
101
102    /// Get the [MetaRef] which describes this [meta::Meta] object.
103    pub(crate) fn as_meta_ref<'a>(&'a self, pool: &'a Pool) -> MetaRef<'a> {
104        MetaRef {
105            context: self.context,
106            hash: self.hash,
107            item: pool.item(self.item_meta.item),
108            kind: &self.kind,
109            source: self.source.as_ref(),
110        }
111    }
112
113    /// Get the type hash of the base type (the one to type check for) for the
114    /// given compile meta.
115    ///
116    /// Note: Variants cannot be used for type checking, you should instead
117    /// compare them against the enum type.
118    pub(crate) fn type_hash_of(&self) -> Option<Hash> {
119        match &self.kind {
120            Kind::Type { .. } => Some(self.hash),
121            Kind::Struct {
122                enum_hash: Hash::EMPTY,
123                ..
124            } => Some(self.hash),
125            Kind::Struct { .. } => None,
126            Kind::Enum { .. } => Some(self.hash),
127            Kind::Function { .. } => Some(self.hash),
128            Kind::Closure { .. } => Some(self.hash),
129            Kind::AsyncBlock { .. } => Some(self.hash),
130            Kind::Const => None,
131            Kind::ConstFn => None,
132            Kind::Macro => None,
133            Kind::AttributeMacro => None,
134            Kind::Import { .. } => None,
135            Kind::Alias { .. } => None,
136            Kind::Module => None,
137            Kind::Trait => None,
138        }
139    }
140}
141
142/// The kind of a variant.
143#[derive(Debug, TryClone)]
144pub enum Fields {
145    /// Named fields.
146    Named(FieldsNamed),
147    /// Unnamed fields.
148    Unnamed(usize),
149    /// Empty.
150    Empty,
151}
152
153impl Fields {
154    /// Coerce into a tuple field count.
155    pub(crate) fn as_tuple(&self) -> Option<usize> {
156        match *self {
157            Fields::Unnamed(count) => Some(count),
158            Fields::Empty => Some(0),
159            _ => None,
160        }
161    }
162}
163
164/// Compile-time metadata kind about a unit.
165#[derive(Debug, TryClone)]
166#[non_exhaustive]
167pub enum Kind {
168    /// The type is completely opaque. We have no idea about what it is with the
169    /// exception of it having a type hash.
170    Type {
171        /// Hash of generic parameters.
172        parameters: Hash,
173    },
174    /// Metadata about a struct.
175    Struct {
176        /// Fields information.
177        fields: Fields,
178        /// Native constructor for this struct.
179        constructor: Option<Signature>,
180        /// Hash of generic parameters.
181        parameters: Hash,
182        /// If this is a variant, this is the type hash of the enum.
183        ///
184        /// If this is not a variant, this is [Hash::EMPTY].
185        enum_hash: Hash,
186    },
187    /// An enum item.
188    Enum {
189        /// Hash of generic parameters.
190        parameters: Hash,
191    },
192    /// A macro item.
193    Macro,
194    /// An attribute macro item.
195    AttributeMacro,
196    /// A function declaration.
197    Function {
198        /// The associated kind of the function, if it is an associated
199        /// function.
200        associated: Option<AssociatedKind>,
201        /// The hash of the trait this function is associated with.
202        trait_hash: Option<Hash>,
203        /// Native signature for this function.
204        signature: Signature,
205        /// Whether this function has a `#[test]` annotation
206        is_test: bool,
207        /// Whether this function has a `#[bench]` annotation.
208        is_bench: bool,
209        /// Hash of generic parameters.
210        parameters: Hash,
211        /// The container of the associated function.
212        #[cfg(feature = "doc")]
213        container: Option<Hash>,
214        /// Parameter types.
215        #[cfg(feature = "doc")]
216        parameter_types: Vec<Hash>,
217    },
218    /// A closure.
219    Closure {
220        /// Runtime calling convention.
221        call: Call,
222        /// If the closure moves its environment.
223        do_move: bool,
224    },
225    /// An async block.
226    AsyncBlock {
227        /// Runtime calling convention.
228        call: Call,
229        /// If the async block moves its environment.
230        do_move: bool,
231    },
232    /// The constant expression.
233    Const,
234    /// A constant function.
235    ConstFn,
236    /// Purely an import.
237    Import(Import),
238    /// A re-export.
239    Alias(Alias),
240    /// A module.
241    Module,
242    /// A trait.
243    Trait,
244}
245
246impl Kind {
247    /// Access the underlying signature of the kind, if available.
248    #[cfg(all(feature = "doc", any(feature = "languageserver", feature = "cli")))]
249    pub(crate) fn as_signature(&self) -> Option<&Signature> {
250        match self {
251            Kind::Struct { constructor, .. } => constructor.as_ref(),
252            Kind::Function { signature, .. } => Some(signature),
253            _ => None,
254        }
255    }
256
257    /// Access underlying generic parameters.
258    pub(crate) fn as_parameters(&self) -> Hash {
259        match self {
260            Kind::Function { parameters, .. } => *parameters,
261            Kind::Type { parameters, .. } => *parameters,
262            Kind::Enum { parameters, .. } => *parameters,
263            Kind::Struct { parameters, .. } => *parameters,
264            _ => Hash::EMPTY,
265        }
266    }
267
268    /// Get the associated container of the meta kind.
269    #[cfg(feature = "doc")]
270    pub(crate) fn associated_container(&self) -> Option<Hash> {
271        match *self {
272            Kind::Struct { enum_hash, .. } if enum_hash != Hash::EMPTY => Some(enum_hash),
273            Kind::Function { container, .. } => container,
274            _ => None,
275        }
276    }
277}
278
279/// An imported entry.
280#[derive(Debug, TryClone, Clone, Copy)]
281#[try_clone(copy)]
282#[non_exhaustive]
283pub struct Import {
284    /// The location of the import.
285    pub(crate) location: Location,
286    /// The item being imported.
287    pub(crate) target: ItemId,
288    /// The module in which the imports are located.
289    pub(crate) module: ModId,
290}
291
292/// A context alias.
293#[derive(Debug, TryClone)]
294pub struct Alias {
295    /// The item being aliased.
296    pub(crate) to: ItemBuf,
297}
298
299/// Metadata about named fields.
300#[derive(Debug, TryClone)]
301#[non_exhaustive]
302pub struct FieldsNamed {
303    /// Fields associated with the type.
304    pub(crate) fields: Box<[FieldMeta]>,
305}
306
307impl FieldsNamed {
308    /// Coerce into a hashmap of fields.
309    pub(crate) fn to_fields(&self) -> alloc::Result<FieldMap<Box<str>, usize>> {
310        let mut fields = crate::runtime::new_field_hash_map_with_capacity(self.fields.len())?;
311
312        for f in self.fields.iter() {
313            fields.try_insert(f.name.try_clone()?, f.position)?;
314        }
315
316        Ok(fields)
317    }
318}
319
320/// Metadata for a single named field.
321#[derive(Debug, TryClone)]
322pub struct FieldMeta {
323    /// Position of the field in its containing type declaration.
324    pub(crate) name: Box<str>,
325    /// The position of the field.
326    pub(crate) position: usize,
327}
328
329/// Item and the module that the item belongs to.
330#[derive(Debug, TryClone, Clone, Copy)]
331#[try_clone(copy)]
332#[non_exhaustive]
333pub(crate) struct ItemMeta {
334    /// The location of the item.
335    pub(crate) location: Location,
336    /// The name of the item.
337    pub(crate) item: ItemId,
338    /// The visibility of the item.
339    pub(crate) visibility: Visibility,
340    /// The module associated with the item.
341    pub(crate) module: ModId,
342    /// The impl item associated with the item.
343    pub(crate) impl_item: Option<ItemId>,
344}
345
346impl ItemMeta {
347    /// Test if the item is public (and should be exported).
348    pub(crate) fn is_public(&self, pool: &Pool) -> bool {
349        self.visibility.is_public() && pool.module(self.module).is_public(pool)
350    }
351}
352
353/// A description of a function signature.
354#[derive(Debug, TryClone)]
355pub struct Signature {
356    /// An asynchronous function.
357    #[cfg(feature = "doc")]
358    pub(crate) is_async: bool,
359    /// Arguments to the function.
360    #[cfg(feature = "doc")]
361    pub(crate) arguments: Option<Box<[DocArgument]>>,
362    /// Return type of the function.
363    #[cfg(feature = "doc")]
364    pub(crate) return_type: DocType,
365}
366
367impl Signature {
368    /// Construct a signature from context metadata.
369    #[cfg_attr(not(feature = "doc"), allow(unused_variables))]
370    pub(crate) fn from_context(
371        doc: &DocFunction,
372        common: &ModuleItemCommon,
373    ) -> alloc::Result<Self> {
374        Ok(Self {
375            #[cfg(feature = "doc")]
376            is_async: doc.is_async,
377            #[cfg(feature = "doc")]
378            arguments: context_to_arguments(
379                doc.args,
380                doc.argument_types.as_ref(),
381                common.docs.args(),
382            )?,
383            #[cfg(feature = "doc")]
384            return_type: doc.return_type.try_clone()?,
385        })
386    }
387}
388
389#[cfg(feature = "doc")]
390fn context_to_arguments(
391    args: Option<usize>,
392    types: &[meta::DocType],
393    names: &[String],
394) -> alloc::Result<Option<Box<[meta::DocArgument]>>> {
395    use core::iter;
396
397    let Some(args) = args else {
398        return Ok(None);
399    };
400
401    let len = args.max(types.len()).max(names.len()).max(names.len());
402    let mut out = Vec::try_with_capacity(len)?;
403
404    let mut types = types.iter();
405
406    let names = names
407        .iter()
408        .map(|name| Some(name.as_str()))
409        .chain(iter::repeat(None));
410
411    for (n, name) in (0..len).zip(names) {
412        let empty;
413
414        let ty = match types.next() {
415            Some(ty) => ty,
416            None => {
417                empty = meta::DocType::empty();
418                &empty
419            }
420        };
421
422        out.try_push(meta::DocArgument {
423            name: match name {
424                Some(name) => meta::DocName::Name(Box::try_from(name)?),
425                None => meta::DocName::Index(n),
426            },
427            base: ty.base,
428            generics: ty.generics.try_clone()?,
429        })?;
430    }
431
432    Ok(Some(Box::try_from(out)?))
433}
434
435/// A name inside of a document.
436#[derive(Debug, TryClone)]
437#[cfg(feature = "doc")]
438pub(crate) enum DocName {
439    /// A string name.
440    Name(Box<str>),
441    /// A numbered name.
442    Index(#[try_clone(copy)] usize),
443}
444
445#[cfg(feature = "cli")]
446impl DocName {
447    pub(crate) fn is_self(&self) -> bool {
448        match self {
449            DocName::Name(name) => name.as_ref() == "self",
450            DocName::Index(..) => false,
451        }
452    }
453}
454
455#[cfg(feature = "doc")]
456impl fmt::Display for DocName {
457    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
458        match self {
459            DocName::Name(name) => write!(f, "{name}"),
460            DocName::Index(index) if *index == 0 => write!(f, "value"),
461            DocName::Index(index) => write!(f, "value{index}"),
462        }
463    }
464}
465
466/// A description of a type.
467#[derive(Debug, TryClone)]
468#[cfg(feature = "doc")]
469pub(crate) struct DocArgument {
470    /// The name of an argument.
471    pub(crate) name: DocName,
472    /// The base type.
473    pub(crate) base: Hash,
474    /// Generic parameters.
475    pub(crate) generics: Box<[DocType]>,
476}
477
478/// A description of a type.
479#[derive(Default, Debug, TryClone)]
480pub struct DocType {
481    /// The base type.
482    #[cfg(feature = "doc")]
483    pub(crate) base: Hash,
484    /// Generic parameters.
485    #[cfg(feature = "doc")]
486    pub(crate) generics: Box<[DocType]>,
487}
488
489impl DocType {
490    /// Construct an empty type documentation.
491    pub(crate) fn empty() -> Self {
492        Self::new(Hash::EMPTY)
493    }
494
495    /// Construct type documentation.
496    #[cfg_attr(not(feature = "doc"), allow(unused_variables))]
497    pub fn with_generics<const N: usize>(
498        base: Hash,
499        generics: [DocType; N],
500    ) -> alloc::Result<Self> {
501        Ok(Self {
502            #[cfg(feature = "doc")]
503            base,
504            #[cfg(feature = "doc")]
505            generics: Box::try_from(generics)?,
506        })
507    }
508
509    /// Construct type with the specified base type.
510    #[cfg_attr(not(feature = "doc"), allow(unused_variables))]
511    pub(crate) fn new(base: Hash) -> Self {
512        Self {
513            #[cfg(feature = "doc")]
514            base,
515            #[cfg(feature = "doc")]
516            generics: Box::default(),
517        }
518    }
519}
520
521/// The kind of an associated function.
522#[derive(Debug, TryClone, PartialEq, Eq, Hash)]
523#[non_exhaustive]
524pub enum AssociatedKind {
525    /// A protocol function implemented on the type itself.
526    Protocol(&'static Protocol),
527    /// A field function with the given protocol.
528    FieldFn(&'static Protocol, Cow<'static, str>),
529    /// An index function with the given protocol.
530    IndexFn(&'static Protocol, usize),
531    /// The instance function refers to the given named instance fn.
532    Instance(Cow<'static, str>),
533}
534
535impl AssociatedKind {
536    /// Convert the kind into a hash function.
537    pub(crate) fn hash(&self, instance_type: Hash) -> Hash {
538        match self {
539            Self::Protocol(protocol) => Hash::associated_function(instance_type, protocol.hash),
540            Self::IndexFn(protocol, index) => {
541                Hash::index_function(protocol.hash, instance_type, Hash::index(*index))
542            }
543            Self::FieldFn(protocol, field) => {
544                Hash::field_function(protocol.hash, instance_type, field.as_ref())
545            }
546            Self::Instance(name) => Hash::associated_function(instance_type, name.as_ref()),
547        }
548    }
549}
550
551impl fmt::Display for AssociatedKind {
552    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
553        match self {
554            AssociatedKind::Protocol(protocol) => write!(f, "<{}>", protocol.name),
555            AssociatedKind::FieldFn(protocol, field) => {
556                write!(f, ".{field}<{}>", protocol.name)
557            }
558            AssociatedKind::IndexFn(protocol, index) => {
559                write!(f, ".{index}<{}>", protocol.name)
560            }
561            AssociatedKind::Instance(name) => write!(f, "{}", name),
562        }
563    }
564}