Commit 00a94983 authored by Federico Mena Quintero's avatar Federico Mena Quintero

Merge branch 'mredlek/gnome-class-syn0.15'

parents 975ebcae 8b5d118b
target/
**/*.rs.bk
*~
Counter.gir
.idea
This diff is collapsed.
......@@ -10,16 +10,14 @@ proc-macro = true
test = false
[dependencies]
compiletest_rs = "0.3.10"
error-chain = "0.11.0-rc.2"
glib = "0.5.0"
glib-sys = "0.6.0"
gobject-sys = "0.6.0"
libc = "0.2"
proc-macro2 = {version = "0.4.8",features = ["nightly"]}
proc-macro2 = {version = "0.4.8"}
quote = { version="0.6" }
rustfmt = "0.10.0"
syn = { version="0.14", features=["full", "extra-traits"] }
syn = { version="0.15.3", features=["full"] }
unicode-xid = "0.0.4"
xml-rs = "0.8.0"
......
......@@ -2,6 +2,9 @@
use proc_macro2::Ident;
use syn::punctuated::Punctuated;
use syn::{Attribute, Lit};
use parser::keywords;
use syn::token;
use syn::spanned::Spanned;
use syn::{Block, FieldsNamed, FnArg, Path, ReturnType, Type, LitStr};
pub struct Program {
......@@ -51,10 +54,12 @@ pub fn get_program_classes<'a>(program: &'a Program) -> Vec<&'a Class> {
}
pub struct Class {
pub attrs: Vec<Attribute>,
pub class: keywords::class,
pub name: Ident,
pub colon: Option<Token![:]>,
pub extends: Option<Path>,
pub fields: FieldsNamed,
pub gir: Option<LitStr>,
}
// similar to syn::ItemImpl
......@@ -66,8 +71,11 @@ pub struct Impl {
}
pub struct Interface {
pub attrs: Vec<Attribute>,
pub interface: keywords::interface,
pub name: Ident,
// FIXME: required class and interfaces
pub brace: token::Brace,
pub items: Vec<ImplItem>,
}
......@@ -82,19 +90,56 @@ pub enum ImplItemKind {
ReserveSlots(Lit),
}
impl ImplItemKind
{
pub fn ident_span(&self) -> ::proc_macro2::Span {
match self {
ImplItemKind::Method(ImplItemMethod { ref name, ..}) => name.span(),
ImplItemKind::Prop(ImplProp { ref name, ..}) => name.span(),
ImplItemKind::ReserveSlots(ref l) => l.span(),
}
}
}
pub struct ImplItemMethod {
pub public: Option<Ident>, // requires body
pub virtual_: Option<Ident>, // implies public, doesn't need body
pub signal: Option<Ident>, // ignore
pub public: Option<Token![pub]>, // requires body
pub virtual_: Option<Token![virtual]>, // implies public, doesn't need body
pub signal: Option<keywords::signal>, // ignore
pub fn_: Token![fn],
pub name: Ident,
pub parem_token: token::Paren,
pub inputs: Punctuated<FnArg, Token!(,)>, // must start with &self
pub output: ReturnType,
pub body: Option<Block>,
pub body: ImplItemMethodBlock,
}
pub enum ImplItemMethodBlock
{
Block(Block),
Empty(Token![;]),
}
impl ImplItemMethodBlock
{
pub fn as_ref(&self) -> Option<&Block>
{
match self {
ImplItemMethodBlock::Block(ref b) => Some(b),
ImplItemMethodBlock::Empty(_) => None,
}
}
}
pub struct ImplProp {
pub property: keywords::property,
pub name: Ident,
pub colon1: Token![:],
pub t1: keywords::T,
pub where_: Token![where],
pub t2: keywords::T,
pub colon2: Token![:],
pub type_: Type,
pub braces: token::Brace,
pub items: Vec<ImplPropBlock>,
}
......@@ -115,12 +160,41 @@ impl ImplProp {
}
pub enum ImplPropBlock {
Getter(Block),
Getter(ImplPropGetter),
Setter(ImplPropSetter),
Default(ImplPropDefault),
}
// get(&self) -> T {}
pub struct ImplPropGetter {
pub get: keywords::get,
pub paren: token::Paren,
pub and: Token![&],
pub self_: Token![self],
pub rarrow: Token![->],
pub t: keywords::T,
pub block: Block,
}
// set(&mut self, ident: T)
pub struct ImplPropSetter {
pub set: keywords::set,
pub paren: token::Paren,
pub amp: Token![&],
pub self_: Token![self],
pub comma: Token![,],
pub param: Ident,
pub colon: Token![:],
pub t: keywords::T,
pub block: Block,
}
// default() -> T { }
pub struct ImplPropDefault {
pub default: Token![default],
pub paren: token::Paren,
pub rarrow: Token![->],
pub self_: keywords::T,
pub block: Block,
}
......
......@@ -18,26 +18,18 @@ fn check_class_items(_class: &Class) -> Result<()> {
pub mod tests {
use super::*;
use proc_macro::TokenStream;
use syn::buffer::TokenBuffer;
use syn::synom::Synom;
use ast;
pub fn run() {
checks_empty_class();
pub fn run() -> Result<()> {
checks_empty_class()
}
fn checks_empty_class() {
fn checks_empty_class() -> Result<()> {
let raw = "class Foo {}";
let token_stream = raw.parse::<TokenStream>().unwrap();
let program : ast::Program = ::syn::parse_str(raw)?;
let buffer = TokenBuffer::new(token_stream);
let cursor = buffer.begin();
let program = ast::Program::parse(cursor).unwrap().0;
assert!(check_program(&program).is_ok());
check_program(&program)
}
}
use syn::synom::ParseError;
error_chain! {
// The type defined for this error. These are the conventional
// and recommended names, but they can be arbitrarily chosen.
//
// It is also possible to leave this section out entirely, or
// leave it empty, and these names will be used automatically.
types {
Error, ErrorKind, ResultExt, Result;
}
foreign_links {
Io(::std::io::Error) #[cfg(unix)];
Parse(ParseError);
GIR(::gen::gir::Error);
}
errors {
}
}
pub use syn::parse::{Error, Result};
......@@ -8,14 +8,9 @@ use hir::{Program, Class, Slot, Method, Ty, FnArg};
use glib_utils::*;
use gen::names::Names;
use xml::writer::Error as EmitterError;
use errors::*;
error_chain! {
foreign_links {
Io(::std::io::Error) #[cfg(unix)];
GIR(EmitterError);
}
}
use xml::writer::{Result as EmitterResult};
pub fn generate(program: &Program) -> Result<()> {
for class in program.classes.iter() {
......@@ -27,81 +22,88 @@ pub fn generate(program: &Program) -> Result<()> {
pub fn generate_gir(class: &Class) -> Result<()> {
if let Some(ref s) = class.gir {
let mut f = File::create(&s.value())?;
let mut w = EmitterConfig::new()
.perform_indent(true)
.create_writer(&mut f);
let name = class.name.to_string();
let lower_name = lower_case_instance_name(&name).to_string();
let names = Names::new(&class.name, "Class");
// TODO: Get the version and shared-library from Cargo.toml?
let version = "1.0.0";
let slib = format!("lib{}.so", lower_name);
// TODO: Find the parent namespace and class
let parent_name = "GObject.Object";
// TODO: include deps like GLib, etc
// the dep should be `("GLib", "2.0")` lib and version
let deps: Vec<(&str, &str)> = vec![];
w.write(XmlEvent::start_element("repository")
.attr("version", "1.2")
.attr("xmlns", "http://www.gtk.org/introspection/core/1.0")
.attr("xmlns:c", "http://www.gtk.org/introspection/c/1.0")
.attr("xmlns:glib", "http://www.gtk.org/introspection/glib/1.0"))?;
// <include name="GLib" version="2.0"/>
for (name, version) in deps {
w.write(XmlEvent::start_element("include")
.attr("name", name)
.attr("version", version)
)?;
w.write(XmlEvent::end_element())?;
}
let mut f = File::create(&s.value()).map_err(|err| Error::new(s.span(), err.to_string()))?;;
w.write(XmlEvent::start_element("namespace")
.attr("name", &name)
.attr("c:identifier-prefixes", &name)
.attr("c:symbol-prefixes", &lower_name)
.attr("version", &version)
.attr("shared-library", &slib)
)?;
generate_gir_xml(class, &mut f).map_err(|err| Error::new(s.span(), err.to_string()))?;
}
w.write(XmlEvent::start_element("class")
.attr("name", &name)
.attr("c:type", &name)
.attr("glib:type-name", &name)
.attr("c:symbol-prefix", &lower_name)
.attr("glib:type-struct", &names.vtable().to_string())
.attr("glib:get-type", &names.get_type_fn().to_string())
.attr("parent", &parent_name)
)?;
Ok(())
}
gen_constructor_xml(&mut w, &names)?;
fn generate_gir_xml(class: &Class, f: &mut File) -> EmitterResult<()>
{
let mut w = EmitterConfig::new()
.perform_indent(true)
.create_writer(f);
let name = class.name.to_string();
let lower_name = lower_case_instance_name(&name).to_string();
let names = Names::new(&class.name, "Class");
// TODO: Get the version and shared-library from Cargo.toml?
let version = "1.0.0";
let slib = format!("lib{}.so", lower_name);
// TODO: Find the parent namespace and class
let parent_name = "GObject.Object";
// TODO: include deps like GLib, etc
// the dep should be `("GLib", "2.0")` lib and version
let deps: Vec<(&str, &str)> = vec![];
w.write(XmlEvent::start_element("repository")
.attr("version", "1.2")
.attr("xmlns", "http://www.gtk.org/introspection/core/1.0")
.attr("xmlns:c", "http://www.gtk.org/introspection/c/1.0")
.attr("xmlns:glib", "http://www.gtk.org/introspection/glib/1.0"))?;
// <include name="GLib" version="2.0"/>
for (name, version) in deps {
w.write(XmlEvent::start_element("include")
.attr("name", name)
.attr("version", version)
)?;
w.write(XmlEvent::end_element())?;
}
for slot in class.slots.iter() {
gen_slot_xml(&mut w, &names, slot)?;
}
w.write(XmlEvent::start_element("namespace")
.attr("name", &name)
.attr("c:identifier-prefixes", &name)
.attr("c:symbol-prefixes", &lower_name)
.attr("version", &version)
.attr("shared-library", &slib)
)?;
// closing class
w.write(XmlEvent::end_element())?;
w.write(XmlEvent::start_element("class")
.attr("name", &name)
.attr("c:type", &name)
.attr("glib:type-name", &name)
.attr("c:symbol-prefix", &lower_name)
.attr("glib:type-struct", &names.vtable().to_string())
.attr("glib:get-type", &names.get_type_fn().to_string())
.attr("parent", &parent_name)
)?;
gen_record_xml(&mut w, &names)?;
gen_constructor_xml(&mut w, &names)?;
// closing namespace
w.write(XmlEvent::end_element())?;
// closing repository
w.write(XmlEvent::end_element())?;
for slot in class.slots.iter() {
gen_slot_xml(&mut w, &names, slot)?;
}
// closing class
w.write(XmlEvent::end_element())?;
gen_record_xml(&mut w, &names)?;
// closing namespace
w.write(XmlEvent::end_element())?;
// closing repository
w.write(XmlEvent::end_element())?;
Ok(())
}
fn gen_slot_xml(w: &mut EventWriter<&mut File>, names: &Names, slot: &Slot) -> Result<()> {
fn gen_slot_xml(w: &mut EventWriter<&mut File>, names: &Names, slot: &Slot) -> EmitterResult<()> {
// <method name="add" c:identifier="counter_add">
// <return-value transfer-ownership="none">
// <type name="gint" c:type="gint"/>
......@@ -171,7 +173,7 @@ fn gen_slot_xml(w: &mut EventWriter<&mut File>, names: &Names, slot: &Slot) -> R
Ok(())
}
fn gen_constructor_xml(w: &mut EventWriter<&mut File>, names: &Names) -> Result<()> {
fn gen_constructor_xml(w: &mut EventWriter<&mut File>, names: &Names) -> EmitterResult<()> {
// <constructor name="new" c:identifier="counter_new">
// <return-value transfer-ownership="full">
// <type name="Counter" c:type="Counter*"/>
......@@ -209,7 +211,7 @@ fn type_to_ctype(type_: &Ty) -> String {
}
}
fn gen_record_xml(w: &mut EventWriter<&mut File>, names: &Names) -> Result<()> {
fn gen_record_xml(w: &mut EventWriter<&mut File>, names: &Names) -> EmitterResult<()> {
// <record name="CounterClass"
// c:type="CounterClass"
// disguised="1"
......
use proc_macro::{Diagnostic, Level};
use proc_macro2::{Delimiter, Group, Span, TokenStream, TokenTree};
use quote::{ToTokens, TokenStreamExt};
use syn::spanned::Spanned;
use hir::{FnArg, FnSig, Ty};
use errors::*;
use syn::spanned::Spanned;
impl<'ast> FnSig<'ast> {
/// Generates the Glib type name of the function's return value
......
......@@ -11,6 +11,7 @@ use std::collections::HashMap;
use proc_macro2::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
use quote::{ToTokens, TokenStreamExt};
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{self, parse_str, Block, Field, Path, ReturnType, Type, LitStr};
use super::ast;
......@@ -176,12 +177,37 @@ impl<'ast> Classes<'ast> {
self.items.iter().find(|c| c.1.name == name).unwrap().1
}
fn extract_gir_from_attribute(class: &'ast ast::Class) -> Result<Option<LitStr>>
{
let mut res = None;
for attr in class.attrs.iter() {
if let Some(syn::Meta::List(metalist)) = attr.interpret_meta() {
if metalist.ident == "generate_gir" {
if metalist.nested.len() != 1 {
return Err(Error::new(metalist.ident.span(), "Only one generate_gir allowed"));
}
match metalist.nested.into_iter().next().expect("Length checked above") {
syn::NestedMeta::Literal(syn::Lit::Str(litstr)) => {
if res.is_some() {
return Err(Error::new(metalist.ident.span(), "multiple generate_gir attributes found"));
}
res = Some(litstr);
},
_ => { return Err(Error::new(metalist.ident.span(), "generate_gir expects one string literal"));}
}
}
}
}
Ok(res)
}
fn add(&mut self, ast_class: &'ast ast::Class) -> Result<()> {
let gir = Self::extract_gir_from_attribute(ast_class)?;
let prev = self.items.insert(
ast_class.name.clone(),
Class {
name: ast_class.name.clone(),
gir: ast_class.gir.clone(),
gir,
gobject_parent: ast_class.extends.is_none(),
parent: tokens_ParentInstance(ast_class),
parent_ffi: tokens_ParentInstanceFfi(ast_class),
......@@ -194,7 +220,7 @@ impl<'ast> Classes<'ast> {
},
);
if prev.is_some() {
bail!("redefinition of class `{}`", ast_class.name);
return Err(Error::new(ast_class.name.span(), format!("redefinition of class `{}`", ast_class.name)));
}
Ok(())
}
......@@ -202,7 +228,7 @@ impl<'ast> Classes<'ast> {
fn add_impl(&mut self, impl_: &'ast ast::Impl) -> Result<()> {
let class = match self.items.get_mut(&impl_.self_path) {
Some(class) => class,
None => bail!("impl for class that doesn't exist: {}", impl_.self_path),
None => { Err(Error::new(impl_.self_path.span(), format!("impl for class that doesn't exist: {}", impl_.self_path)))? },
};
match *impl_ {
ast::Impl {
......@@ -213,21 +239,21 @@ impl<'ast> Classes<'ast> {
for item in impl_.items.iter() {
let item = match item.node {
ast::ImplItemKind::Method(ref m) => m,
ast::ImplItemKind::ReserveSlots(_) => {
bail!("can't reserve slots in a parent class impl");
ast::ImplItemKind::ReserveSlots(ref l) => {
return Err(Error::new(l.span(), "can't reserve slots in a parent class impl"));
}
ast::ImplItemKind::Prop(_) => {
bail!("can't define props in a parent class impl");
ast::ImplItemKind::Prop(ast::ImplProp { ref name, ..}) => {
return Err(Error::new(name.span(), "can't define props in a parent class impl"));
}
};
if item.signal.is_some() {
bail!("can't implement signals for parent classes")
if let Some(spanned) = &item.signal {
return Err(Error::new(spanned.span(), "can't implement signals for parent classes"));
}
if !item.virtual_.is_some() {
bail!("can only implement virtual functions for parent classes")
return Err(Error::new(item.name.span(), "can only implement virtual functions for parent classes"));
}
if item.public.is_some() {
bail!("overrides are always public, no `pub` needed")
if let Some(spanned) = &item.public {
return Err(Error::new(spanned.span(), "overrides are always public, no `pub` needed"));
}
let method = match Slot::translate_from_method(item)? {
Slot::VirtualMethod(VirtualMethod {
......@@ -239,7 +265,7 @@ impl<'ast> Classes<'ast> {
body,
},
Slot::VirtualMethod(VirtualMethod { .. }) => {
bail!("overrides must provide a body for virtual methods");
return Err(Error::new(item.name.span(), "overrides must provide a body for virtual methods"));
}
_ => unreachable!(),
};
......@@ -301,37 +327,42 @@ impl<'ast> Slot<'ast> {
fn translate_from_method(method: &'ast ast::ImplItemMethod) -> Result<Slot<'ast>> {
if method.signal.is_some() {
if method.public.is_some() {
bail!(
"function `{}` is a signal so it doesn't need to be public",
method.name
)
if let Some(spanned) = &method.public {
return Err(Error::new(spanned.span(),
format!("function `{}` is a signal so it doesn't need to be public",
method.name)));
}
if method.virtual_.is_some() {
bail!(
"function `{}` is a signal so it doesn't need to be virtual",
method.name
)
if let Some(spanned) = &method.virtual_ {
return Err(Error::new(spanned.span(),
format!("function `{}` is a signal so it doesn't need to be virtual",
method.name)));
}
let sig = extract_sig(method)?;
let body = match method.body {
ast::ImplItemMethodBlock::Block(ref block) => Some(block),
_ => None
};
Ok(Slot::Signal(Signal {
// FIXME: signal flags
sig,
body: method.body.as_ref(),
body,
}))
} else if method.virtual_.is_some() {
if method.public.is_some() {
bail!(
"function `{}` is virtual so it doesn't need to be public",
method.name
)
if let Some(spanned) = &method.public {
return Err(Error::new(spanned.span(),
format!("function `{}` is virtual so it doesn't need to be public",
method.name)));
}
let sig = extract_sig(method)?;
let body = match method.body {
ast::ImplItemMethodBlock::Block(ref block) => Some(block),
_ => None
};
Ok(Slot::VirtualMethod(VirtualMethod {
sig,
body: method.body.as_ref(),
body,
}))
} else {
let sig = extract_sig(method)?;
......@@ -341,7 +372,7 @@ impl<'ast> Slot<'ast> {
body: method
.body
.as_ref()
.ok_or_else(|| format!("function `{}` requires a body", method.name))?,
.ok_or_else(|| Error::new(method.name.span(), format!("function `{}` requires a body", method.name)))?,
}))
}
}
......@@ -375,7 +406,7 @@ fn extract_inputs<'ast>(punc: &'ast Punctuated<syn::FnArg, Token!(,)>) -> Result
ref ident,
subpat: None,
}) => (ident, m),
_ => bail!("only bare identifiers are allowed as argument patterns"),
_ => { return Err(Error::new(pat.span(), "only bare identifiers are allowed as argument patterns")); },
};
Ok(FnArg::Arg {
......@@ -393,25 +424,25 @@ fn extract_inputs<'ast>(punc: &'ast Punctuated<syn::FnArg, Token!(,)>) -> Result
syn::FnArg::SelfRef(syn::ArgSelfRef {
mutability: Some(..),
..
}) => bail!("&mut self not implemented yet"),
}) => { return Err(Error::new(arg.span(), "&mut self not implemented yet")); } ,
syn::FnArg::SelfRef(syn::ArgSelfRef {
lifetime: Some(..), ..
}) => bail!("lifetime arguments on self not implemented yet"),
syn::FnArg::SelfValue(_) => bail!("by-value self not implemented"),
syn::FnArg::Inferred(_) => bail!("cannot have inferred function arguments"),
syn::FnArg::Ignored(_) => bail!("cannot have ignored function arguments"),
}) => { return Err(Error::new(arg.span(), "lifetime arguments on self not implemented yet")); },
syn::FnArg::SelfValue(_) => { return Err(Error::new(arg.span(), "by-value self not implemented")); },
syn::FnArg::Inferred(_) => { return Err(Error::new(arg.span(), "cannot have inferred function arguments")); },
syn::FnArg::Ignored(_) => { return Err(Error::new(arg.span(), "cannot have ignored function arguments")); },
}).collect()
}
impl<'ast> Ty<'ast> {
pub fn extract_from_type(t: &'ast syn::Type) -> Result<Ty<'ast>> {
match *t {
syn::Type::Slice(_) => bail!("slice types not implemented yet"),
syn::Type::Array(_) => bail!("array types not implemented yet"),
syn::Type::Ptr(_) => bail!("ptr types not implemented yet"),
syn::Type::Slice(_) => { return Err(Error::new(t.span(), "slice types not implemented yet")); },
syn::Type::Array(_) => { return Err(Error::new(t.span(), "array types not implemented yet")); },
syn::Type::Ptr(_) => { return Err(Error::new(t.span(), "ptr types not implemented yet")); },
syn::Type::Reference(syn::TypeReference {
lifetime: Some(_), ..
}) => bail!("borrowed types with lifetimes not implemented yet"),
}) => { return Err(Error::new(t.span(), "borrowed types with lifetimes not implemented yet")); },
syn::Type::Reference(syn::TypeReference {
lifetime: None,
ref elem,
......@@ -419,41 +450,41 @@ impl<'ast> Ty<'ast> {
..
}) => {
if mutability.is_some() {
bail!("mutable borrowed pointers not implemented");
return Err(Error::new(t.span(), "mutable borrowed pointers not implemented"));
}
let path = match **elem {
syn::Type::Path(syn::TypePath {
qself: None,
ref path,
}) => path,
_ => bail!("only borrowed pointers to paths supported"),
_ => { return Err(Error::new(t.span(), "only borrowed pointers to paths supported")); },
};
let ty = Ty::extract_from_path(path)?;
Ok(Ty::Borrowed(Box::new(ty)))
}
syn::Type::BareFn(_) => bail!("function pointer types not implemented yet"),
syn::Type::Never(_) => bail!("never not implemented yet"),
syn::Type::BareFn(_) => Err(Error::new(t.span(), "function pointer types not implemented yet")),
syn::Type::Never(_) => Err(Error::new(t.span(), "never not implemented yet")),
syn::Type::Tuple(syn::TypeTuple { ref elems, .. }) => {
if elems.is_empty() {
Ok(Ty::Unit)
} else {
bail!("tuple types not implemented yet")
Err(Error::new(t.span(), "tuple types not implemented yet"))
}
}
syn::Type::Path(syn::TypePath { qself: Some(_), .. }) => {
bail!("path types with qualified self (`as` syntax) not allowed")
Err(Error::new(t.span(), "path types with qualified self (`as` syntax) not allowed"))
}
syn::Type::Path(syn::TypePath {
qself: None,
ref path,
}) => Ty::extract_from_path(path),
syn::Type::TraitObject(_) => bail!("trait objects not implemented yet"),
syn::Type::ImplTrait(_) => bail!("trait objects not implemented yet"),
syn::Type::TraitObject(_) => Err(Error::new(t.span(), "trait objects not implemented yet")),
syn::Type::ImplTrait(_) => Err(Error::new(t.span(), "trait objects not implemented yet")),
syn::Type::Paren(syn::TypeParen { ref elem, .. }) => Ty::extract_from_type(elem),
syn::Type::Group(syn::TypeGroup { ref elem, .. }) => Ty::extract_from_type(elem),
syn::Type::Infer(_) => bail!("underscore types not allowed"),
syn::Type::Macro(_) => bail!("type macros not allowed"),
syn::Type::Verbatim(_) => bail!("type macros not allowed"),
syn::Type::Infer(_) => Err(Error::new(t.span(), "underscore types not allowed")),
syn::Type::Macro(_) => Err(Error::new(t.span(), "type macros not allowed")),
syn::Type::Verbatim(_) => Err(Error::new(t.span(), "type macros not allowed")),
}
}
......@@ -462,7 +493,7 @@ impl<'ast> Ty<'ast> {
syn::PathArguments::None => false,
_ => true,
}) {
bail!("type or lifetime parameters not allowed")
return Err(Error::new(t.span(), "type or lifetime parameters not allowed"));
}
if t.leading_colon.is_some() || t.segments.len() > 1 {
return Ok(Ty::Owned(t));
......@@ -609,8 +640,8 @@ impl<'ast> Property<'ast> {
let getter = match prop.getter() {
Some(&ast::ImplPropBlock::Getter(ref b)) => b,
None => bail!("property without getter: {}", name),
_ => bail!("invalid property getter: {}", name),
None => { return Err(Error::new(prop.name.span(), format!("property without getter: {}", name))); },
_ => { return Err(Error::new(prop.name.span(), format!("invalid property getter: {}", name))); },
};
let setter = match prop.setter() {
......@@ -618,19 +649,19 @@ impl<'ast> Property<'ast> {