1
Fork 0

[RFC 2091] Add #[track_caller] attribute.

- The attribute is behind a feature gate.
- Error if both #[naked] and #[track_caller] are applied to the same function.
- Error if #[track_caller] is applied to a non-function item.
- Error if ABI is not "rust"
- Error if #[track_caller] is applied to a trait function.

Error codes and descriptions are pending.
This commit is contained in:
Ayose 2019-07-20 00:55:58 +00:00 committed by Adam Perry
parent e3cb9ea15a
commit 543449d4fd
22 changed files with 199 additions and 1 deletions

View file

@ -0,0 +1,5 @@
# `track_caller`
The tracking issue for this feature is: [#47809](https://github.com/rust-lang/rust/issues/47809).
------------------------

View file

@ -1640,6 +1640,16 @@ each method; it is not possible to annotate the entire impl with an `#[inline]`
attribute. attribute.
"##, "##,
E0900: r##"
TODO: change error number
TODO: track_caller: invalid syntax
"##,
E0901: r##"
TODO: change error number
TODO: track_caller: no naked functions
"##,
E0522: r##" E0522: r##"
The lang attribute is intended for marking special items that are built-in to The lang attribute is intended for marking special items that are built-in to
Rust itself. This includes special traits (like `Copy` and `Sized`) that affect Rust itself. This includes special traits (like `Copy` and `Sized`) that affect

View file

@ -94,6 +94,7 @@ impl CheckAttrVisitor<'tcx> {
/// Checks any attribute. /// Checks any attribute.
fn check_attributes(&self, item: &hir::Item, target: Target) { fn check_attributes(&self, item: &hir::Item, target: Target) {
let mut is_valid = true; let mut is_valid = true;
let mut track_caller_span = None;
for attr in &item.attrs { for attr in &item.attrs {
is_valid &= if attr.check_name(sym::inline) { is_valid &= if attr.check_name(sym::inline) {
self.check_inline(attr, &item.span, target) self.check_inline(attr, &item.span, target)
@ -103,6 +104,9 @@ impl CheckAttrVisitor<'tcx> {
self.check_marker(attr, item, target) self.check_marker(attr, item, target)
} else if attr.check_name(sym::target_feature) { } else if attr.check_name(sym::target_feature) {
self.check_target_feature(attr, item, target) self.check_target_feature(attr, item, target)
} else if attr.check_name(sym::track_caller) {
track_caller_span = Some(attr.span);
self.check_track_caller(attr, &item, target)
} else { } else {
true true
}; };
@ -118,6 +122,19 @@ impl CheckAttrVisitor<'tcx> {
self.check_repr(item, target); self.check_repr(item, target);
self.check_used(item, target); self.check_used(item, target);
// Checks if `#[track_caller]` and `#[naked]` are both used.
if let Some(span) = track_caller_span {
if item.attrs.iter().any(|attr| attr.check_name(sym::naked)) {
struct_span_err!(
self.tcx.sess,
span,
E0901,
"cannot use `#[track_caller]` with `#[naked]`",
)
.emit();
}
}
} }
/// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid. /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
@ -135,6 +152,23 @@ impl CheckAttrVisitor<'tcx> {
} }
} }
/// Checks if a `#[target_feature]` can be applied.
fn check_track_caller(&self, attr: &hir::Attribute, item: &hir::Item, target: Target) -> bool {
if target != Target::Fn {
struct_span_err!(
self.tcx.sess,
attr.span,
E0900,
"attribute should be applied to function"
)
.span_label(item.span, "not a function")
.emit();
false
} else {
true
}
}
/// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid. /// Checks if the `#[non_exhaustive]` attribute on an `item` is valid. Returns `true` if valid.
fn check_non_exhaustive( fn check_non_exhaustive(
&self, &self,

View file

@ -2721,7 +2721,9 @@ bitflags! {
const USED = 1 << 9; const USED = 1 << 9;
/// #[ffi_returns_twice], indicates that an extern function can return /// #[ffi_returns_twice], indicates that an extern function can return
/// multiple times /// multiple times
const FFI_RETURNS_TWICE = 1 << 10; const FFI_RETURNS_TWICE = 1 << 10;
/// #[track_caller]: allow access to the caller location
const TRACK_CALLER = 1 << 11;
} }
} }

View file

