Skip to content

Add Neon error codes #1110

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ Neon!
See our [Neon fundamentals docs](https://neon-bindings.com/docs/intro) and
our [API docs](https://docs.rs/neon/latest/neon).

Compiler diagnostics may include error codes like `N0001`. Refer to
[the error code reference](doc/error-codes.md) for details.

## Neon 1.0.0 Migration Guide

The latest version of Neon, 1.0.0, includes several breaking changes in order to fix unsoundness, improve consistency, and add features.
Expand Down
55 changes: 55 additions & 0 deletions crates/neon-macros/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#[derive(Copy, Clone)]
pub(crate) enum ErrorCode {
UnsupportedExportItem,
WrongContextChannelRef,
ContextMustBeMut,
WrongContextChannel,
MustBeMutRef,
ChannelByRef,
ChannelContextRef,
ContextNotAvailable,
MissingContext,
SelfReceiver,
UnsupportedProperty,
AsyncAttrAsyncFn,
}

impl ErrorCode {
pub(crate) fn code(self) -> &'static str {
match self {
ErrorCode::UnsupportedExportItem => "N0001",
ErrorCode::WrongContextChannelRef => "N0002",
ErrorCode::ContextMustBeMut => "N0003",
ErrorCode::WrongContextChannel => "N0004",
ErrorCode::MustBeMutRef => "N0005",
ErrorCode::ChannelByRef => "N0006",
ErrorCode::ChannelContextRef => "N0007",
ErrorCode::ContextNotAvailable => "N0008",
ErrorCode::MissingContext => "N0009",
ErrorCode::SelfReceiver => "N0010",
ErrorCode::UnsupportedProperty => "N0011",
ErrorCode::AsyncAttrAsyncFn => "N0012",
}
}

pub(crate) fn message(self) -> &'static str {
match self {
ErrorCode::UnsupportedExportItem => "`neon::export` can only be applied to functions, consts, and statics.",
ErrorCode::WrongContextChannelRef => "Expected `&mut Cx` instead of a `Channel` reference.",
ErrorCode::ContextMustBeMut => "Context must be a `&mut` reference.",
ErrorCode::WrongContextChannel => "Expected `&mut Cx` instead of `Channel`.",
ErrorCode::MustBeMutRef => "Must be a `&mut` reference.",
ErrorCode::ChannelByRef => "Expected an owned `Channel` instead of a reference.",
ErrorCode::ChannelContextRef => "Expected an owned `Channel` instead of a context reference.",
ErrorCode::ContextNotAvailable => "Context is not available in async functions. Try a `Channel` instead.",
ErrorCode::MissingContext => "Expected a context argument. Try removing the `context` attribute.",
ErrorCode::SelfReceiver => "Exported functions cannot receive `self`.",
ErrorCode::UnsupportedProperty => "unsupported property",
ErrorCode::AsyncAttrAsyncFn => "`async` attribute should not be used with an `async fn`",
}
}
}

pub(crate) fn error(span: proc_macro2::Span, code: ErrorCode) -> syn::Error {
syn::Error::new(span, format!("{} [{}]", code.message(), code.code()))
}
18 changes: 16 additions & 2 deletions crates/neon-macros/src/export/function/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ pub(super) enum Kind {
Task,
}

use crate::error::ErrorCode;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All use statements should be placed in the top of the file.


