Skip to main content

rune/compile/
meta.rs

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