diff --git a/src/formatting/items.rs b/src/formatting/items.rs index b79faf3a555..6a8a72e1a70 100644 --- a/src/formatting/items.rs +++ b/src/formatting/items.rs @@ -194,13 +194,19 @@ struct Item<'a> { } impl<'a> Item<'a> { - fn from_foreign_mod(fm: &'a ast::ForeignMod, span: Span, config: &Config) -> Item<'a> { + fn from_foreign_mod( + fm: &'a ast::ForeignMod, + span: Span, + config: &Config, + attrs: &[ast::Attribute], + ) -> Item<'a> { Item { keyword: "", abi: format_extern( ast::Extern::from_abi(fm.abi), config.force_explicit_abi(), true, + Some(attrs), ), vis: None, body: fm @@ -308,6 +314,7 @@ impl<'a> FnSig<'a> { self.ext, context.config.force_explicit_abi(), false, + None, )); result } @@ -352,8 +359,13 @@ impl<'a> FmtVisitor<'a> { } } - pub(crate) fn format_foreign_mod(&mut self, fm: &ast::ForeignMod, span: Span) { - let item = Item::from_foreign_mod(fm, span, self.config); + pub(crate) fn format_foreign_mod( + &mut self, + fm: &ast::ForeignMod, + span: Span, + attrs: &[ast::Attribute], + ) { + let item = Item::from_foreign_mod(fm, span, self.config, attrs); self.format_item(&item); } diff --git a/src/formatting/types.rs b/src/formatting/types.rs index 1c28dd60140..bc2ee840f10 100644 --- a/src/formatting/types.rs +++ b/src/formatting/types.rs @@ -785,6 +785,7 @@ fn rewrite_bare_fn( bare_fn.ext, context.config.force_explicit_abi(), false, + None, )); result.push_str("fn"); diff --git a/src/formatting/utils.rs b/src/formatting/utils.rs index 97697945c77..06b10113152 100644 --- a/src/formatting/utils.rs +++ b/src/formatting/utils.rs @@ -147,19 +147,28 @@ pub(crate) fn format_extern( ext: ast::Extern, explicit_abi: bool, is_mod: bool, + attrs: Option<&[ast::Attribute]>, ) -> Cow<'static, str> { - let abi = match ext { - ast::Extern::None => "Rust".to_owned(), - ast::Extern::Implicit => "C".to_owned(), - ast::Extern::Explicit(abi) => abi.symbol_unescaped.to_string(), - }; - - if abi == "Rust" && !is_mod { - Cow::from("") - } else if abi == "C" && !explicit_abi { - Cow::from("extern ") - } else { - Cow::from(format!(r#"extern "{}" "#, abi)) + let format_explicit_abi = |abi: &str| Cow::from(format!(r#"extern "{}" "#, abi)); + let explicit_conversion_preserves_semantics = + || !is_mod || (is_mod && attrs.map_or(true, |a| a.is_empty())); + + match ext { + ast::Extern::None if !is_mod => Cow::from(""), + ast::Extern::Explicit(ast::StrLit { + symbol_unescaped, .. + }) if !is_mod && symbol_unescaped == rustc_span::sym::rust => Cow::from(""), + ast::Extern::Implicit if !explicit_abi || !explicit_conversion_preserves_semantics() => { + Cow::from("extern ") + } + ast::Extern::Explicit(ast::StrLit { + symbol_unescaped, .. + }) if !explicit_abi && symbol_unescaped == rustc_span::sym::C => Cow::from("extern "), + ast::Extern::None => format_explicit_abi("Rust"), + ast::Extern::Implicit => format_explicit_abi("C"), + ast::Extern::Explicit(ast::StrLit { + symbol_unescaped, .. + }) => format_explicit_abi(&symbol_unescaped.to_string()), } } diff --git a/src/formatting/visitor.rs b/src/formatting/visitor.rs index 262ec5cf64e..ffe74e0d333 100644 --- a/src/formatting/visitor.rs +++ b/src/formatting/visitor.rs @@ -513,7 +513,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> { } ast::ItemKind::ForeignMod(ref foreign_mod) => { self.format_missing_with_indent(source!(self, item.span).lo()); - self.format_foreign_mod(foreign_mod, item.span); + self.format_foreign_mod(foreign_mod, item.span, item.attrs()); } ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => { self.visit_static(&StaticParts::from_item(item)); diff --git a/tests/source/extern.rs b/tests/source/extern.rs index 564c078ed2c..1dd2bdbab8c 100644 --- a/tests/source/extern.rs +++ b/tests/source/extern.rs @@ -63,3 +63,20 @@ libc::c_long; extern { } + +// #2908 - https://github.com/rust-lang/rustfmt/issues/2908 +#[wasm_bindgen(module = "child_process", version = "*")] +extern { + #[wasm_bindgen(js_name = execSync)] + fn exec_sync(cmd: &str) -> Buffer; + + fn foo() -> Bar; + } + +// Users that have an existing explicit ABI would need to convert to implicit +// manually, as rustfmt will be conservative and not attempt to convert explicit +// to implicit in the wasm case. +#[wasm_bindgen] +extern "C" { + fn foo() -> Bar; +} \ No newline at end of file diff --git a/tests/target/extern.rs b/tests/target/extern.rs index d25dba7d1bb..82cc8a77fc8 100644 --- a/tests/target/extern.rs +++ b/tests/target/extern.rs @@ -76,3 +76,20 @@ extern "C" { } extern "C" {} + +// #2908 - https://github.com/rust-lang/rustfmt/issues/2908 +#[wasm_bindgen(module = "child_process", version = "*")] +extern { + #[wasm_bindgen(js_name = execSync)] + fn exec_sync(cmd: &str) -> Buffer; + + fn foo() -> Bar; +} + +// Users that have an existing explicit ABI would need to convert to implicit +// manually, as rustfmt will be conservative and not attempt to convert explicit +// to implicit in the wasm case. +#[wasm_bindgen] +extern "C" { + fn foo() -> Bar; +}