Batch errors from the parser

This commit is contained in:
David Tolnay 2020-05-03 23:59:40 -07:00
parent cbf3f03ff2
commit 5275978d13
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
5 changed files with 60 additions and 49 deletions

View File

@ -47,8 +47,9 @@ fn generate(path: &Path, opt: Opt, header: bool) -> Vec<u8> {
let syntax = syn::parse_file(&source)?;
let bridge = find_bridge_mod(syntax)?;
let ref namespace = bridge.namespace;
let ref apis = syntax::parse_items(bridge.module)?;
let ref types = Types::collect(apis)?;
let ref apis = syntax::parse_items(errors, bridge.module);
let ref types = Types::collect(errors, apis);
errors.propagate()?;
check::typecheck(errors, namespace, apis, types);
errors.propagate()?;
let out = write::gen(namespace, apis, types, opt, header);

View File

@ -15,8 +15,9 @@ pub fn bridge(namespace: &Namespace, mut ffi: ItemMod) -> Result<TokenStream> {
Span::call_site(),
"#[cxx::bridge] module must have inline contents",
))?;
let ref apis = syntax::parse_items(content.1)?;
let ref types = Types::collect(apis)?;
let ref apis = syntax::parse_items(errors, content.1);
let ref types = Types::collect(errors, apis);
errors.propagate()?;
check::typecheck(errors, namespace, apis, types);
errors.propagate()?;

View File

