Extract trivial extern type alias detection to module

This commit is contained in:
David Tolnay 2020-12-20 21:04:04 -08:00
parent ee20544e01
commit 0634b1f28c
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
4 changed files with 137 additions and 116 deletions

View File

@ -4,7 +4,7 @@ use crate::gen::out::OutFile;
use crate::gen::{builtin, include, Opt};
use crate::syntax::atom::Atom::{self, *};
use crate::syntax::symbol::Symbol;
use crate::syntax::types::TrivialReason;
use crate::syntax::trivial::TrivialReason;
use crate::syntax::{
derive, mangle, Api, Enum, ExternFn, ExternType, Pair, RustName, Signature, Struct, Trait,
Type, Types, Var,

View File

@ -22,6 +22,7 @@ pub mod set;
pub mod symbol;
mod tokens;
mod toposort;
pub mod trivial;
pub mod types;
use self::discriminant::Discriminant;

131
syntax/trivial.rs Normal file
View File

@ -0,0 +1,131 @@
use crate::syntax::set::OrderedSet as Set;
use crate::syntax::{Api, Enum, ExternFn, ExternType, RustName, Struct, Type};
use proc_macro2::Ident;
use std::collections::BTreeMap as Map;
use std::fmt::Display;
#[derive(Copy, Clone)]
pub enum TrivialReason<'a> {
StructField(&'a Struct),
FunctionArgument(&'a ExternFn),
FunctionReturn(&'a ExternFn),
BoxTarget,
VecElement,
UnpinnedMutArg(&'a ExternFn),
}
pub fn required_trivial_reasons<'a>(
apis: &'a [Api],
all: &Set<&'a Type>,
structs: &Map<&'a Ident, &'a Struct>,
enums: &Map<&'a Ident, &'a Enum>,
cxx: &Set<&'a Ident>,
) -> Map<&'a Ident, Vec<TrivialReason<'a>>> {
let mut required_trivial = Map::new();
let mut insist_extern_types_are_trivial = |ident: &'a RustName, reason| {
if cxx.contains(&ident.rust)
&& !structs.contains_key(&ident.rust)
&& !enums.contains_key(&ident.rust)
{
required_trivial
.entry(&ident.rust)
.or_insert_with(Vec::new)
.push(reason);
}
};
for api in apis {
match api {
Api::Struct(strct) => {
for field in &strct.fields {
if let Type::Ident(ident) = &field.ty {
let reason = TrivialReason::StructField(strct);
insist_extern_types_are_trivial(ident, reason);
}
}
}
Api::CxxFunction(efn) | Api::RustFunction(efn) => {
if let Some(receiver) = &efn.receiver {
if receiver.mutable && !receiver.pinned {
let reason = TrivialReason::UnpinnedMutArg(efn);
insist_extern_types_are_trivial(&receiver.ty, reason);
}
}
for arg in &efn.args {
match &arg.ty {
Type::Ident(ident) => {
let reason = TrivialReason::FunctionArgument(efn);
insist_extern_types_are_trivial(ident, reason);
}
Type::Ref(ty) => {
if ty.mutable && !ty.pinned {
if let Type::Ident(ident) = &ty.inner {
let reason = TrivialReason::UnpinnedMutArg(efn);
insist_extern_types_are_trivial(ident, reason);
}
}
}
_ => {}
}
}
if let Some(ret) = &efn.ret {
if let Type::Ident(ident) = &ret {
let reason = TrivialReason::FunctionReturn(efn);
insist_extern_types_are_trivial(ident, reason);
}
}
}
_ => {}
}
}
for ty in all {
match ty {
Type::RustBox(ty) => {
if let Type::Ident(ident) = &ty.inner {
let reason = TrivialReason::BoxTarget;
insist_extern_types_are_trivial(ident, reason);
}
}
Type::RustVec(ty) => {
if let Type::Ident(ident) = &ty.inner {
let reason = TrivialReason::VecElement;
insist_extern_types_are_trivial(ident, reason);
}
}
_ => {}
}
}
required_trivial
}
impl<'a> TrivialReason<'a> {
pub fn describe_in_context(&self, ety: &ExternType) -> String {
match self {
TrivialReason::BoxTarget => format!("Box<{}>", ety.name.rust),
TrivialReason::VecElement => format!("a vector element in Vec<{}>", ety.name.rust),
_ => self.to_string(),
}
}
}
impl<'a> Display for TrivialReason<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TrivialReason::StructField(strct) => write!(f, "a field of `{}`", strct.name.rust),
TrivialReason::FunctionArgument(efn) => write!(f, "an argument of `{}`", efn.name.rust),
TrivialReason::FunctionReturn(efn) => {
write!(f, "a return value of `{}`", efn.name.rust)
}
TrivialReason::BoxTarget => write!(f, "in a Box<...>"),
TrivialReason::VecElement => write!(f, "a Vec<...> element"),
TrivialReason::UnpinnedMutArg(efn) => write!(
f,
"a non-pinned mutable reference argument of {}",
efn.name.rust
),
}
}
}

