Skip to content

Commit

Permalink
Add compile time error contrib docs
Browse files Browse the repository at this point in the history
This commit adds contributor documentation explaining how to add new
compile time errors.
  • Loading branch information
chinedufn committed Jan 23, 2023
1 parent 36c90e7 commit e0d3d62
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 3 deletions.
1 change: 1 addition & 0 deletions book/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@
- [Internal Design](./contributing/internal-design/README.md)
- [Code Generation](./contributing/internal-design/codegen/README.md)
- [Adding support for a signature](./contributing/adding-support-for-a-signature/README.md)
- [Adding compile time errors](./contributing/adding-compile-time-errors/README.md)
110 changes: 110 additions & 0 deletions book/src/contributing/adding-compile-time-errors/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
# Adding compile time errors

When users write bridge modules that will not compile we want to emit compile time errors that will
guide them towards the right fix.

For example, if a user wrote the following bridge module:

```rust
{{#include ../../../../crates/swift-bridge-macro/tests/ui/unrecognized-opaque-type-attribute.rs:mdbook-ui-test-example}}
```

We would want to emit a compile time error along the lines of:

```sh
{{#include ../../../../crates/swift-bridge-macro/tests/ui/unrecognized-opaque-type-attribute.stderr}}
```

This chapter shows how to add support for compile time errors.

## Implementing Support for a Compile Time Error

To support a new compile time error we first write an automated UI test for the error case.

These tests live in [`crates/swift-bridge-macro/tests/ui`][ui-tests] and are powered by the [trybuild] crate.

After adding our UI test, we create a new `ParseError` variant that can be used to describe the error.

Here are a few example parse errors:

```rust
// via: crates/swift-bridge-ir/src/errors/parse_error.rs

{{#include ../../../../crates/swift-bridge-ir/src/errors/parse_error.rs:mdbook-parse-error-enum}}

// ...
}
```

After adding a parse error variant, we write the code to generate an error message for the new variant.
Here are a few examples:

````rust
// via: crates/swift-bridge-ir/src/errors/parse_error.rs

{{#include ../../../../crates/swift-bridge-ir/src/errors/parse_error.rs:mdbook-parse-error-message}}

// ...
}
}
````

After adding our `ParseError` we can implement just enough code to make it pass.
This typically happens in `crates/swift-bridge-ir/src/parse.rs`, or one of its descendant modules.

For example, for the given UI test:

```rust
// via: crates/swift-bridge-macro/tests/ui/invalid-module-item.rs

#[swift_bridge::bridge]
mod ffi {
use std;
fn foo() {}
}

fn main() {}
```

```sh
# via: crates/swift-bridge-macro/tests/ui/invalid-module-item.stderr

error: Only `extern` blocks, structs and enums are supported.
--> tests/ui/invalid-module-item.rs:6:5
|
6 | use std;
| ^^^^^^^^

error: Only `extern` blocks, structs and enums are supported.
--> tests/ui/invalid-module-item.rs:7:5
|
7 | fn foo() {}
| ^^^^^^^^^^^
```

We push the `ParseError` error using:

```rust
// via: crates/swift-bridge-ir/src/parse.rs

for outer_mod_item in item_mod.content.unwrap().1 {
match outer_mod_item {
Item::ForeignMod(foreign_mod) => {
// ...
}
Item::Struct(item_struct) => {
// ...
}
Item::Enum(item_enum) => {
// ...
}
invalid_item => {
let error = ParseError::InvalidModuleItem { item: invalid_item };
errors.push(error);
}
};
}
```

[ui-tests]: https://github.com/chinedufn/swift-bridge/tree/master/crates/swift-bridge-macro/tests/ui
[trybuild]: https://github.com/dtolnay/trybuild
4 changes: 4 additions & 0 deletions crates/swift-bridge-ir/src/errors/parse_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use syn::{Error, FnArg, Item, Receiver};
use syn::{ForeignItemType, LitStr};
use syn::{Token, Type};

// <!-- ANCHOR: mdbook-parse-error-enum -->
pub(crate) enum ParseError {
/// `extern {}`
AbiNameMissing {
Expand All @@ -23,6 +24,7 @@ pub(crate) enum ParseError {
/// fn foo (bar: &Bar);
/// If Bar wasn't declared using a `type Bar` declaration.
UndeclaredType { ty: Type },
// <!-- ANCHOR_END: mdbook-parse-error-enum -->
/// Declared a type that we already support.
/// Example: `type u32`
DeclaredBuiltInType { ty: ForeignItemType },
Expand Down Expand Up @@ -65,6 +67,7 @@ pub(crate) enum IdentifiableParseError {
MissingReturnType { fn_ident: Ident },
}

// <!-- ANCHOR: mdbook-parse-error-message -->
impl Into<syn::Error> for ParseError {
fn into(self) -> Error {
match self {
Expand Down Expand Up @@ -105,6 +108,7 @@ self: &mut SomeType
);
Error::new_spanned(ty, message)
}
// <!-- ANCHOR_END: mdbook-parse-error-message -->
ParseError::DeclaredBuiltInType { ty } => {
let message = format!(
r#"Type {} is already supported
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ mod ffi {

#[swift_bridge(Copy(10))]
type IncorrectCopySize;

}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,4 @@ mod ffi {
}
}

pub struct SomeType;

fn main() {}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! # To Run
//! cargo test -p swift-bridge-macro -- ui trybuild=unrecognized-opaque-type-attribute.rs
// <!-- ANCHOR: mdbook-ui-test-example -->
#[swift_bridge::bridge]
mod ffi {
extern "Rust" {
Expand All @@ -10,5 +11,6 @@ mod ffi {
}

pub struct SomeType;
// <!-- ANCHOR_END: mdbook-ui-test-example -->

fn main() {}

0 comments on commit e0d3d62

Please sign in to comment.