Skip to content
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

salsa-macros: handle invalid inputs in a way friendlier to rust-analyzer #604

Merged
merged 2 commits into from
Oct 25, 2024
Merged
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
5 changes: 3 additions & 2 deletions components/salsa-macros/src/accumulator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use proc_macro2::TokenStream;
use crate::{
hygiene::Hygiene,
options::{AllowedOptions, Options},
token_stream_with_error,
};

// #[salsa::accumulator(jar = Jar0)]
Expand All @@ -14,7 +15,7 @@ pub(crate) fn accumulator(
) -> proc_macro::TokenStream {
let hygiene = Hygiene::from1(&input);
let args = syn::parse_macro_input!(args as Options<Accumulator>);
let struct_item = syn::parse_macro_input!(input as syn::ItemStruct);
let struct_item = parse_macro_input!(input as syn::ItemStruct);
let ident = struct_item.ident.clone();
let m = StructMacro {
hygiene,
Expand All @@ -23,7 +24,7 @@ pub(crate) fn accumulator(
};
match m.try_expand() {
Ok(v) => crate::debug::dump_tokens(ident, v).into(),
Err(e) => e.to_compile_error().into(),
Err(e) => token_stream_with_error(input, e),
}
}

Expand Down
8 changes: 4 additions & 4 deletions components/salsa-macros/src/db.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use proc_macro2::TokenStream;
use syn::parse::Nothing;

use crate::hygiene::Hygiene;
use crate::{hygiene::Hygiene, token_stream_with_error};

// Source:
//
Expand All @@ -16,11 +16,11 @@ pub(crate) fn db(
) -> proc_macro::TokenStream {
let _nothing = syn::parse_macro_input!(args as Nothing);
let hygiene = Hygiene::from1(&input);
let input = syn::parse_macro_input!(input as syn::Item);
let item = parse_macro_input!(input as syn::Item);
let db_macro = DbMacro { hygiene };
match db_macro.try_db(input) {
match db_macro.try_db(item) {
Ok(v) => crate::debug::dump_tokens("db", v).into(),
Err(e) => e.to_compile_error().into(),
Err(e) => token_stream_with_error(input, e),
}
}

Expand Down
5 changes: 3 additions & 2 deletions components/salsa-macros/src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::{
hygiene::Hygiene,
options::Options,
salsa_struct::{SalsaStruct, SalsaStructAllowedOptions},
token_stream_with_error,
};
use proc_macro2::TokenStream;

Expand All @@ -16,15 +17,15 @@ pub(crate) fn input(
) -> proc_macro::TokenStream {
let args = syn::parse_macro_input!(args as InputArgs);
let hygiene = Hygiene::from1(&input);
let struct_item = syn::parse_macro_input!(input as syn::ItemStruct);
let struct_item = parse_macro_input!(input as syn::ItemStruct);
let m = Macro {
hygiene,
args,
struct_item,
};
match m.try_macro() {
Ok(v) => v.into(),
Err(e) => e.to_compile_error().into(),
Err(e) => token_stream_with_error(input, e),
}
}

Expand Down
5 changes: 3 additions & 2 deletions components/salsa-macros/src/interned.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::{
hygiene::Hygiene,
options::Options,
salsa_struct::{SalsaStruct, SalsaStructAllowedOptions},
token_stream_with_error,
};
use proc_macro2::TokenStream;

Expand All @@ -17,15 +18,15 @@ pub(crate) fn interned(
) -> proc_macro::TokenStream {
let args = syn::parse_macro_input!(args as InternedArgs);
let hygiene = Hygiene::from1(&input);
let struct_item = syn::parse_macro_input!(input as syn::ItemStruct);
let struct_item = parse_macro_input!(input as syn::ItemStruct);
let m = Macro {
hygiene,
args,
struct_item,
};
match m.try_macro() {
Ok(v) => v.into(),
Err(e) => e.to_compile_error().into(),
Err(e) => token_stream_with_error(input, e),
}
}

Expand Down
23 changes: 21 additions & 2 deletions components/salsa-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ macro_rules! parse_quote {
}
}

/// Similar to `syn::parse_macro_input`, however, when a parse error is encountered, it will return
/// the input token stream in addition to the error. This will make it so that rust-analyzer can work
/// with incomplete code.
macro_rules! parse_macro_input {
($tokenstream:ident as $ty:ty) => {
match syn::parse::<$ty>($tokenstream.clone()) {
Ok(data) => data,
Err(err) => {
return $crate::token_stream_with_error($tokenstream, err);
}
}
};
}

mod accumulator;
mod db;
mod db_lifetime;
Expand Down Expand Up @@ -64,9 +78,14 @@ pub fn tracked(args: TokenStream, input: TokenStream) -> TokenStream {

#[proc_macro_derive(Update)]
pub fn update(input: TokenStream) -> TokenStream {
let item = syn::parse_macro_input!(input as syn::DeriveInput);
let item = parse_macro_input!(input as syn::DeriveInput);
match update::update_derive(item) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
Err(error) => token_stream_with_error(input, error),
}
}

pub(crate) fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream {
tokens.extend(TokenStream::from(error.into_compile_error()));
tokens
}
6 changes: 4 additions & 2 deletions components/salsa-macros/src/tracked.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use syn::{spanned::Spanned, Item};

use crate::token_stream_with_error;

pub(crate) fn tracked(
args: proc_macro::TokenStream,
input: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let item = syn::parse_macro_input!(input as Item);
let item = parse_macro_input!(input as Item);
let res = match item {
syn::Item::Struct(item) => crate::tracked_struct::tracked_struct(args, item),
syn::Item::Fn(item) => crate::tracked_fn::tracked_fn(args, item),
Expand All @@ -16,6 +18,6 @@ pub(crate) fn tracked(
};
match res {
Ok(s) => s.into(),
Err(err) => err.into_compile_error().into(),
Err(err) => token_stream_with_error(input, err),
}
}
6 changes: 6 additions & 0 deletions tests/compile-fail/input_struct_incompatibles.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,9 @@ error: `#[id]` cannot be used with `#[salsa::input]`
21 | / #[id]
22 | | field: u32,
| |______________^

error: cannot find attribute `id` in this scope
--> tests/compile-fail/input_struct_incompatibles.rs:21:7
|
21 | #[id]
| ^^
6 changes: 6 additions & 0 deletions tests/compile-fail/interned_struct_incompatibles.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,9 @@ error: `#[id]` cannot be used with `#[salsa::interned]`
33 | / #[id]
34 | | field: u32,
| |______________^

error: cannot find attribute `id` in this scope
--> tests/compile-fail/interned_struct_incompatibles.rs:33:7
|
33 | #[id]
| ^^
31 changes: 31 additions & 0 deletions tests/compile-fail/tracked_fn_incompatibles.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,20 @@ error: only a single lifetime parameter is accepted
67 | fn tracked_fn_with_multiple_lts<'db1, 'db2>(db: &'db1 dyn Db, interned: MyInterned<'db2>) -> u32 {
| ^^^^

error: `self` parameter is only allowed in associated functions
--> tests/compile-fail/tracked_fn_incompatibles.rs:27:55
|
27 | fn tracked_fn_with_receiver_not_applied_to_impl_block(&self, db: &dyn Db) -> u32 {}
| ^^^^^ not semantically valid as function parameter
|
= note: associated functions are those in `impl` or `trait` definitions

error[E0415]: identifier `input` is bound more than once in this parameter list
--> tests/compile-fail/tracked_fn_incompatibles.rs:33:5
|
33 | input: MyInput,
| ^^^^^ used as parameter more than once

error[E0106]: missing lifetime specifier
--> tests/compile-fail/tracked_fn_incompatibles.rs:61:15
|
Expand All @@ -64,3 +78,20 @@ error[E0308]: mismatched types
| ----------------- implicitly returns `()` as its body has no tail or `return` expression
24 | fn tracked_fn_with_one_input(db: &dyn Db) -> u32 {}
| ^^^ expected `u32`, found `()`

error[E0308]: mismatched types
--> tests/compile-fail/tracked_fn_incompatibles.rs:27:78
|
27 | fn tracked_fn_with_receiver_not_applied_to_impl_block(&self, db: &dyn Db) -> u32 {}
| -------------------------------------------------- ^^^ expected `u32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression

error[E0308]: mismatched types
--> tests/compile-fail/tracked_fn_incompatibles.rs:34:6
|
30 | fn tracked_fn_with_too_many_arguments_for_specify(
| ---------------------------------------------- implicitly returns `()` as its body has no tail or `return` expression
...
34 | ) -> u32 {
| ^^^ expected `u32`, found `()`
127 changes: 127 additions & 0 deletions tests/compile-fail/tracked_impl_incompatibles.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,69 @@ error: unexpected token
41 | #[salsa::tracked(constructor = Constructor)]
| ^^^^^^^^^^^

error[E0119]: conflicting implementations of trait `Default` for type `MyTracked<'_>`
--> tests/compile-fail/tracked_impl_incompatibles.rs:12:1
|
7 | impl<'db> std::default::Default for MyTracked<'db> {
| -------------------------------------------------- first implementation here
...
12 | impl<'db> std::default::Default for MyTracked<'db> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyTracked<'_>`

error[E0119]: conflicting implementations of trait `Default` for type `MyTracked<'_>`
--> tests/compile-fail/tracked_impl_incompatibles.rs:17:1
|
7 | impl<'db> std::default::Default for MyTracked<'db> {
| -------------------------------------------------- first implementation here
...
17 | impl<'db> std::default::Default for MyTracked<'db> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyTracked<'_>`

error[E0119]: conflicting implementations of trait `Default` for type `MyTracked<'_>`
--> tests/compile-fail/tracked_impl_incompatibles.rs:22:1
|
7 | impl<'db> std::default::Default for MyTracked<'db> {
| -------------------------------------------------- first implementation here
...
22 | impl<'db> std::default::Default for MyTracked<'db> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyTracked<'_>`

error[E0119]: conflicting implementations of trait `Default` for type `MyTracked<'_>`
--> tests/compile-fail/tracked_impl_incompatibles.rs:27:1
|
7 | impl<'db> std::default::Default for MyTracked<'db> {
| -------------------------------------------------- first implementation here
...
27 | impl<'db> std::default::Default for MyTracked<'db> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyTracked<'_>`

error[E0119]: conflicting implementations of trait `Default` for type `MyTracked<'_>`
--> tests/compile-fail/tracked_impl_incompatibles.rs:32:1
|
7 | impl<'db> std::default::Default for MyTracked<'db> {
| -------------------------------------------------- first implementation here
...
32 | impl<'db> std::default::Default for MyTracked<'db> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyTracked<'_>`

error[E0119]: conflicting implementations of trait `Default` for type `MyTracked<'_>`
--> tests/compile-fail/tracked_impl_incompatibles.rs:37:1
|
7 | impl<'db> std::default::Default for MyTracked<'db> {
| -------------------------------------------------- first implementation here
...
37 | impl<'db> std::default::Default for MyTracked<'db> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyTracked<'_>`

error[E0119]: conflicting implementations of trait `Default` for type `MyTracked<'_>`
--> tests/compile-fail/tracked_impl_incompatibles.rs:42:1
|
7 | impl<'db> std::default::Default for MyTracked<'db> {
| -------------------------------------------------- first implementation here
...
42 | impl<'db> std::default::Default for MyTracked<'db> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyTracked<'_>`

error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> tests/compile-fail/tracked_impl_incompatibles.rs:47:1
|
Expand All @@ -57,6 +120,70 @@ error[E0117]: only traits defined in the current crate can be implemented for ar
|
= note: define and implement a trait or new type instead

error[E0308]: mismatched types
--> tests/compile-fail/tracked_impl_incompatibles.rs:8:21
|
8 | fn default() -> Self {}
| ------- ^^^^ expected `MyTracked<'_>`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression

error[E0308]: mismatched types
--> tests/compile-fail/tracked_impl_incompatibles.rs:13:21
|
13 | fn default() -> Self {}
| ------- ^^^^ expected `MyTracked<'_>`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression

error[E0308]: mismatched types
--> tests/compile-fail/tracked_impl_incompatibles.rs:18:21
|
18 | fn default() -> Self {}
| ------- ^^^^ expected `MyTracked<'_>`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression

error[E0308]: mismatched types
--> tests/compile-fail/tracked_impl_incompatibles.rs:23:21
|
23 | fn default() -> Self {}
| ------- ^^^^ expected `MyTracked<'_>`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression

error[E0308]: mismatched types
--> tests/compile-fail/tracked_impl_incompatibles.rs:28:21
|
28 | fn default() -> Self {}
| ------- ^^^^ expected `MyTracked<'_>`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression

error[E0308]: mismatched types
--> tests/compile-fail/tracked_impl_incompatibles.rs:33:21
|
33 | fn default() -> Self {}
| ------- ^^^^ expected `MyTracked<'_>`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression

error[E0308]: mismatched types
--> tests/compile-fail/tracked_impl_incompatibles.rs:38:21
|
38 | fn default() -> Self {}
| ------- ^^^^ expected `MyTracked<'_>`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression

error[E0308]: mismatched types
--> tests/compile-fail/tracked_impl_incompatibles.rs:43:21
|
43 | fn default() -> Self {}
| ------- ^^^^ expected `MyTracked<'_>`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression

error[E0308]: mismatched types
--> tests/compile-fail/tracked_impl_incompatibles.rs:48:21
|
Expand Down
Loading
Loading