Skip to content

Commit 14a0d14

Browse files
authored
Merge pull request #1418 from nicholasbishop/bishop-macros-cleanup
uefi-macros: Require that the entry function takes zero args
2 parents 9243540 + 1c41f17 commit 14a0d14

24 files changed

+79
-217
lines changed

uefi-macros/CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# uefi-macros - [Unreleased]
22

3+
## Changed
4+
5+
- **Breaking:** The `entry` no longer accepts any arguments.
6+
37

48
# uefi-macros - 0.16.0 (2024-09-09)
59

uefi-macros/src/lib.rs

+39-113
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ use proc_macro2::TokenStream as TokenStream2;
88
use quote::{quote, quote_spanned, TokenStreamExt};
99
use syn::spanned::Spanned;
1010
use syn::{
11-
parse_macro_input, parse_quote, parse_quote_spanned, Error, Expr, ExprLit, ExprPath, FnArg,
12-
Ident, ItemFn, ItemStruct, Lit, Pat, Visibility,
11+
parse_macro_input, parse_quote, parse_quote_spanned, Error, Expr, ExprLit, ExprPath, ItemFn,
12+
ItemStruct, Lit, Visibility,
1313
};
1414

1515
macro_rules! err {
@@ -93,49 +93,19 @@ pub fn unsafe_protocol(args: TokenStream, input: TokenStream) -> TokenStream {
9393
.into()
9494
}
9595

96-
/// Get the name of a function's argument at `arg_index`.
97-
fn get_function_arg_name(f: &ItemFn, arg_index: usize, errors: &mut TokenStream2) -> Option<Ident> {
98-
if let Some(FnArg::Typed(arg)) = f.sig.inputs.iter().nth(arg_index) {
99-
if let Pat::Ident(pat_ident) = &*arg.pat {
100-
// The argument has a valid name such as `handle` or `_handle`.
101-
Some(pat_ident.ident.clone())
102-
} else {
103-
// The argument is unnamed, i.e. `_`.
104-
errors.append_all(err!(
105-
arg.pat.span(),
106-
"Entry method's arguments must be named"
107-
));
108-
None
109-
}
110-
} else {
111-
// Either there are too few arguments, or it's the wrong kind of
112-
// argument (e.g. `self`).
113-
//
114-
// Don't append an error in this case. The error will be caught
115-
// by the typecheck later on, which will give a better error
116-
// message.
117-
None
118-
}
119-
}
120-
12196
/// Custom attribute for a UEFI executable entry point.
12297
///
12398
/// This attribute modifies a function to mark it as the entry point for
12499
/// a UEFI executable. The function:
125100
/// * Must return [`Status`].
126-
/// * Must have either zero parameters or two: [`Handle`] and [`SystemTable<Boot>`].
101+
/// * Must have zero parameters.
127102
/// * Can optionally be `unsafe`.
128103
///
129-
/// Due to internal implementation details the parameters must both be
130-
/// named, so `arg` or `_arg` are allowed, but not `_`.
131-
///
132104
/// The global system table pointer and global image handle will be set
133105
/// automatically.
134106
///
135107
/// # Examples
136108
///
137-
/// With no arguments:
138-
///
139109
/// ```no_run
140110
/// #![no_main]
141111
///
@@ -147,23 +117,7 @@ fn get_function_arg_name(f: &ItemFn, arg_index: usize, errors: &mut TokenStream2
147117
/// }
148118
/// ```
149119
///
150-
/// With two arguments:
151-
///
152-
/// ```no_run
153-
/// #![no_main]
154-
///
155-
/// use uefi::prelude::*;
156-
///
157-
/// #[entry]
158-
/// fn main(image: Handle, st: SystemTable<Boot>) -> Status {
159-
/// Status::SUCCESS
160-
/// }
161-
/// ```
162-
///
163-
/// [`Handle`]: https://docs.rs/uefi/latest/uefi/data_types/struct.Handle.html
164-
/// [`SystemTable<Boot>`]: https://docs.rs/uefi/latest/uefi/table/struct.SystemTable.html
165120
/// [`Status`]: https://docs.rs/uefi/latest/uefi/struct.Status.html
166-
/// [`BootServices::set_image_handle`]: https://docs.rs/uefi/latest/uefi/table/boot/struct.BootServices.html#method.set_image_handle
167121
#[proc_macro_attribute]
168122
pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
169123
// This code is inspired by the approach in this embedded Rust crate:
@@ -181,92 +135,64 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
181135
let mut f = parse_macro_input!(input as ItemFn);
182136

183137
if let Some(ref abi) = f.sig.abi {
184-
errors.append_all(err!(abi, "Entry method must have no ABI modifier"));
138+
errors.append_all(err!(abi, "Entry function must have no ABI modifier"));
185139
}
186140
if let Some(asyncness) = f.sig.asyncness {
187-
errors.append_all(err!(asyncness, "Entry method should not be async"));
141+
errors.append_all(err!(asyncness, "Entry function should not be async"));
188142
}
189143
if let Some(constness) = f.sig.constness {
190-
errors.append_all(err!(constness, "Entry method should not be const"));
144+
errors.append_all(err!(constness, "Entry function should not be const"));
191145
}
192146
if !f.sig.generics.params.is_empty() {
193147
errors.append_all(err!(
194148
f.sig.generics.params,
195-
"Entry method should not be generic"
149+
"Entry function should not be generic"
196150
));
197151
}
198-
199-
let signature_span = f.sig.span();
200-
201-
// If the user doesn't specify any arguments to the entry function, fill in
202-
// the image handle and system table arguments automatically.
203-
let generated_args = f.sig.inputs.is_empty();
204-
if generated_args {
205-
f.sig.inputs = parse_quote_spanned!(
206-
signature_span=>
207-
internal_image_handle: ::uefi::Handle,
208-
internal_system_table: *const ::core::ffi::c_void,
209-
);
152+
if !f.sig.inputs.is_empty() {
153+
errors.append_all(err!(f.sig.inputs, "Entry function must have no arguments"));
210154
}
211155

212-
let image_handle_ident = get_function_arg_name(&f, 0, &mut errors);
213-
let system_table_ident = get_function_arg_name(&f, 1, &mut errors);
214-
215-
// show most errors at once instead of one by one
156+
// Show most errors all at once instead of one by one.
216157
if !errors.is_empty() {
217158
return errors.into();
218159
}
219160

220-
f.sig.abi = Some(syn::parse2(quote_spanned! (signature_span=> extern "efiapi")).unwrap());
161+
let signature_span = f.sig.span();
221162

222-
// allow the entry function to be unsafe (by moving the keyword around so that it actually works)
223-
let unsafety = &f.sig.unsafety;
224-
// strip any visibility modifiers
163+
// Fill in the image handle and system table arguments automatically.
164+
let image_handle_ident = quote!(internal_image_handle);
165+
let system_table_ident = quote!(internal_system_table);
166+
f.sig.inputs = parse_quote_spanned!(
167+
signature_span=>
168+
#image_handle_ident: ::uefi::Handle,
169+
#system_table_ident: *const ::core::ffi::c_void,
170+
);
171+
172+
// Insert code at the beginning of the entry function to set the global
173+
// image handle and system table pointer.
174+
f.block.stmts.insert(
175+
0,
176+
parse_quote! {
177+
unsafe {
178+
::uefi::boot::set_image_handle(#image_handle_ident);
179+
::uefi::table::set_system_table(#system_table_ident.cast());
180+
}
181+
},
182+
);
183+
184+
// Set the required ABI.
185+
f.sig.abi = Some(parse_quote_spanned!(signature_span=> extern "efiapi"));
186+
187+
// Strip any visibility modifiers.
225188
f.vis = Visibility::Inherited;
226-
// Set the global image handle. If `image_handle_ident` is `None`
227-
// then the typecheck is going to fail anyway.
228-
if let Some(image_handle_ident) = image_handle_ident {
229-
// Convert the system table arg (either `SystemTable<Boot>` or
230-
// `*const c_void`) to a pointer of the correct type.
231-
let system_table_ptr = if generated_args {
232-
quote!(#system_table_ident.cast())
233-
} else {
234-
quote!(#system_table_ident.as_ptr().cast())
235-
};
236-
237-
f.block.stmts.insert(
238-
0,
239-
parse_quote! {
240-
unsafe {
241-
::uefi::boot::set_image_handle(#image_handle_ident);
242-
::uefi::table::set_system_table(#system_table_ptr);
243-
}
244-
},
245-
);
246-
}
247189

190+
let unsafety = &f.sig.unsafety;
248191
let fn_ident = &f.sig.ident;
249-
// Get an iterator of the function inputs types. This is needed instead of
250-
// directly using `sig.inputs` because patterns you can use in fn items like
251-
// `mut <arg>` aren't valid in fn pointers.
252-
let fn_inputs = f.sig.inputs.iter().map(|arg| match arg {
253-
FnArg::Receiver(arg) => quote!(#arg),
254-
FnArg::Typed(arg) => {
255-
let ty = &arg.ty;
256-
quote!(#ty)
257-
}
258-
});
259192
let fn_output = &f.sig.output;
260193

261194
// Get the expected argument types for the main function.
262-
let expected_args = if generated_args {
263-
quote!(::uefi::Handle, *const core::ffi::c_void)
264-
} else {
265-
quote!(
266-
::uefi::Handle,
267-
::uefi::table::SystemTable<::uefi::table::Boot>
268-
)
269-
};
195+
let expected_args = quote!(::uefi::Handle, *const core::ffi::c_void);
270196

271197
let fn_type_check = quote_spanned! {signature_span=>
272198
// Cast from the function type to a function pointer with the same
@@ -281,7 +207,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
281207
// The expected fn pointer type.
282208
#unsafety extern "efiapi" fn(#expected_args) -> ::uefi::Status =
283209
// Cast from a fn item to a function pointer.
284-
#fn_ident as #unsafety extern "efiapi" fn(#(#fn_inputs),*) #fn_output;
210+
#fn_ident as #unsafety extern "efiapi" fn(#expected_args) #fn_output;
285211
};
286212

287213
let result = quote! {
+1-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
#![allow(unused_imports)]
21
#![no_main]
3-
#![allow(deprecated)]
42

53
use uefi::prelude::*;
6-
use uefi_macros::entry;
74

85
#[entry]
9-
extern "C" fn main(_handle: Handle, _st: SystemTable<Boot>) -> Status {
6+
extern "C" fn main() -> Status {
107
Status::SUCCESS
118
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
error: Entry method must have no ABI modifier
2-
--> tests/ui/fail/entry_bad_abi.rs:9:1
1+
error: Entry function must have no ABI modifier
2+
--> tests/ui/fail/entry_bad_abi.rs:6:1
33
|
4-
9 | extern "C" fn main(_handle: Handle, _st: SystemTable<Boot>) -> Status {
4+
6 | extern "C" fn main() -> Status {
55
| ^^^^^^
+1-4
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
#![allow(unused_imports)]
21
#![no_main]
3-
#![allow(deprecated)]
42

53
use uefi::prelude::*;
6-
use uefi_macros::entry;
74

85
#[entry]
9-
fn main(_handle: Handle, _st: SystemTable<Boot>, _x: usize) -> Status {
6+
fn main(_x: usize) -> Status {
107
Status::SUCCESS
118
}
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
error[E0308]: mismatched types
2-
--> tests/ui/fail/entry_bad_arg.rs:9:1
1+
error: Entry function must have no arguments
2+
--> tests/ui/fail/entry_bad_arg.rs:6:9
33
|
4-
9 | fn main(_handle: Handle, _st: SystemTable<Boot>, _x: usize) -> Status {
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ incorrect number of function parameters
6-
|
7-
= note: expected fn pointer `extern "efiapi" fn(uefi::Handle, uefi::prelude::SystemTable<uefi::prelude::Boot>) -> uefi::Status`
8-
found fn pointer `extern "efiapi" fn(uefi::Handle, uefi::prelude::SystemTable<uefi::prelude::Boot>, usize) -> uefi::Status`
4+
6 | fn main(_x: usize) -> Status {
5+
| ^^
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
#![allow(unused_imports)]
21
#![no_main]
32

43
use uefi::prelude::*;
5-
use uefi_macros::entry;
64

75
#[entry]
8-
async fn main(_handle: Handle, _st: SystemTable<Boot>) -> Status {
6+
async fn main() -> Status {
97
Status::SUCCESS
108
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
error: Entry method should not be async
2-
--> tests/ui/fail/entry_bad_async.rs:8:1
1+
error: Entry function should not be async
2+
--> tests/ui/fail/entry_bad_async.rs:6:1
33
|
4-
8 | async fn main(_handle: Handle, _st: SystemTable<Boot>) -> Status {
4+
6 | async fn main() -> Status {
55
| ^^^^^
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
#![allow(unused_imports)]
21
#![no_main]
32

43
use uefi::prelude::*;
5-
use uefi_macros::entry;
64

75
#[entry(some_arg)]
8-
fn main(_handle: Handle, _st: SystemTable<Boot>) -> Status {
6+
fn main() -> Status {
97
Status::SUCCESS
108
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: Entry attribute accepts no arguments
2-
--> tests/ui/fail/entry_bad_attr_arg.rs:7:9
2+
--> tests/ui/fail/entry_bad_attr_arg.rs:5:9
33
|
4-
7 | #[entry(some_arg)]
4+
5 | #[entry(some_arg)]
55
| ^^^^^^^^
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
#![allow(unused_imports)]
21
#![no_main]
32

43
use uefi::prelude::*;
5-
use uefi_macros::entry;
64

75
#[entry]
8-
const fn main(_handle: Handle, _st: SystemTable<Boot>) -> Status {
6+
const fn main() -> Status {
97
Status::SUCCESS
108
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
error: Entry method should not be const
2-
--> tests/ui/fail/entry_bad_const.rs:8:1
1+
error: Entry function should not be const
2+
--> tests/ui/fail/entry_bad_const.rs:6:1
33
|
4-
8 | const fn main(_handle: Handle, _st: SystemTable<Boot>) -> Status {
4+
6 | const fn main() -> Status {
55
| ^^^^^
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
#![allow(unused_imports)]
21
#![no_main]
32

43
use uefi::prelude::*;
5-
use uefi_macros::entry;
64

75
#[entry]
8-
fn main<T>(_handle: Handle, _st: SystemTable<Boot>) -> Status {
6+
fn main<T>() -> Status {
97
Status::SUCCESS
108
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
error: Entry method should not be generic
2-
--> tests/ui/fail/entry_bad_generic.rs:8:9
1+
error: Entry function should not be generic
2+
--> tests/ui/fail/entry_bad_generic.rs:6:9
33
|
4-
8 | fn main<T>(_handle: Handle, _st: SystemTable<Boot>) -> Status {
4+
6 | fn main<T>() -> Status {
55
| ^
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
#![allow(unused_imports)]
21
#![no_main]
3-
#![allow(deprecated)]
42

53
use uefi::prelude::*;
6-
use uefi_macros::entry;
74

85
#[entry]
9-
fn main(_handle: Handle, _st: SystemTable<Boot>) -> bool {
6+
fn main() -> bool {
107
false
118
}
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
error[E0308]: mismatched types
2-
--> tests/ui/fail/entry_bad_return_type.rs:9:1
2+
--> tests/ui/fail/entry_bad_return_type.rs:6:1
33
|
4-
9 | fn main(_handle: Handle, _st: SystemTable<Boot>) -> bool {
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Status`, found `bool`
4+
6 | fn main() -> bool {
5+
| ^^^^^^^^^^^^^^^^^ expected `Status`, found `bool`
66
|
7-
= note: expected fn pointer `extern "efiapi" fn(uefi::Handle, uefi::prelude::SystemTable<uefi::prelude::Boot>) -> Status`
8-
found fn pointer `extern "efiapi" fn(uefi::Handle, uefi::prelude::SystemTable<uefi::prelude::Boot>) -> bool`
7+
= note: expected fn pointer `extern "efiapi" fn(Handle, *const c_void) -> Status`
8+
found fn pointer `extern "efiapi" fn(Handle, *const c_void) -> bool`

0 commit comments

Comments
 (0)