@ -172,6 +172,18 @@ pub fn check_trait_item(tcx: TyCtxt<'_>, def_id: DefId) {
_ => None _ => None
}; };
check_associated_item(tcx, trait_item.hir_id, trait_item.span, method_sig); check_associated_item(tcx, trait_item.hir_id, trait_item.span, method_sig);
// Prohibits applying `#[track_caller]` to trait methods
for attr in &trait_item.attrs {
if attr.check_name(sym::track_caller) {
struct_span_err!(
tcx.sess,
attr.span,
E0903,
"`#[track_caller]` is not supported for trait items yet."
).emit();
}
}
} }
pub fn check_impl_item(tcx: TyCtxt<'_>, def_id: DefId) { pub fn check_impl_item(tcx: TyCtxt<'_>, def_id: DefId) {

View file

@ -2594,6 +2594,16 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED; codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
} else if attr.check_name(sym::thread_local) { } else if attr.check_name(sym::thread_local) {
codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL; codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL;
} else if attr.check_name(sym::track_caller) {
if tcx.fn_sig(id).abi() != abi::Abi::Rust {
struct_span_err!(
tcx.sess,
attr.span,
E0902,
"rust ABI is required to use `#[track_caller]`"
).emit();
}
codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
} else if attr.check_name(sym::export_name) { } else if attr.check_name(sym::export_name) {
if let Some(s) = attr.value_str() { if let Some(s) = attr.value_str() {
if s.as_str().contains("\0") { if s.as_str().contains("\0") {

View file

@ -4907,6 +4907,17 @@ fn foo_recursive(n: usize) -> Pin<Box<dyn Future<Output = ()>>> {
The `Box<...>` ensures that the result is of known size, The `Box<...>` ensures that the result is of known size,
and the pin is required to keep it in the same place in memory. and the pin is required to keep it in the same place in memory.
"##, "##,
E0902: r##"
TODO: change error number
TODO: track_caller: require Rust ABI to use track_caller
"##,
E0903: r##"
TODO: change error number
TODO: track_caller: can't apply in traits
"##,
; ;
// E0035, merged into E0087/E0089 // E0035, merged into E0087/E0089
// E0036, merged into E0087/E0089 // E0036, merged into E0087/E0089

View file

@ -525,6 +525,9 @@ declare_features! (
// Allows the use of raw-dylibs (RFC 2627). // Allows the use of raw-dylibs (RFC 2627).
(active, raw_dylib, "1.40.0", Some(58713), None), (active, raw_dylib, "1.40.0", Some(58713), None),
/// Enable accurate caller location reporting during panic (RFC 2091).
(active, track_caller, "1.37.0", Some(47809), None),
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// feature-group-end: actual feature gates // feature-group-end: actual feature gates
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------

View file

@ -499,6 +499,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
cfg_fn!(no_debug) cfg_fn!(no_debug)
) )
), ),
gated!(
track_caller, Whitelisted, template!(Word),
"the `#[track_caller]` attribute is an experimental feature",
),
gated!( gated!(
// Used in resolve: // Used in resolve:
prelude_import, Whitelisted, template!(Word), prelude_import, Whitelisted, template!(Word),

View file

@ -674,6 +674,7 @@ symbols! {
tool_attributes, tool_attributes,
tool_lints, tool_lints,
trace_macros, trace_macros,
track_caller,
trait_alias, trait_alias,
transmute, transmute,
transparent, transparent,

View file

@ -0,0 +1,6 @@
#[track_caller]
fn f() {}
//~^^ ERROR the `#[track_caller]` attribute is an experimental feature
fn main() {}

View file

@ -0,0 +1,12 @@
error[E0658]: the `#[track_caller]` attribute is an experimental feature
--> $DIR/feature-gate-track_caller.rs:2:1
|
LL | #[track_caller]
| ^^^^^^^^^^^^^^^
|
= note: for more information, see https://github.com/rust-lang/rust/issues/47809
= help: add `#![feature(track_caller)]` to the crate attributes to enable
error: aborting due to previous error
For more information about this error, try `rustc --explain E0658`.

View file

@ -0,0 +1,7 @@
#![feature(track_caller)]
#[track_caller(1)]
fn f() {}
//~^^ ERROR malformed `track_caller` attribute input
fn main() {}

View file

@ -0,0 +1,8 @@
error: malformed `track_caller` attribute input
--> $DIR/error-odd-syntax.rs:3:1
|
LL | #[track_caller(1)]
| ^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[track_caller]`
error: aborting due to previous error

View file

@ -0,0 +1,7 @@
#![feature(track_caller)]
#[track_caller]
extern "C" fn f() {}
//~^^ ERROR rust ABI is required to use `#[track_caller]`
fn main() {}

View file

@ -0,0 +1,9 @@
error[E0902]: rust ABI is required to use `#[track_caller]`
--> $DIR/error-with-invalid-abi.rs:3:1
|
LL | #[track_caller]
| ^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0902`.

View file

@ -0,0 +1,8 @@
#![feature(naked_functions, track_caller)]
#[track_caller]
#[naked]
fn f() {}
//~^^^ ERROR cannot use `#[track_caller]` with `#[naked]`
fn main() {}

View file

@ -0,0 +1,9 @@
error[E0901]: cannot use `#[track_caller]` with `#[naked]`
--> $DIR/error-with-naked.rs:3:1
|
LL | #[track_caller]
| ^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0901`.

View file

@ -0,0 +1,13 @@
#![feature(track_caller)]
trait Trait {
#[track_caller]
fn unwrap(&self);
//~^^ ERROR: `#[track_caller]` is not supported for trait items yet.
}
impl Trait for u64 {
fn unwrap(&self) {}
}
fn main() {}

View file

@ -0,0 +1,9 @@
error[E0903]: `#[track_caller]` is not supported for trait items yet.
--> $DIR/error-with-trait-fns.rs:4:5
|
LL | #[track_caller]
| ^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0903`.

View file

@ -0,0 +1,7 @@
#![feature(track_caller)]
#[track_caller]
struct S;
//~^^ ERROR attribute should be applied to function
fn main() {}

View file

@ -0,0 +1,11 @@
error[E0900]: attribute should be applied to function
--> $DIR/only-for-fns.rs:3:1
|
LL | #[track_caller]
| ^^^^^^^^^^^^^^^
LL | struct S;
| --------- not a function
error: aborting due to previous error
For more information about this error, try `rustc --explain E0900`.