View File

@ -1,13 +1,13 @@
use crate::syntax::improper::ImproperCtype;
use crate::syntax::report::Errors;
use crate::syntax::set::{OrderedSet as Set, UnorderedSet};
use crate::syntax::trivial::{self, TrivialReason};
use crate::syntax::{
toposort, Api, Enum, ExternFn, ExternType, Impl, Pair, RustName, Struct, Type, TypeAlias,
toposort, Api, Enum, ExternType, Impl, Pair, RustName, Struct, Type, TypeAlias,
};
use proc_macro2::Ident;
use quote::ToTokens;
use std::collections::BTreeMap as Map;
use std::fmt::Display;
pub struct Types<'a> {
pub all: Set<&'a Type>,
@ -168,80 +168,8 @@ impl<'a> Types<'a> {
// we check that this is permissible. We do this _after_ scanning all
// the APIs above, in case some function or struct references a type
// which is declared subsequently.
let mut required_trivial: Map<_, Vec<_>> = Map::new();
let mut insist_extern_types_are_trivial = |ident: &'a RustName, reason| {
if cxx.contains(&ident.rust)
&& !structs.contains_key(&ident.rust)
&& !enums.contains_key(&ident.rust)
{
required_trivial
.entry(&ident.rust)
.or_default()
.push(reason);
}
};
for api in apis {
match api {
Api::Struct(strct) => {
for field in &strct.fields {
if let Type::Ident(ident) = &field.ty {
let reason = TrivialReason::StructField(strct);
insist_extern_types_are_trivial(ident, reason);
}
}
}
Api::CxxFunction(efn) | Api::RustFunction(efn) => {
if let Some(receiver) = &efn.receiver {
if receiver.mutable && !receiver.pinned {
let reason = TrivialReason::UnpinnedMutArg(efn);
insist_extern_types_are_trivial(&receiver.ty, reason);
}
}
for arg in &efn.args {
match &arg.ty {
Type::Ident(ident) => {
let reason = TrivialReason::FunctionArgument(efn);
insist_extern_types_are_trivial(ident, reason);
}
Type::Ref(ty) => {
if ty.mutable && !ty.pinned {
if let Type::Ident(ident) = &ty.inner {
let reason = TrivialReason::UnpinnedMutArg(efn);
insist_extern_types_are_trivial(ident, reason);
}
}
}
_ => {}
}
}
if let Some(ret) = &efn.ret {
if let Type::Ident(ident) = &ret {
let reason = TrivialReason::FunctionReturn(efn);
insist_extern_types_are_trivial(ident, reason);
}
}
}
_ => {}
}
}
for ty in &all {
match ty {
Type::RustBox(ty) => {
if let Type::Ident(ident) = &ty.inner {
let reason = TrivialReason::BoxTarget;
insist_extern_types_are_trivial(ident, reason);
}
}
Type::RustVec(ty) => {
if let Type::Ident(ident) = &ty.inner {
let reason = TrivialReason::VecElement;
insist_extern_types_are_trivial(ident, reason);
}
}
_ => {}
}
}
let required_trivial =
trivial::required_trivial_reasons(apis, &all, &structs, &enums, &cxx);
let mut types = Types {
all,
@ -321,45 +249,6 @@ impl<'t, 'a> IntoIterator for &'t Types<'a> {
}
}
#[derive(Copy, Clone)]
pub enum TrivialReason<'a> {
StructField(&'a Struct),
FunctionArgument(&'a ExternFn),
FunctionReturn(&'a ExternFn),
BoxTarget,
VecElement,
UnpinnedMutArg(&'a ExternFn),
}
impl<'a> TrivialReason<'a> {
pub fn describe_in_context(&self, ety: &ExternType) -> String {
match self {
TrivialReason::BoxTarget => format!("Box<{}>", ety.name.rust),
TrivialReason::VecElement => format!("a vector element in Vec<{}>", ety.name.rust),
_ => self.to_string(),
}
}
}
impl<'a> Display for TrivialReason<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TrivialReason::StructField(strct) => write!(f, "a field of `{}`", strct.name.rust),
TrivialReason::FunctionArgument(efn) => write!(f, "an argument of `{}`", efn.name.rust),
TrivialReason::FunctionReturn(efn) => {
write!(f, "a return value of `{}`", efn.name.rust)
}
TrivialReason::BoxTarget => write!(f, "in a Box<...>"),
TrivialReason::VecElement => write!(f, "a Vec<...> element"),
TrivialReason::UnpinnedMutArg(efn) => write!(
f,
"a non-pinned mutable reference argument of {}",
efn.name.rust
),
}
}
}
fn duplicate_name(cx: &mut Errors, sp: impl ToTokens, ident: &Ident) {
let msg = format!("the name `{}` is defined multiple times", ident);
cx.error(sp, msg);