use core::mem::replace;
use crate::alloc;
use crate::alloc::prelude::*;
use crate::ast::{self, Kind, Span, Spanned};
use crate::compile::{
meta, Doc, DynLocation, Error, ErrorKind, Location, Result, Visibility, WithSpan,
};
use crate::grammar::{Ignore, MaybeNode, Node, NodeId, Remaining, Stream, StreamBuf};
use crate::indexing;
use crate::parse::Resolve;
use crate::query::{Attrs, BuiltInLiteral, DeferEntry, ExpandMacroBuiltin, ImplItem, ImplItemKind};
use crate::runtime::Call;
use crate::worker::{self, Import, ImportKind, ImportState};
use super::items::Guard;
use super::{validate_call, IndexItem, Indexed, Indexer};
use Kind::*;
#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub(crate) enum ExprBlockKind {
Default,
Async,
Const,
}
#[derive(Default, Debug)]
enum ExprSupport {
Yes,
#[default]
No,
}
struct Function<'a> {
node: Node<'a>,
name: Option<ast::Ident>,
attrs: Attrs,
mods: Mods,
}
fn is_instance(node: &Node<'_>) -> alloc::Result<(bool, Vec<Span>)> {
fn is_self(node: Node<'_>) -> bool {
let Some([node]) = node.nodes::<1>() else {
return false;
};
matches!(
(node.kind(), node.kinds()),
(IndexedPath(..), Some([K![self]]))
)
}
let mut is_instance = false;
let mut args = Vec::new();
let Some(node) = node.find(FnArgs) else {
return Ok((is_instance, args));
};
for node in node.children() {
if matches!(node.kind(), Pat) {
args.try_push(node.span())?;
is_instance |= is_self(node);
}
}
Ok((is_instance, args))
}
#[derive(Debug)]
enum State<'a> {
Stream(StreamBuf<'a>),
Expr(Mods, Attrs),
Block(Guard, IndexItem),
ConstBlock(Guard, IndexItem, Node<'a>),
AsyncBlock(Guard, IndexItem, Span, Option<ast::Move>),
Bare(IndexItem, NodeId),
Function(Guard, IndexItem, usize, Option<Span>, ExprSupport),
Module(Guard, IndexItem),
Closure(Guard, IndexItem, Option<ast::Async>, Option<ast::Move>),
Const(Guard, IndexItem, Option<Span>, Node<'a>, ExprSupport),
}
struct Processor<'a> {
span: Span,
fns: Vec<Function<'a>>,
stack: Vec<State<'a>>,
expr: ExprSupport,
}
impl<'a> Processor<'a> {
fn new(span: Span) -> Self {
Self {
span,
fns: Vec::new(),
stack: Vec::new(),
expr: ExprSupport::No,
}
}
fn with_expr(mut self, expr: ExprSupport) -> Self {
self.expr = expr;
self
}
fn process(mut self, idx: &mut Indexer<'_, '_>) -> Result<()> {
loop {
if let Some(State::Stream(p)) = self.stack.last_mut() {
let Some(node) = p.next() else {
self.stack.pop();
continue;
};
tracing::trace!(?node, stack = self.stack.len());
self.node(idx, node)?;
continue;
};
let Some(state) = self.stack.pop() else {
break;
};
tracing::trace!(?state, stack = self.stack.len());
match state {
State::Expr(mods, attrs) => {
mods.deny_all(idx)?;
attrs.deny_any(idx)?;
}
State::Block(guard, item) => {
idx.item = item;
idx.items.pop(guard).with_span(self.span)?;
}
State::AsyncBlock(guard, item, span, move_token) => {
let item_meta = idx
.q
.item_for("async block", idx.item.id)
.with_span(self.span)?;
let layer = idx.scopes.pop().with_span(self.span)?;
let call = validate_call(false, true, &layer)?;
if let Some(call) = call {
idx.q.index_meta(
&span,
item_meta,
meta::Kind::AsyncBlock {
call,
do_move: move_token.is_some(),
},
)?;
} else {
idx.error(Error::new(span, ErrorKind::ClosureKind))?;
}
idx.item = item;
idx.items.pop(guard).with_span(self.span)?;
}
State::ConstBlock(guard, item, node) => {
let item_meta = idx
.q
.item_for("const block", idx.item.id)
.with_span(self.span)?;
idx.q.index_const_block(
item_meta,
indexing::ConstBlock::Node(node.node_at(idx.source_id, idx.tree.clone())),
)?;
idx.item = item;
idx.items.pop(guard).with_span(self.span)?;
}
State::Closure(guard, item, async_token, move_token) => {
let layer = idx.scopes.pop().with_span(self.span)?;
let call = validate_call(false, async_token.is_some(), &layer)?;
let Some(call) = call else {
return Err(Error::new(self.span, ErrorKind::ClosureKind));
};
let item_meta = idx
.q
.item_for("closure", idx.item.id)
.with_span(self.span)?;
idx.q.index_meta(
&self.span,
item_meta,
meta::Kind::Closure {
call,
do_move: move_token.is_some(),
},
)?;
idx.item = item;
idx.items.pop(guard).with_span(self.span)?;
}
State::Bare(item, id) => {
self.bare(idx, id)?;
idx.item = item;
}
State::Function(guard, item, index, nested_item, expr) => {
self.function(idx, index, nested_item)?;
self.expr = expr;
idx.item = item;
idx.items.pop(guard).with_span(self.span)?;
idx.nested_item = nested_item;
}
State::Module(guard, item) => {
idx.item = item;
idx.items.pop(guard).with_span(self.span)?;
}
State::Const(guard, idx_item, nested_item, node, expr) => {
let item_meta = idx.q.item_for("const", idx.item.id).with_span(self.span)?;
idx.q.index_const_expr(
item_meta,
indexing::ConstExpr::Node(node.node_at(idx.source_id, idx.tree.clone())),
)?;
self.expr = expr;
idx.items.pop(guard).with_span(&node)?;
idx.item = idx_item;
idx.nested_item = nested_item;
}
State::Stream(..) => {}
}
}
Ok(())
}
fn node(&mut self, idx: &mut Indexer<'_, '_>, node: Node<'a>) -> Result<()> {
self.span = node.span();
match node.kind() {
Expr => {
if matches!(self.expr, ExprSupport::No) {
idx.error(Error::msg(
&node,
"expression is not supported during indexing",
))?;
return Ok(());
}
let (mods, attrs, p) = node.parse(|p| {
let mods = p
.eat(Modifiers)
.parse(|p| Mods::parse(idx, p))?
.unwrap_or_default();
let attrs = attributes(idx, p)?;
Ok((mods, attrs, p.take_remaining()))
})?;
self.stack.try_push(State::Expr(mods, attrs))?;
self.stack.try_push(State::Stream(p))?;
return Ok(());
}
ExprMacroCall => {
let builtin;
if let [.., State::Expr(_, attrs), _] = &mut self.stack[..] {
builtin = attrs.builtin.take();
} else {
builtin = None;
}
let (literal, is_builtin) = match builtin {
Some((_, literal)) => (literal, true),
_ => (BuiltInLiteral::No, false),
};
let call = |id| {
Ok::<_, alloc::Error>(ExpandMacroBuiltin {
id,
node: node.node_at(idx.source_id, idx.tree.clone()),
location: Location::new(idx.source_id, node.span()),
root: idx.root.map(TryToOwned::try_to_owned).transpose()?,
macro_depth: idx.macro_depth,
item: idx.item,
literal,
})
};
let id = if is_builtin {
idx.q
.insert_new_macro(move |id| Ok(DeferEntry::ExpandMacroBuiltin(call(id)?)))
.with_span(self.span)?
} else {
for node in node.children() {
if node.kind() == Path {
node.replace(IndexedPath(idx.item.id));
}
}
idx.q
.insert_new_macro(move |id| Ok(DeferEntry::ExpandMacroCall(call(id)?)))
.with_span(self.span)?
};
node.replace(ExpandedMacro(id));
return Ok(());
}
ExprClosure => {
let async_token;
let move_token;
if let [.., State::Expr(mods, _), _] = &mut self.stack[..] {
async_token = mods.async_token.take();
move_token = mods.move_token.take();
} else {
async_token = None;
move_token = None;
}
let guard = idx.push_id()?;
idx.scopes.push()?;
let item_meta = idx.insert_new_item(&self.span, Visibility::Inherited, &[])?;
let item = idx.item.replace(item_meta.item);
self.stack
.try_push(State::Closure(guard, item, async_token, move_token))?;
node.replace(Closure(idx.item.id));
}
Path => {
node.replace(IndexedPath(idx.item.id));
}
Block => {
let async_token;
let const_token;
let move_token;
if let [.., State::Expr(mods, _), _] = &mut self.stack[..] {
async_token = mods.async_token.take();
const_token = mods.const_token.take();
move_token = mods.move_token.take();
} else {
async_token = None;
const_token = None;
move_token = None;
}
let guard = idx.push_id()?;
let item_meta = idx.insert_new_item(&self.span, Visibility::Inherited, &[])?;
let item = idx.item.replace(item_meta.item);
let kind = match (async_token, const_token) {
(Some(const_token), Some(async_token)) => {
idx.error(Error::new(
const_token.span.join(async_token.span),
ErrorKind::FnConstAsyncConflict,
))?;
ExprBlockKind::Default
}
(Some(..), None) => ExprBlockKind::Async,
(None, Some(..)) => ExprBlockKind::Const,
_ => ExprBlockKind::Default,
};
match kind {
ExprBlockKind::Default => {
self.stack.try_push(State::Block(guard, item))?;
}
ExprBlockKind::Const => {
self.stack
.try_push(State::ConstBlock(guard, item, node.clone()))?;
node.replace(ConstBlock(idx.item.id));
}
ExprBlockKind::Async => {
idx.scopes.push()?;
self.stack.try_push(State::AsyncBlock(
guard,
item,
node.span(),
move_token,
))?;
node.replace(AsyncBlock(idx.item.id));
}
}
}
ExprSelect | ExprAwait => {
let l = idx.scopes.mark().with_span(self.span)?;
l.awaits.try_push(node.span())?;
}
ExprYield => {
let l = idx.scopes.mark().with_span(self.span)?;
l.yields.try_push(node.span())?;
}
Item => {
let result = node.parse(|p| {
let attrs = attributes(idx, p)?;
p.pump()?.parse(|p| self.item(idx, p, attrs))?;
Ok(())
});
if let Err(error) = result {
idx.error(error)?;
}
return Ok(());
}
_ => {}
}
let mut p = node.into_stream();
if !p.is_eof() {
self.stack.try_push(State::Stream(p)).with_span(self.span)?;
}
Ok(())
}
fn bare(&mut self, idx: &mut Indexer<'_, '_>, id: NodeId) -> Result<()> {
let item_meta = idx
.q
.item_for("bare function", idx.item.id)
.with_span(self.span)?;
let layer = idx.scopes.pop().with_span(self.span)?;
let call = match (layer.awaits.is_empty(), layer.yields.is_empty()) {
(true, true) => Call::Immediate,
(false, true) => Call::Async,
(true, false) => Call::Generator,
(false, false) => Call::Stream,
};
idx.q.index_and_build(indexing::Entry {
item_meta,
indexed: Indexed::Function(indexing::Function {
ast: indexing::FunctionAst::Bare(idx.tree.node_at(idx.source_id, id)),
call,
is_instance: false,
is_test: false,
is_bench: false,
impl_item: None,
args: Vec::new(),
}),
})?;
Ok(())
}
fn function(
&mut self,
idx: &mut Indexer<'_, '_>,
index: usize,
nested_item: Option<Span>,
) -> Result<(), Error> {
let Some(f) = self.fns.pop() else {
return Err(Error::msg(self.span, "missing function being indexed"));
};
if index != self.fns.len() {
return Err(Error::msg(self.span, "function indexing mismatch"));
}
let item_meta = idx
.q
.item_for("function", idx.item.id)
.with_span(self.span)?;
let Function {
node,
name,
attrs,
mods,
} = f;
self.span = node.span();
let (is_instance, args) = is_instance(&node).with_span(&node)?;
let layer = idx.scopes.pop().with_span(self.span)?;
if let (Some(const_token), Some(async_token)) = (mods.const_token, mods.async_token) {
idx.error(Error::new(
const_token.span.join(async_token.span),
ErrorKind::FnConstAsyncConflict,
))?;
};
let call = validate_call(
mods.const_token.is_some(),
mods.async_token.is_some(),
&layer,
)?;
let Some(call) = call else {
idx.q.index_const_fn(
item_meta,
indexing::ConstFn::Node(node.node_at(idx.source_id, idx.tree.clone())),
)?;
return Ok(());
};
if let (Some(span), Some(_nested_span)) = (attrs.test, nested_item) {
idx.error(Error::new(
span,
ErrorKind::NestedTest {
#[cfg(feature = "emit")]
nested_span: _nested_span,
},
))?;
}
if let (Some(span), Some(_nested_span)) = (attrs.bench, nested_item) {
idx.error(Error::new(
span,
ErrorKind::NestedBench {
#[cfg(feature = "emit")]
nested_span: _nested_span,
},
))?;
}
let is_test = attrs.test.is_some();
let is_bench = attrs.bench.is_some();
if idx.item.impl_item.is_some() {
if is_test {
idx.error(Error::msg(
&node,
"the #[test] attribute is not supported on associated functions",
))?;
}
if is_bench {
idx.error(Error::msg(
&node,
"the #[bench] attribute is not supported on associated functions",
))?;
}
}
if is_instance && idx.item.impl_item.is_none() {
idx.error(Error::new(&node, ErrorKind::InstanceFunctionOutsideImpl))?;
};
let entry = indexing::Entry {
item_meta,
indexed: Indexed::Function(indexing::Function {
ast: indexing::FunctionAst::Node(
node.node_at(idx.source_id, idx.tree.clone()),
name,
),
call,
is_instance,
is_test,
is_bench,
impl_item: idx.item.impl_item,
args,
}),
};
let is_exported = is_instance
|| item_meta.is_public(idx.q.pool) && nested_item.is_none()
|| is_test
|| is_bench;
if is_exported {
idx.q.index_and_build(entry)?;
} else {
idx.q.index(entry)?;
}
Ok(())
}
fn item(&mut self, idx: &mut Indexer<'_, '_>, p: &mut Stream<'a>, attrs: Attrs) -> Result<()> {
let mods = p
.eat(Modifiers)
.parse(|p| Mods::parse(idx, p))?
.unwrap_or_default();
match p.kind() {
ItemFn => {
self.item_fn(idx, p, mods, attrs)?;
}
ItemMod => {
self.item_mod(idx, p, mods, attrs)?;
}
ItemFileMod => {
self.item_file_mod(idx, p, mods, attrs)?;
}
ItemImpl => {
item_impl(idx, p, mods, attrs)?;
}
ItemStruct => {
item_struct(idx, p, mods, attrs)?;
}
ItemEnum => {
item_enum(idx, p, mods, attrs)?;
}
ItemConst => {
self.item_const(idx, p, mods, attrs)?;
}
ItemUse => {
item_use(idx, p, mods, attrs)?;
}
_ => {
idx.error(p.expected("item"))?;
p.ignore();
}
}
Ok(())
}
fn item_fn(
&mut self,
idx: &mut Indexer<'_, '_>,
p: &mut Stream<'a>,
mods: Mods,
attrs: Attrs,
) -> Result<(), Error> {
let expr = replace(&mut self.expr, ExprSupport::Yes);
p.expect(K![fn])?;
let (guard, name) = push_name(idx, p, "function")?;
let item_meta = idx.insert_new_item(&*p, mods.visibility, &attrs.docs)?;
let item = idx.item.replace(item_meta.item);
idx.scopes.push()?;
let nested_item = idx.nested_item.replace(p.span());
let index = self.fns.len();
self.fns.try_push(Function {
node: p.node(),
name,
attrs,
mods,
})?;
self.stack
.try_push(State::Function(guard, item, index, nested_item, expr))?;
self.stack.try_push(State::Stream(p.take_remaining()))?;
Ok(())
}
fn item_mod(
&mut self,
idx: &mut Indexer<'_, '_>,
p: &mut Stream<'a>,
mut mods: Mods,
attrs: Attrs,
) -> Result<()> {
p.expect(K![mod])?;
let (guard, _) = push_name(idx, p, "module")?;
let MaybeNode::Some(node) = p.eat(Block) else {
idx.error(p.expected_peek(Block))?;
return Ok(());
};
let (mod_item, mod_item_id) = idx.q.insert_mod(
&idx.items,
&DynLocation::new(idx.source_id, &*p),
idx.item.module,
mods.visibility.take(),
&attrs.docs,
)?;
let item = idx.item.replace_module(mod_item, mod_item_id);
self.stack.try_push(State::Module(guard, item))?;
self.stack.try_push(State::Stream(node.into_stream()))?;
mods.deny_all(idx)?;
attrs.deny_non_docs(idx)?;
Ok(())
}
fn item_file_mod(
&mut self,
idx: &mut Indexer<'_, '_>,
p: &mut Stream<'_>,
mut mods: Mods,
attrs: Attrs,
) -> Result<()> {
p.expect(K![mod])?;
let (guard, _) = push_name(idx, p, "module")?;
let (mod_item, mod_item_id) = idx.q.insert_mod(
&idx.items,
&DynLocation::new(idx.source_id, &*p),
idx.item.module,
mods.visibility.take(),
&attrs.docs,
)?;
idx.items.pop(guard).with_span(&*p)?;
let Some(root) = idx.root else {
return Err(Error::new(&*p, ErrorKind::UnsupportedModuleSource));
};
let source = idx
.q
.source_loader
.load(root, idx.q.pool.module_item(mod_item), &*p)?;
if let Some(loaded) = idx.loaded.as_mut() {
if let Some(_existing) = loaded.try_insert(mod_item, (idx.source_id, p.span()))? {
return Err(Error::new(
&*p,
ErrorKind::ModAlreadyLoaded {
item: idx.q.pool.module_item(mod_item).try_to_owned()?,
#[cfg(feature = "emit")]
existing: _existing,
},
));
}
}
let source_id = idx.q.sources.insert(source)?;
idx.q
.visitor
.visit_mod(&DynLocation::new(source_id, &*p))
.with_span(&*p)?;
if let Some(queue) = idx.queue.as_mut() {
queue.try_push_back(worker::Task::LoadFile {
kind: worker::LoadFileKind::Module {
root: idx.root.map(|p| p.try_to_owned()).transpose()?,
},
source_id,
mod_item,
mod_item_id,
})?;
}
mods.deny_all(idx)?;
attrs.deny_non_docs(idx)?;
Ok(())
}
fn item_const(
&mut self,
idx: &mut Indexer<'_, '_>,
p: &mut Stream<'a>,
mut mods: Mods,
attrs: Attrs,
) -> Result<()> {
let expr = replace(&mut self.expr, ExprSupport::Yes);
if mods.const_token.take().is_none() {
idx.error(Error::msg(&*p, "missing `const` modifier"))?;
}
let (guard, _) = push_name(idx, p, "constant")?;
p.expect(K![=])?;
let value = p.expect(Expr)?;
let item_meta = idx.insert_new_item(&value, mods.visibility.take(), &attrs.docs)?;
let idx_item = idx.item.replace(item_meta.item);
let last = idx.nested_item.replace(value.span());
self.stack
.try_push(State::Const(guard, idx_item, last, value.clone(), expr))?;
self.stack.try_push(State::Stream(value.into_stream()))?;
mods.deny_all(idx)?;
attrs.deny_non_docs(idx)?;
Ok(())
}
}
pub(crate) fn bare(idx: &mut Indexer<'_, '_>, p: &mut Stream<'_>) -> Result<()> {
let item_meta = idx.insert_new_item(p, Visibility::Public, &[])?;
let item = idx.item.replace(item_meta.item);
idx.scopes.push()?;
inner_attributes(idx, p)?;
let mut proc = Processor::new(p.span()).with_expr(ExprSupport::Yes);
proc.stack.try_push(State::Bare(item, p.id()))?;
proc.stack.try_push(State::Stream(p.take_remaining()))?;
proc.process(idx)?;
Ok(())
}
pub(crate) fn file(idx: &mut Indexer<'_, '_>, p: &mut Stream<'_>) -> Result<()> {
inner_attributes(idx, p)?;
let mut proc = Processor::new(p.span());
proc.stack.try_push(State::Stream(p.take_remaining()))?;
proc.process(idx)?;
Ok(())
}
pub(crate) fn item(idx: &mut Indexer<'_, '_>, p: &mut Stream<'_>, attrs: Attrs) -> Result<()> {
let mut proc = Processor::new(p.span());
proc.item(idx, p, attrs)?;
proc.process(idx)?;
Ok(())
}
pub(crate) fn any(idx: &mut Indexer<'_, '_>, p: &mut Stream<'_>) -> Result<()> {
let mut proc = Processor::new(p.span()).with_expr(ExprSupport::Yes);
proc.stack.try_push(State::Stream(p.take_remaining()))?;
proc.process(idx)?;
Ok(())
}
fn item_impl(
idx: &mut Indexer<'_, '_>,
p: &mut Stream<'_>,
mods: Mods,
attrs: Attrs,
) -> Result<()> {
p.expect(K![impl])?;
let MaybeNode::Some(node) = p.eat(Path) else {
idx.error(p.expected_peek(Path))?;
return Ok(());
};
node.replace(IndexedPath(idx.item.id));
let mut functions = Vec::new();
p.eat(Block).parse(|p| {
p.eat(K!['{']);
p.expect(BlockBody)?.parse(|p| {
while let MaybeNode::Some(item) = p.eat(Item) {
item.parse(|p| {
let attrs = attributes(idx, p)?;
p.pump()?.parse(|p| {
let mods = p
.eat(Modifiers)
.parse(|p| Mods::parse(idx, p))?
.unwrap_or_default();
mods.deny_all(idx)?;
match p.kind() {
ItemFn => {
functions.try_push((p.id(), attrs)).with_span(&*p)?;
p.ignore();
}
_ => {
idx.error(p.expected(ItemFn)).with_span(&*p)?;
}
}
Ok(())
})?;
Ok(())
})?;
}
Ok(())
})?;
p.eat(K!['}']);
Ok(())
})?;
let location = Location::new(idx.source_id, p.span());
idx.q
.inner
.defer_queue
.try_push_back(DeferEntry::ImplItem(ImplItem {
kind: ImplItemKind::Node {
path: node.node_at(idx.source_id, idx.tree.clone()),
functions,
},
location,
root: idx.root.map(TryToOwned::try_to_owned).transpose()?,
nested_item: idx.nested_item,
macro_depth: idx.macro_depth,
}))?;
mods.deny_all(idx)?;
attrs.deny_non_docs(idx)?;
Ok(())
}
fn item_struct(
idx: &mut Indexer<'_, '_>,
p: &mut Stream<'_>,
mut mods: Mods,
attrs: Attrs,
) -> Result<()> {
p.expect(K![struct])?;
let (guard, _) = push_name(idx, p, "struct")?;
let item_meta = idx.insert_new_item(&*p, mods.visibility.take(), &attrs.docs)?;
let fields = p.pump()?.parse(|p| fields(idx, p))?;
idx.q.index_struct(item_meta, indexing::Struct { fields })?;
idx.items.pop(guard).with_span(&*p)?;
mods.deny_all(idx)?;
attrs.deny_non_docs(idx)?;
Ok(())
}
fn item_enum(
idx: &mut Indexer<'_, '_>,
p: &mut Stream<'_>,
mut mods: Mods,
attrs: Attrs,
) -> Result<()> {
p.expect(K![enum])?;
let (guard, _) = push_name(idx, p, "enum")?;
let vis = mods.visibility.take();
let enum_item_meta = idx.insert_new_item(&*p, vis, &attrs.docs)?;
idx.q.index_enum(enum_item_meta)?;
p.eat(K!['{']);
let mut index = 0;
let mut comma = Remaining::default();
while let MaybeNode::Some(node) = p.eat(Variant) {
comma.at_most_one(idx)?;
let (item_meta, fields, attrs) = node.parse(|p| {
let attrs = attributes(idx, p)?;
let (guard, _) = push_name(idx, p, "variant")?;
let item_meta = idx.insert_new_item(&*p, vis, &attrs.docs)?;
let fields = p.pump()?.parse(|p| fields(idx, p))?;
idx.items.pop(guard).with_span(&*p)?;
Ok((item_meta, fields, attrs))
})?;
attrs.deny_non_docs(idx)?;
let variant = indexing::Variant {
enum_id: enum_item_meta.item,
index,
fields,
};
idx.q.index_variant(item_meta, variant)?;
comma = p.remaining(idx, K![,])?;
index += 1;
}
comma.at_most_one(idx)?;
p.eat(K!['}']);
idx.items.pop(guard).with_span(&*p)?;
mods.deny_all(idx)?;
attrs.deny_non_docs(idx)?;
Ok(())
}
fn item_use(
idx: &mut Indexer<'_, '_>,
p: &mut Stream<'_>,
mut mods: Mods,
attrs: Attrs,
) -> Result<()> {
let import = Import {
state: ImportState::Node(p.node().node_at(idx.source_id, idx.tree.clone())),
kind: ImportKind::Global,
visibility: mods.visibility.take(),
module: idx.item.module,
item: idx.items.item().try_to_owned()?,
source_id: idx.source_id,
};
let span = p.span();
import.process(&mut idx.q, &mut |task| {
let Some(queue) = &mut idx.queue else {
return Err(Error::msg(
span,
"deferred imports are not supported in this context",
));
};
queue.try_push_back(task)?;
Ok(())
})?;
p.ignore();
mods.deny_all(idx)?;
attrs.deny_any(idx)?;
Ok(())
}
fn push_name(
idx: &mut Indexer<'_, '_>,
p: &mut Stream<'_>,
what: &'static str,
) -> Result<(Guard, Option<ast::Ident>)> {
let (guard, ident) = if let Some(ident) = p.try_ast::<ast::Ident>()? {
let name = ident.resolve(resolve_context!(idx.q))?;
(idx.items.push_name(name.as_ref())?, Some(ident))
} else {
idx.error(Error::msg(
p.peek_span().head(),
try_format!("expected {what} name"),
))?;
(idx.push_id()?, None)
};
Ok((guard, ident))
}
fn attributes(idx: &mut Indexer<'_, '_>, p: &mut Stream<'_>) -> Result<Attrs> {
let mut attrs = Attrs::default();
while let MaybeNode::Some(node) = p.eat(Attribute) {
node.parse(|p| {
let span = p.span();
p.all([K![#], K!['[']])?;
p.expect(TokenStream)?.parse(|p| {
let ident = p.ast::<ast::Ident>()?;
match ident.resolve(resolve_context!(idx.q))? {
"test" => {
if attrs.bench.is_some() {
idx.error(Error::msg(ident.span, "duplicate #[test] attribute"))?;
} else {
attrs.test = Some(ident.span);
}
}
"bench" => {
if attrs.bench.is_some() {
idx.error(Error::msg(ident.span, "duplicate #[bench] attribute"))?;
} else {
attrs.bench = Some(ident.span);
}
}
"doc" => {
p.expect(K![=])?;
let doc_string = p.ast::<ast::LitStr>()?;
attrs
.docs
.try_push(Doc { span, doc_string })
.with_span(&*p)?;
}
"builtin" => {
let mut literal = BuiltInLiteral::No;
if p.eat(K!['(']).is_some() {
while matches!(p.peek(), K![ident]) {
let ident = p.ast::<ast::Ident>()?;
match ident.resolve(resolve_context!(idx.q))? {
"literal" => {
literal = BuiltInLiteral::Yes(ident.span());
}
other => {
idx.error(Error::msg(
ident,
try_format!("unsupported builtin option `{other}`"),
))?;
}
}
p.remaining(idx, K![,])?.ignore(idx)?;
}
p.expect(K![')'])?;
}
if attrs.builtin.is_some() {
idx.error(Error::msg(ident.span, "duplicate #[builtin] attribute"))?;
} else {
attrs.builtin = Some((ident.span, literal));
}
}
name => {
idx.error(Error::msg(
ident,
try_format!("unsupported attribute `{name}`"),
))?;
}
}
Ok(())
})?;
p.expect(K![']'])?;
Ok(())
})?;
}
Ok(attrs)
}
fn inner_attributes(idx: &mut Indexer<'_, '_>, p: &mut Stream<'_>) -> Result<()> {
while let MaybeNode::Some(node) = p.eat(InnerAttribute) {
node.parse(|p| {
p.all([K![#], K![!], K!['[']])?;
p.expect(TokenStream)?.parse(|p| {
let ident = p.ast::<ast::Ident>()?;
match ident.resolve(resolve_context!(idx.q))? {
"doc" => {
p.expect(K![=])?;
let str = p.ast::<ast::LitStr>()?;
let str = str.resolve(resolve_context!(idx.q))?;
let loc = DynLocation::new(idx.source_id, &*p);
let item = idx.q.pool.item(idx.item.id);
let hash = idx.q.pool.item_type_hash(idx.item.id);
idx.q
.visitor
.visit_doc_comment(&loc, item, hash, &str)
.with_span(&*p)?;
}
name => {
idx.error(Error::msg(
ident,
try_format!("unsupported attribute `{name}`"),
))?;
}
}
Ok(())
})?;
p.expect(K![']'])?;
Ok(())
})?;
}
Ok(())
}
fn fields(idx: &mut Indexer<'_, '_>, p: &mut Stream<'_>) -> Result<meta::Fields> {
match p.kind() {
StructBody => {
p.one(K!['{']).exactly_one(idx)?;
let mut fields = Vec::new();
let mut comma = Remaining::default();
while let MaybeNode::Some(field) = p.eat(Field) {
comma.exactly_one(idx)?;
let name = field.parse(|p| p.ast::<ast::Ident>())?;
let name = name.resolve(resolve_context!(idx.q))?;
let position = fields.len();
fields.try_push(meta::FieldMeta {
name: name.try_into()?,
position,
})?;
comma = p.remaining(idx, K![,])?;
}
comma.at_most_one(idx)?;
p.one(K!['}']).exactly_one(idx)?;
Ok(meta::Fields::Named(meta::FieldsNamed {
fields: fields.try_into()?,
}))
}
TupleBody => {
p.one(K!['(']).exactly_one(idx)?;
let mut count = 0;
let mut comma = Remaining::default();
while p.eat(Field).is_some() {
comma.exactly_one(idx)?;
count += 1;
comma = p.remaining(idx, K![,])?;
}
comma.at_most_one(idx)?;
p.one(K![')']).exactly_one(idx)?;
Ok(meta::Fields::Unnamed(count))
}
EmptyBody => Ok(meta::Fields::Empty),
_ => {
idx.error(p.expected("struct body"))?;
Ok(meta::Fields::Empty)
}
}
}
#[derive(Default, Debug)]
struct Mods {
span: Span,
visibility: Visibility,
const_token: Option<ast::Const>,
async_token: Option<ast::Async>,
move_token: Option<ast::Move>,
}
impl Mods {
fn parse(cx: &mut dyn Ignore<'_>, p: &mut Stream<'_>) -> Result<Mods> {
let mut mods = Mods {
span: p.span().head(),
visibility: Visibility::Inherited,
const_token: None,
async_token: None,
move_token: None,
};
loop {
match p.peek() {
K![pub] => {
mods.visibility = Visibility::Public;
}
ModifierSelf if mods.visibility == Visibility::Public => {
mods.visibility = Visibility::SelfValue;
}
ModifierSuper if mods.visibility == Visibility::Public => {
mods.visibility = Visibility::Super;
}
ModifierCrate if mods.visibility == Visibility::Public => {
mods.visibility = Visibility::Crate;
}
_ => {
break;
}
}
mods.span = mods.span.join(p.pump()?.span());
}
while let Some(tok) = p.try_ast::<ast::Const>()? {
if mods.const_token.is_some() {
cx.error(Error::msg(tok, "duplicate `const` modifier"))?;
} else {
mods.const_token = Some(tok);
}
}
while let Some(tok) = p.try_ast::<ast::Async>()? {
if mods.async_token.is_some() {
cx.error(Error::msg(tok, "duplicate `async` modifier"))?;
} else {
mods.async_token = Some(tok);
}
}
while let Some(tok) = p.try_ast::<ast::Move>()? {
if mods.move_token.is_some() {
cx.error(Error::msg(tok, "duplicate `move` modifier"))?;
} else {
mods.move_token = Some(tok);
}
}
Ok(mods)
}
fn deny_all(self, cx: &mut dyn Ignore<'_>) -> Result<()> {
if !matches!(self.visibility, Visibility::Inherited) {
cx.error(Error::msg(self.span, "unsupported visibility modifier"))?;
}
if let Some(span) = self.const_token {
cx.error(Error::msg(span, "unsupported `const` modifier"))?;
}
if let Some(span) = self.async_token {
cx.error(Error::msg(span, "unsupported `async` modifier"))?;
}
Ok(())
}
}