decl_runtime_apis! - check that a method without changed_in exists (#5635)

* `decl_runtime_apis!` - check that a method without `changed_in` exists

This adds another check to the macro that ensures that not only methods
with `changed_in` exists, but there are also the default methods exist.

* Update primitives/api/proc-macro/src/decl_runtime_apis.rs

Co-Authored-By: Nikolay Volf <nikvolf@gmail.com>

* Fix test

Co-authored-by: Nikolay Volf <nikvolf@gmail.com>
This commit is contained in:
Bastian Köcher
2020-04-15 00:02:14 +02:00
committed by GitHub
parent 51f9bb3c0e
commit b267b1728f
4 changed files with 76 additions and 2 deletions
@@ -876,6 +876,53 @@ struct CheckTraitDecl {
errors: Vec<Error>,
}
impl CheckTraitDecl {
/// Check the given trait.
///
/// All errors will be collected in `self.errors`.
fn check(&mut self, trait_: &ItemTrait) {
self.check_method_declarations(trait_.items.iter().filter_map(|i| match i {
TraitItem::Method(method) => Some(method),
_ => None,
}));
visit::visit_item_trait(self, trait_);
}
/// Check that the given method declarations are correct.
///
/// Any error is stored in `self.errors`.
fn check_method_declarations<'a>(&mut self, methods: impl Iterator<Item = &'a TraitItemMethod>) {
let mut method_to_signature_changed = HashMap::<Ident, Vec<Option<u64>>>::new();
methods.into_iter().for_each(|method| {
let attributes = remove_supported_attributes(&mut method.attrs.clone());
let changed_in = match get_changed_in(&attributes) {
Ok(r) => r,
Err(e) => { self.errors.push(e); return; },
};
method_to_signature_changed
.entry(method.sig.ident.clone())
.or_default()
.push(changed_in);
});
method_to_signature_changed.into_iter().for_each(|(f, changed)| {
// If `changed_in` is `None`, it means it is the current "default" method that calls
// into the latest implementation.
if changed.iter().filter(|c| c.is_none()).count() == 0 {
self.errors.push(Error::new(
f.span(),
"There is no 'default' method with this name (without `changed_in` attribute).\n\
The 'default' method is used to call into the latest implementation.",
));
}
});
}
}
impl<'ast> Visit<'ast> for CheckTraitDecl {
fn visit_fn_arg(&mut self, input: &'ast FnArg) {
if let FnArg::Receiver(_) = input {
@@ -923,7 +970,7 @@ impl<'ast> Visit<'ast> for CheckTraitDecl {
/// Check that the trait declarations are in the format we expect.
fn check_trait_decls(decls: &[ItemTrait]) -> Result<()> {
let mut checker = CheckTraitDecl { errors: Vec::new() };
decls.iter().for_each(|decl| visit::visit_item_trait(&mut checker, &decl));
decls.iter().for_each(|decl| checker.check(decl));
if let Some(err) = checker.errors.pop() {
Err(checker.errors.into_iter().fold(err, |mut err, other| {