@ -1,3 +1,4 @@
use crate::syntax::report::Errors;
use crate::syntax::Atom::*;
use crate::syntax::{
attrs, error, Api, Doc, Enum, ExternFn, ExternType, Lang, Receiver, Ref, Signature, Slice,
@ -16,27 +17,24 @@ pub mod kw {
syn::custom_keyword!(Result);
}
pub fn parse_items(items: Vec<Item>) -> Result<Vec<Api>> {
pub fn parse_items(cx: &mut Errors, items: Vec<Item>) -> Vec<Api> {
let mut apis = Vec::new();
for item in items {
match item {
Item::Struct(item) => {
let strct = parse_struct(item)?;
apis.push(strct);
}
Item::Enum(item) => {
let enm = parse_enum(item)?;
apis.push(enm);
}
Item::ForeignMod(foreign_mod) => {
let functions = parse_foreign_mod(foreign_mod)?;
apis.extend(functions);
}
Item::Use(item) => return Err(Error::new_spanned(item, error::USE_NOT_ALLOWED)),
_ => return Err(Error::new_spanned(item, "unsupported item")),
Item::Struct(item) => match parse_struct(item) {
Ok(strct) => apis.push(strct),
Err(err) => cx.push(err),
},
Item::Enum(item) => match parse_enum(item) {
Ok(enm) => apis.push(enm),
Err(err) => cx.push(err),
},
Item::ForeignMod(foreign_mod) => parse_foreign_mod(cx, foreign_mod, &mut apis),
Item::Use(item) => cx.error(item, error::USE_NOT_ALLOWED),
_ => cx.error(item, "unsupported item"),
}
}
Ok(apis)
apis
}
fn parse_struct(item: ItemStruct) -> Result<Api> {
@ -151,8 +149,11 @@ fn parse_variant(variant: RustVariant) -> Result<Variant> {
}
}
fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result<Vec<Api>> {
let lang = parse_lang(foreign_mod.abi)?;
fn parse_foreign_mod(cx: &mut Errors, foreign_mod: ItemForeignMod, out: &mut Vec<Api>) {
let lang = match parse_lang(foreign_mod.abi) {
Ok(lang) => lang,
Err(err) => return cx.push(err),
};
let api_type = match lang {
Lang::Cxx => Api::CxxType,
Lang::Rust => Api::RustType,
@ -165,19 +166,21 @@ fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result<Vec<Api>> {
let mut items = Vec::new();
for foreign in &foreign_mod.items {
match foreign {
ForeignItem::Type(foreign) => {
let ety = parse_extern_type(foreign)?;
items.push(api_type(ety));
}
ForeignItem::Fn(foreign) => {
let efn = parse_extern_fn(foreign, lang)?;
items.push(api_function(efn));
}
ForeignItem::Type(foreign) => match parse_extern_type(foreign) {
Ok(ety) => items.push(api_type(ety)),
Err(err) => cx.push(err),
},
ForeignItem::Fn(foreign) => match parse_extern_fn(foreign, lang) {
Ok(efn) => items.push(api_function(efn)),
Err(err) => cx.push(err),
},
ForeignItem::Macro(foreign) if foreign.mac.path.is_ident("include") => {
let include = foreign.mac.parse_body()?;
items.push(Api::Include(include));
match foreign.mac.parse_body() {
Ok(include) => items.push(Api::Include(include)),
Err(err) => cx.push(err),
}
}
_ => return Err(Error::new_spanned(foreign, "unsupported foreign item")),
_ => cx.error(foreign, "unsupported foreign item"),
}
}
@ -198,7 +201,7 @@ fn parse_foreign_mod(foreign_mod: ItemForeignMod) -> Result<Vec<Api>> {
}
}
Ok(items)
out.extend(items);
}
fn parse_lang(abi: Abi) -> Result<Lang> {

View File

@ -15,6 +15,10 @@ impl Errors {
self.errors.push(Error::new_spanned(sp, msg));
}
pub fn push(&mut self, error: Error) {
self.errors.push(error);
}
pub fn propagate(&mut self) -> Result<()> {
let mut iter = self.errors.drain(..);
let mut all_errors = match iter.next() {

View File

@ -1,10 +1,10 @@
use crate::syntax::atom::Atom::{self, *};
use crate::syntax::report::Errors;
use crate::syntax::set::OrderedSet as Set;
use crate::syntax::{Api, Derive, Enum, Struct, Type};
use proc_macro2::Ident;
use quote::ToTokens;
use std::collections::{BTreeMap as Map, HashSet as UnorderedSet};
use syn::{Error, Result};
pub struct Types<'a> {
pub all: Set<'a, Type>,
@ -15,7 +15,7 @@ pub struct Types<'a> {
}
impl<'a> Types<'a> {
pub fn collect(apis: &'a [Api]) -> Result<Self> {
pub fn collect(cx: &mut Errors, apis: &'a [Api]) -> Self {
let mut all = Set::new();
let mut structs = Map::new();
let mut enums = Map::new();
@ -50,39 +50,41 @@ impl<'a> Types<'a> {
Api::Include(_) => {}
Api::Struct(strct) => {
let ident = &strct.ident;
if !type_names.insert(ident) {
return Err(duplicate_name(strct, ident));
if type_names.insert(ident) {
structs.insert(ident.clone(), strct);
} else {
duplicate_name(cx, strct, ident);
}
structs.insert(ident.clone(), strct);
for field in &strct.fields {
visit(&mut all, &field.ty);
}
}
Api::Enum(enm) => {
let ident = &enm.ident;
if !type_names.insert(ident) {
return Err(duplicate_name(enm, ident));
if type_names.insert(ident) {
enums.insert(ident.clone(), enm);
} else {
duplicate_name(cx, enm, ident);
}
enums.insert(ident.clone(), enm);
}
Api::CxxType(ety) => {
let ident = &ety.ident;
if !type_names.insert(ident) {
return Err(duplicate_name(ety, ident));
duplicate_name(cx, ety, ident);
}
cxx.insert(ident);
}
Api::RustType(ety) => {
let ident = &ety.ident;
if !type_names.insert(ident) {
return Err(duplicate_name(ety, ident));
duplicate_name(cx, ety, ident);
}
rust.insert(ident);
}
Api::CxxFunction(efn) | Api::RustFunction(efn) => {
let ident = &efn.ident;
if !function_names.insert((&efn.receiver, ident)) {
return Err(duplicate_name(efn, ident));
duplicate_name(cx, efn, ident);
}
for arg in &efn.args {
visit(&mut all, &arg.ty);
@ -94,13 +96,13 @@ impl<'a> Types<'a> {
}
}
Ok(Types {
Types {
all,
structs,
enums,
cxx,
rust,
})
}
}
pub fn needs_indirect_abi(&self, ty: &Type) -> bool {
@ -135,7 +137,7 @@ impl<'t, 'a> IntoIterator for &'t Types<'a> {
}
}
fn duplicate_name(sp: impl ToTokens, ident: &Ident) -> Error {
fn duplicate_name(cx: &mut Errors, sp: impl ToTokens, ident: &Ident) {
let msg = format!("the name `{}` is defined multiple times", ident);
Error::new_spanned(sp, msg)
cx.error(sp, msg);
}