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