Skip to content

Commit

Permalink
Merge pull request #190 from dtolnay/provider
Browse files Browse the repository at this point in the history
Fix "Multiple applicable provide methods in scope"
  • Loading branch information
dtolnay authored Sep 13, 2022
2 parents 37442be + 01e7c18 commit 2ca76ed
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 48 deletions.
66 changes: 66 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use std::env;
use std::fs;
use std::path::Path;
use std::process::{Command, ExitStatus, Stdio};
use std::str;

// This code exercises the surface area that we expect of the Provider API. If
// the current toolchain is able to compile it, then thiserror is able to use
// providers for backtrace support.
const PROBE: &str = r#"
#![feature(provide_any)]
use std::any::{Demand, Provider};
fn _f<'a, P: Provider>(p: &'a P, demand: &mut Demand<'a>) {
p.provide(demand);
}
"#;

fn main() {
match compile_probe() {
Some(status) if status.success() => println!("cargo:rustc-cfg=provide_any"),
_ => {}
}
}

fn compile_probe() -> Option<ExitStatus> {
let rustc = env::var_os("RUSTC")?;
let out_dir = env::var_os("OUT_DIR")?;
let probefile = Path::new(&out_dir).join("probe.rs");
fs::write(&probefile, PROBE).ok()?;

// Make sure to pick up Cargo rustc configuration.
let mut cmd = if let Some(wrapper) = env::var_os("RUSTC_WRAPPER") {
let mut cmd = Command::new(wrapper);
// The wrapper's first argument is supposed to be the path to rustc.
cmd.arg(rustc);
cmd
} else {
Command::new(rustc)
};

cmd.stderr(Stdio::null())
.arg("--edition=2018")
.arg("--crate-name=thiserror_build")
.arg("--crate-type=lib")
.arg("--emit=metadata")
.arg("--out-dir")
.arg(out_dir)
.arg(probefile);

if let Some(target) = env::var_os("TARGET") {
cmd.arg("--target").arg(target);
}

// If Cargo wants to set RUSTFLAGS, use that.
if let Ok(rustflags) = env::var("CARGO_ENCODED_RUSTFLAGS") {
if !rustflags.is_empty() {
for arg in rustflags.split('\x1f') {
cmd.arg(arg);
}
}
}

cmd.status().ok()
}
21 changes: 9 additions & 12 deletions impl/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@ fn impl_struct(input: Struct) -> TokenStream {
let source_provide = if type_is_option(source_field.ty) {
quote_spanned! {source.span()=>
if let std::option::Option::Some(source) = &self.#source {
source.provide(#demand);
source.thiserror_provide(#demand);
}
}
} else {
quote_spanned! {source.span()=>
self.#source.provide(#demand);
self.#source.thiserror_provide(#demand);
}
};
let self_provide = if source == backtrace {
Expand All @@ -89,8 +89,7 @@ fn impl_struct(input: Struct) -> TokenStream {
})
};
quote! {
#[allow(unused_imports)]
use std::error::Error as _;
use thiserror::__private::ThiserrorProvide;
#source_provide
#self_provide
}
Expand Down Expand Up @@ -260,12 +259,12 @@ fn impl_enum(input: Enum) -> TokenStream {
let source_provide = if type_is_option(source_field.ty) {
quote_spanned! {source.span()=>
if let std::option::Option::Some(source) = #varsource {
source.provide(#demand);
source.thiserror_provide(#demand);
}
}
} else {
quote_spanned! {source.span()=>
#varsource.provide(#demand);
#varsource.thiserror_provide(#demand);
}
};
let self_provide = if type_is_option(backtrace_field.ty) {
Expand All @@ -285,8 +284,7 @@ fn impl_enum(input: Enum) -> TokenStream {
#source: #varsource,
..
} => {
#[allow(unused_imports)]
use std::error::Error as _;
use thiserror::__private::ThiserrorProvide;
#source_provide
#self_provide
}
Expand All @@ -300,18 +298,17 @@ fn impl_enum(input: Enum) -> TokenStream {
let source_provide = if type_is_option(source_field.ty) {
quote_spanned! {backtrace.span()=>
if let std::option::Option::Some(source) = #varsource {
source.provide(#demand);
source.thiserror_provide(#demand);
}
}
} else {
quote_spanned! {backtrace.span()=>
#varsource.provide(#demand);
#varsource.thiserror_provide(#demand);
}
};
quote! {
#ty::#ident {#backtrace: #varsource, ..} => {
#[allow(unused_imports)]
use std::error::Error as _;
use thiserror::__private::ThiserrorProvide;
#source_provide
}
}
Expand Down
5 changes: 5 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,12 @@
clippy::return_self_not_must_use,
clippy::wildcard_imports,
)]
#![cfg_attr(provide_any, feature(provide_any))]

mod aserror;
mod display;
#[cfg(provide_any)]
mod provide;

pub use thiserror_impl::*;

Expand All @@ -219,4 +222,6 @@ pub use thiserror_impl::*;
pub mod __private {
pub use crate::aserror::AsDynError;
pub use crate::display::{DisplayAsDisplay, PathAsDisplay};
#[cfg(provide_any)]
pub use crate::provide::ThiserrorProvide;
}
15 changes: 15 additions & 0 deletions src/provide.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use std::any::{Demand, Provider};

pub trait ThiserrorProvide: Sealed {
fn thiserror_provide<'a>(&'a self, demand: &mut Demand<'a>);
}

impl<T: Provider + ?Sized> ThiserrorProvide for T {
#[inline]
fn thiserror_provide<'a>(&'a self, demand: &mut Demand<'a>) {
self.provide(demand);
}
}

pub trait Sealed {}
impl<T: Provider + ?Sized> Sealed for T {}
22 changes: 21 additions & 1 deletion tests/test_backtrace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ pub mod structs {
let error = AnyhowBacktrace {
source: anyhow::Error::msg("..."),
};
assert!(any::request_ref::<Backtrace>(&error).is_some());
// FIXME: change back to is_some after `impl Provider for anyhow::Error` exists.
assert!(any::request_ref::<Backtrace>(&error).is_none());

let error = BoxDynErrorBacktrace {
source: Box::new(PlainBacktrace {
Expand All @@ -149,6 +150,25 @@ pub mod structs {
};
assert!(any::request_ref::<Backtrace>(&error).is_some());
}

// https://github.com/dtolnay/thiserror/issues/185 -- std::error::Error and
// std::any::Provide both have a method called 'provide', so directly
// calling it from generated code could be ambiguous.
#[test]
fn test_provide_name_collision() {
use std::any::Provider;

#[derive(Error, Debug)]
#[error("...")]
struct MyError {
#[source]
#[backtrace]
x: std::io::Error,
}

let _: dyn Error;
let _: dyn Provider;
}
}

#[cfg(thiserror_nightly_testing)]
Expand Down
19 changes: 0 additions & 19 deletions tests/ui/multiple-provide.rs

This file was deleted.

16 changes: 0 additions & 16 deletions tests/ui/multiple-provide.stderr

This file was deleted.

0 comments on commit 2ca76ed

Please sign in to comment.