impl Meta {
fn set_name(&mut self, meta: syn::meta::ParseNestedMeta) -> syn::Result<()> {
self.name = Some(meta.value()?.parse::<syn::LitStr>()?);
Expand Down Expand Up @@ -43,7 +45,13 @@ impl Meta {

fn make_async(&mut self, meta: syn::meta::ParseNestedMeta) -> syn::Result<()> {
if matches!(self.kind, Kind::AsyncFn) {
return Err(meta.error("`async` attribute should not be used with an `async fn`"));
return Err(meta.error(
format!(
"{} [{}]",
ErrorCode::AsyncAttrAsyncFn.message(),
ErrorCode::AsyncAttrAsyncFn.code()
),
));
}

self.kind = Kind::Async;
Expand Down Expand Up @@ -102,7 +110,13 @@ impl syn::parse::Parser for Parser {
return attr.make_task(meta);
}

Err(meta.error("unsupported property"))
Err(meta.error(
format!(
"{} [{}]",
ErrorCode::UnsupportedProperty.message(),
ErrorCode::UnsupportedProperty.code()
),
))
});

parser.parse2(tokens)?;
Expand Down
40 changes: 15 additions & 25 deletions crates/neon-macros/src/export/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ fn context_parse(
// * Context argument must be a `&mut` reference
// * First argument must not be `Channel`
// * Must not be a `self` receiver
use crate::error::{self, ErrorCode};
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please place all use statements at the top of their files.


fn check_context(opts: &meta::Meta, sig: &syn::Signature) -> syn::Result<bool> {
// Extract the first argument
let ty = match first_arg(opts, sig)? {
Expand All @@ -200,28 +202,19 @@ fn check_context(opts: &meta::Meta, sig: &syn::Signature) -> syn::Result<bool> {
let ty = match &*ty.ty {
// Tried to use a borrowed Channel
syn::Type::Reference(ty) if !opts.context && is_channel_type(&ty.elem) => {
return Err(syn::Error::new(
ty.elem.span(),
"Expected `&mut Cx` instead of a `Channel` reference.",
))
return Err(error::error(ty.elem.span(), ErrorCode::WrongContextChannelRef))
}

syn::Type::Reference(ty) => ty,

// Context needs to be a reference
_ if opts.context || is_context_type(&ty.ty) => {
return Err(syn::Error::new(
ty.ty.span(),
"Context must be a `&mut` reference.",
))
return Err(error::error(ty.ty.span(), ErrorCode::ContextMustBeMut))
}

// Hint that `Channel` should be swapped for `&mut Cx`
_ if is_channel_type(&ty.ty) => {
return Err(syn::Error::new(
ty.ty.span(),
"Expected `&mut Cx` instead of `Channel`.",
))
return Err(error::error(ty.ty.span(), ErrorCode::WrongContextChannel))
}

_ => return Ok(false),
Expand All @@ -234,7 +227,7 @@ fn check_context(opts: &meta::Meta, sig: &syn::Signature) -> syn::Result<bool> {

// Context argument must be mutable
if ty.mutability.is_none() {
return Err(syn::Error::new(ty.span(), "Must be a `&mut` reference."));
return Err(error::error(ty.span(), ErrorCode::MustBeMutRef));
}

// All tests passed!
Expand All @@ -258,25 +251,22 @@ fn check_channel(opts: &meta::Meta, sig: &syn::Signature) -> syn::Result<bool> {
match &*ty.ty {
// Provided `&mut Channel` instead of `Channel`
syn::Type::Reference(ty) if opts.context || is_channel_type(&ty.elem) => {
Err(syn::Error::new(
ty.span(),
"Expected an owned `Channel` instead of a reference.",
))
Err(error::error(ty.span(), ErrorCode::ChannelByRef))
}

// Provided a `&mut Cx` instead of a `Channel`
syn::Type::Reference(ty) if is_context_type(&ty.elem) => Err(syn::Error::new(
syn::Type::Reference(ty) if is_context_type(&ty.elem) => Err(error::error(
ty.elem.span(),
"Expected an owned `Channel` instead of a context reference.",
ErrorCode::ChannelContextRef,
)),

// Found a `Channel`
_ if opts.context || is_channel_type(&ty.ty) => Ok(true),

// Tried to use an owned `Cx`
_ if is_context_type(&ty.ty) => Err(syn::Error::new(
_ if is_context_type(&ty.ty) => Err(error::error(
ty.ty.span(),
"Context is not available in async functions. Try a `Channel` instead.",
ErrorCode::ContextNotAvailable,
)),

_ => Ok(false),
Expand All @@ -294,9 +284,9 @@ fn first_arg<'a>(

// If context was forced, error to let the user know the mistake
None if opts.context => {
return Err(syn::Error::new(
return Err(error::error(
sig.inputs.span(),
"Expected a context argument. Try removing the `context` attribute.",
ErrorCode::MissingContext,
))
}

Expand All @@ -306,9 +296,9 @@ fn first_arg<'a>(
// Expect a typed pattern; self receivers are not supported
match arg {
syn::FnArg::Typed(ty) => Ok(Some(ty)),
syn::FnArg::Receiver(arg) => Err(syn::Error::new(
syn::FnArg::Receiver(arg) => Err(error::error(
arg.span(),
"Exported functions cannot receive `self`.",
ErrorCode::SelfReceiver,
)),
}
}
Expand Down
9 changes: 8 additions & 1 deletion crates/neon-macros/src/export/global/meta.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub(crate) struct Meta {
}

pub(crate) struct Parser;
use crate::error::ErrorCode;

impl syn::parse::Parser for Parser {
type Output = Meta;
Expand All @@ -24,7 +25,13 @@ impl syn::parse::Parser for Parser {
return Ok(());
}

Err(meta.error("unsupported property"))
Err(meta.error(
format!(
"{} [{}]",
ErrorCode::UnsupportedProperty.message(),
ErrorCode::UnsupportedProperty.code()
),
))
});

parser.parse2(tokens)?;
Expand Down
4 changes: 2 additions & 2 deletions crates/neon-macros/src/export/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod function;
mod global;
use crate::error::{self, ErrorCode};

// N.B.: Meta attribute parsing happens in this function because `syn::parse_macro_input!`
// must be called from a function that returns `proc_macro::TokenStream`.
Expand Down Expand Up @@ -45,8 +46,7 @@ pub(crate) fn export(
// Generate an error for unsupported item types
fn unsupported(item: syn::Item) -> proc_macro::TokenStream {
let span = syn::spanned::Spanned::span(&item);
let msg = "`neon::export` can only be applied to functions, consts, and statics.";
let err = syn::Error::new(span, msg);
let err = error::error(span, ErrorCode::UnsupportedExportItem);

err.into_compile_error().into()
}
1 change: 1 addition & 0 deletions crates/neon-macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Procedural macros supporting [Neon](https://docs.rs/neon/latest/neon/)

mod export;
mod error;

#[proc_macro_attribute]
pub fn main(
Expand Down
18 changes: 18 additions & 0 deletions doc/error-codes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Neon Error Codes

Neon compiler errors include a short code in brackets. The table below lists each code and the associated message.

| Code | Message |
|-------|---------|
| N0001 | `neon::export` can only be applied to functions, consts, and statics. |
| N0002 | Expected `&mut Cx` instead of a `Channel` reference. |
| N0003 | Context must be a `&mut` reference. |
| N0004 | Expected `&mut Cx` instead of `Channel`. |
| N0005 | Must be a `&mut` reference. |
| N0006 | Expected an owned `Channel` instead of a reference. |
| N0007 | Expected an owned `Channel` instead of a context reference. |
| N0008 | Context is not available in async functions. Try a `Channel` instead. |
| N0009 | Expected a context argument. Try removing the `context` attribute. |
| N0010 | Exported functions cannot receive `self`. |
| N0011 | Unsupported property. |
| N0012 | `async` attribute should not be used with an `async fn`. |
2 changes: 1 addition & 1 deletion test/ui/tests/fail/missing-context.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: Expected a context argument. Try removing the `context` attribute.
error: Expected a context argument. Try removing the `context` attribute. [N0009]
--> tests/fail/missing-context.rs:1:1
|
1 | #[neon::export(context)]
Expand Down
12 changes: 6 additions & 6 deletions test/ui/tests/fail/need-borrowed-context.stderr
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
error: Context must be a `&mut` reference.
error: Context must be a `&mut` reference. [N0003]
--> tests/fail/need-borrowed-context.rs:2:18
|
2 | fn owned_cx(_cx: Cx) {}
| ^^

error: Context must be a `&mut` reference.
error: Context must be a `&mut` reference. [N0003]
--> tests/fail/need-borrowed-context.rs:5:27
|
5 | fn owned_function_cx(_cx: FunctionContext) {}
| ^^^^^^^^^^^^^^^

error: Must be a `&mut` reference.
error: Must be a `&mut` reference. [N0005]
--> tests/fail/need-borrowed-context.rs:8:16
|
8 | fn ref_cx(_cx: &Cx) {}
| ^

error: Must be a `&mut` reference.
error: Must be a `&mut` reference. [N0005]
--> tests/fail/need-borrowed-context.rs:11:25
|
11 | fn ref_function_cx(_cx: &FunctionContext) {}
| ^

error: Context must be a `&mut` reference.
error: Context must be a `&mut` reference. [N0003]
--> tests/fail/need-borrowed-context.rs:14:19
|
14 | fn forced_cx(_cx: String) {}
| ^^^^^^

error: Must be a `&mut` reference.
error: Must be a `&mut` reference. [N0005]
--> tests/fail/need-borrowed-context.rs:17:23
|
17 | fn forced_ref_cx(_cx: &String) {}
Expand Down
6 changes: 3 additions & 3 deletions test/ui/tests/fail/unexpected-self.stderr
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
error: Exported functions cannot receive `self`.
error: Exported functions cannot receive `self`. [N0010]
--> tests/fail/unexpected-self.rs:5:15
|
5 | fn borrow(&self) {}
| ^

error: Exported functions cannot receive `self`.
error: Exported functions cannot receive `self`. [N0010]
--> tests/fail/unexpected-self.rs:8:19
|
8 | fn borrow_mut(&mut self) {}
| ^

error: Exported functions cannot receive `self`.
error: Exported functions cannot receive `self`. [N0010]
--> tests/fail/unexpected-self.rs:11:14
|
11 | fn owned(self) {}
Expand Down
2 changes: 1 addition & 1 deletion test/ui/tests/fail/unnecessary-attribute.stderr
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
error: `async` attribute should not be used with an `async fn`
error: `async` attribute should not be used with an `async fn` [N0012]
--> tests/fail/unnecessary-attribute.rs:1:16
|
1 | #[neon::export(async)]
Expand Down
4 changes: 2 additions & 2 deletions test/ui/tests/fail/unsupported-property.stderr
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
error: unsupported property
error: unsupported property [N0011]
--> tests/fail/unsupported-property.rs:1:16
|
1 | #[neon::export(foo)]
| ^^^

error: unsupported property
error: unsupported property [N0011]
--> tests/fail/unsupported-property.rs:4:16
|
4 | #[neon::export(foo)]
Expand Down
Loading
Loading