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

Add allow-by-default lint on implicit ABI in extern function pointers and items #76219

Merged
merged 4 commits into from
Jan 13, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock
Original file line number Diff line number Diff line change
@@ -3896,6 +3896,7 @@ dependencies = [
"rustc_macros",
"rustc_serialize",
"rustc_span",
"rustc_target",
"tracing",
]

38 changes: 24 additions & 14 deletions compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
@@ -310,19 +310,24 @@ impl<'hir> LoweringContext<'_, 'hir> {
);
let sig = hir::FnSig {
decl,
header: this.lower_fn_header(header),
header: this.lower_fn_header(header, fn_sig_span, id),
span: fn_sig_span,
};
hir::ItemKind::Fn(sig, generics, body_id)
})
}
ItemKind::Mod(ref m) => hir::ItemKind::Mod(self.lower_mod(m)),
ItemKind::ForeignMod(ref fm) => hir::ItemKind::ForeignMod {
abi: fm.abi.map_or(abi::Abi::C, |abi| self.lower_abi(abi)),
items: self
.arena
.alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))),
},
ItemKind::ForeignMod(ref fm) => {
if fm.abi.is_none() {
self.maybe_lint_missing_abi(span, id, abi::Abi::C);
}
hir::ItemKind::ForeignMod {
abi: fm.abi.map_or(abi::Abi::C, |abi| self.lower_abi(abi)),
items: self
.arena
.alloc_from_iter(fm.items.iter().map(|x| self.lower_foreign_item_ref(x))),
}
}
ItemKind::GlobalAsm(ref ga) => hir::ItemKind::GlobalAsm(self.lower_global_asm(ga)),
ItemKind::TyAlias(_, ref gen, _, Some(ref ty)) => {
// We lower
@@ -801,13 +806,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
AssocItemKind::Fn(_, ref sig, ref generics, None) => {
let names = self.lower_fn_params_to_names(&sig.decl);
let (generics, sig) =
self.lower_method_sig(generics, sig, trait_item_def_id, false, None);
self.lower_method_sig(generics, sig, trait_item_def_id, false, None, i.id);
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)))
}
AssocItemKind::Fn(_, ref sig, ref generics, Some(ref body)) => {
let body_id = self.lower_fn_body_block(i.span, &sig.decl, Some(body));
let (generics, sig) =
self.lower_method_sig(generics, sig, trait_item_def_id, false, None);
self.lower_method_sig(generics, sig, trait_item_def_id, false, None, i.id);
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)))
}
AssocItemKind::TyAlias(_, ref generics, ref bounds, ref default) => {
@@ -877,6 +882,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
impl_item_def_id,
impl_trait_return_allow,
asyncness.opt_return_id(),
i.id,
);

(generics, hir::ImplItemKind::Fn(sig, body_id))
@@ -1270,8 +1276,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
fn_def_id: LocalDefId,
impl_trait_return_allow: bool,
is_async: Option<NodeId>,
id: NodeId,
) -> (hir::Generics<'hir>, hir::FnSig<'hir>) {
let header = self.lower_fn_header(sig.header);
let header = self.lower_fn_header(sig.header, sig.span, id);
let (generics, decl) = self.add_in_band_defs(
generics,
fn_def_id,
@@ -1288,12 +1295,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
(generics, hir::FnSig { header, decl, span: sig.span })
}

fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader {
fn lower_fn_header(&mut self, h: FnHeader, span: Span, id: NodeId) -> hir::FnHeader {
hir::FnHeader {
unsafety: self.lower_unsafety(h.unsafety),
asyncness: self.lower_asyncness(h.asyncness),
constness: self.lower_constness(h.constness),
abi: self.lower_extern(h.ext),
abi: self.lower_extern(h.ext, span, id),
}
}

@@ -1304,10 +1311,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
})
}

pub(super) fn lower_extern(&mut self, ext: Extern) -> abi::Abi {
pub(super) fn lower_extern(&mut self, ext: Extern, span: Span, id: NodeId) -> abi::Abi {
match ext {
Extern::None => abi::Abi::Rust,
Extern::Implicit => abi::Abi::C,
Extern::Implicit => {
self.maybe_lint_missing_abi(span, id, abi::Abi::C);
abi::Abi::C
}
Extern::Explicit(abi) => self.lower_abi(abi),
}
}
27 changes: 25 additions & 2 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
@@ -53,13 +53,15 @@ use rustc_hir::definitions::{DefKey, DefPathData, Definitions};
use rustc_hir::intravisit;
use rustc_hir::{ConstArg, GenericArg, ParamName};
use rustc_index::vec::{Idx, IndexVec};
use rustc_session::lint::{builtin::BARE_TRAIT_OBJECTS, BuiltinLintDiagnostics, LintBuffer};
use rustc_session::lint::builtin::{BARE_TRAIT_OBJECTS, MISSING_ABI};
use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer};
use rustc_session::parse::ParseSess;
use rustc_session::Session;
use rustc_span::hygiene::ExpnId;
use rustc_span::source_map::{respan, DesugaringKind};
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::Span;
use rustc_target::spec::abi::Abi;

use smallvec::{smallvec, SmallVec};
use std::collections::BTreeMap;
@@ -1288,14 +1290,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
}
TyKind::BareFn(ref f) => self.with_in_scope_lifetime_defs(&f.generic_params, |this| {
this.with_anonymous_lifetime_mode(AnonymousLifetimeMode::PassThrough, |this| {
let span = this.sess.source_map().next_point(t.span.shrink_to_lo());
hir::TyKind::BareFn(this.arena.alloc(hir::BareFnTy {
generic_params: this.lower_generic_params(
&f.generic_params,
&NodeMap::default(),
ImplTraitContext::disallowed(),
),
unsafety: this.lower_unsafety(f.unsafety),
abi: this.lower_extern(f.ext),
abi: this.lower_extern(f.ext, span, t.id),
decl: this.lower_fn_decl(&f.decl, None, false, None),
param_names: this.lower_fn_params_to_names(&f.decl),
}))
@@ -2777,6 +2780,26 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
)
}
}

fn maybe_lint_missing_abi(&mut self, span: Span, id: NodeId, default: Abi) {
// FIXME(davidtwco): This is a hack to detect macros which produce spans of the
// call site which do not have a macro backtrace. See #61963.
let is_macro_callsite = self
.sess
.source_map()
.span_to_snippet(span)
.map(|snippet| snippet.starts_with("#["))
.unwrap_or(true);
if !is_macro_callsite {
self.resolver.lint_buffer().buffer_lint_with_diagnostic(
MISSING_ABI,
id,
span,
"extern declarations without an explicit ABI are deprecated",
BuiltinLintDiagnostics::MissingAbi(span, default),
)
}
}
}

fn body_ids(bodies: &BTreeMap<hir::BodyId, hir::Body<'_>>) -> Vec<hir::BodyId> {
6 changes: 3 additions & 3 deletions compiler/rustc_error_codes/src/error_codes/E0044.md
Original file line number Diff line number Diff line change
@@ -3,13 +3,13 @@ You cannot use type or const parameters on foreign items.
Example of erroneous code:

```compile_fail,E0044
extern { fn some_func<T>(x: T); }
extern "C" { fn some_func<T>(x: T); }
```

To fix this, replace the generic parameter with the specializations that you
need:

```
extern { fn some_func_i32(x: i32); }
extern { fn some_func_i64(x: i64); }
extern "C" { fn some_func_i32(x: i32); }
extern "C" { fn some_func_i64(x: i64); }
```
6 changes: 3 additions & 3 deletions compiler/rustc_error_codes/src/error_codes/E0130.md
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ A pattern was declared as an argument in a foreign function declaration.
Erroneous code example:

```compile_fail,E0130
extern {
extern "C" {
fn foo((a, b): (u32, u32)); // error: patterns aren't allowed in foreign
// function declarations
}
@@ -17,15 +17,15 @@ struct SomeStruct {
b: u32,
}
extern {
extern "C" {
fn foo(s: SomeStruct); // ok!
}
```

Or:

```
extern {
extern "C" {
fn foo(a: (u32, u32)); // ok!
}
```
4 changes: 2 additions & 2 deletions compiler/rustc_error_codes/src/error_codes/E0454.md
Original file line number Diff line number Diff line change
@@ -3,13 +3,13 @@ A link name was given with an empty name.
Erroneous code example:

```compile_fail,E0454
#[link(name = "")] extern {}
#[link(name = "")] extern "C" {}
// error: `#[link(name = "")]` given with empty name
```

The rust compiler cannot link to an external library if you don't give it its
name. Example:

```no_run
#[link(name = "some_lib")] extern {} // ok!
#[link(name = "some_lib")] extern "C" {} // ok!
```
4 changes: 2 additions & 2 deletions compiler/rustc_error_codes/src/error_codes/E0455.md
Original file line number Diff line number Diff line change
@@ -4,15 +4,15 @@ as frameworks are specific to that operating system.
Erroneous code example:

```ignore (should-compile_fail-but-cannot-doctest-conditionally-without-macos)
#[link(name = "FooCoreServices", kind = "framework")] extern {}
#[link(name = "FooCoreServices", kind = "framework")] extern "C" {}
// OS used to compile is Linux for example
```

To solve this error you can use conditional compilation:

```
#[cfg_attr(target="macos", link(name = "FooCoreServices", kind = "framework"))]
extern {}
extern "C" {}
```

Learn more in the [Conditional Compilation][conditional-compilation] section
2 changes: 1 addition & 1 deletion compiler/rustc_error_codes/src/error_codes/E0458.md
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ An unknown "kind" was specified for a link attribute.
Erroneous code example:

```compile_fail,E0458
#[link(kind = "wonderful_unicorn")] extern {}
#[link(kind = "wonderful_unicorn")] extern "C" {}
// error: unknown kind: `wonderful_unicorn`
```

4 changes: 2 additions & 2 deletions compiler/rustc_error_codes/src/error_codes/E0459.md
Original file line number Diff line number Diff line change
@@ -3,13 +3,13 @@ A link was used without a name parameter.
Erroneous code example:

```compile_fail,E0459
#[link(kind = "dylib")] extern {}
#[link(kind = "dylib")] extern "C" {}
// error: `#[link(...)]` specified without `name = "foo"`
```

Please add the name parameter to allow the rust compiler to find the library
you want. Example:

```no_run
#[link(kind = "dylib", name = "some_lib")] extern {} // ok!
#[link(kind = "dylib", name = "some_lib")] extern "C" {} // ok!
```
4 changes: 2 additions & 2 deletions compiler/rustc_error_codes/src/error_codes/E0617.md
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ Attempted to pass an invalid type of variable into a variadic function.
Erroneous code example:

```compile_fail,E0617
extern {
extern "C" {
fn printf(c: *const i8, ...);
}
@@ -21,7 +21,7 @@ to import from `std::os::raw`).
In this case, `c_double` has the same size as `f64` so we can use it directly:

```no_run
# extern {
# extern "C" {
# fn printf(c: *const i8, ...);
# }
unsafe {
2 changes: 1 addition & 1 deletion compiler/rustc_error_codes/src/error_codes/E0633.md
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ Erroneous code example:
#![feature(unwind_attributes)]
#[unwind()] // error: expected one argument
pub extern fn something() {}
pub extern "C" fn something() {}
fn main() {}
```
2 changes: 1 addition & 1 deletion compiler/rustc_error_codes/src/error_codes/E0724.md
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ the function inside of an `extern` block.
```
#![feature(ffi_returns_twice)]
extern {
extern "C" {
#[ffi_returns_twice] // ok!
pub fn foo();
}
4 changes: 4 additions & 0 deletions compiler/rustc_lint/src/context.rs
Original file line number Diff line number Diff line change
@@ -600,6 +600,10 @@ pub trait LintContext: Sized {
BuiltinLintDiagnostics::PatternsInFnsWithoutBody(span, ident) => {
db.span_suggestion(span, "remove `mut` from the parameter", ident.to_string(), Applicability::MachineApplicable);
}
BuiltinLintDiagnostics::MissingAbi(span, default_abi) => {
db.span_label(span, "ABI should be specified here");
db.help(&format!("the default ABI is {}", default_abi.name()));
}
}
// Rewrap `db`, and pass control to the user.
decorate(LintDiagnosticBuilder::new(db));
1 change: 1 addition & 0 deletions compiler/rustc_lint_defs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -11,3 +11,4 @@ rustc_data_structures = { path = "../rustc_data_structures" }
rustc_span = { path = "../rustc_span" }
rustc_serialize = { path = "../rustc_serialize" }
rustc_macros = { path = "../rustc_macros" }
rustc_target = { path = "../rustc_target" }
26 changes: 26 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
@@ -2917,6 +2917,7 @@ declare_lint_pass! {
FUNCTION_ITEM_REFERENCES,
USELESS_DEPRECATED,
UNSUPPORTED_NAKED_FUNCTIONS,
MISSING_ABI,
]
}

@@ -2944,3 +2945,28 @@ declare_lint! {
}

declare_lint_pass!(UnusedDocComment => [UNUSED_DOC_COMMENTS]);

declare_lint! {
/// The `missing_abi` lint detects cases where the ABI is omitted from
/// extern declarations.
///
/// ### Example
///
/// ```rust,compile_fail
/// #![deny(missing_abi)]
///
/// extern fn foo() {}
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// Historically, Rust implicitly selected C as the ABI for extern
/// declarations. We expect to add new ABIs, like `C-unwind`, in the future,
/// though this has not yet happened, and especially with their addition
/// seeing the ABI easily will make code review easier.
pub MISSING_ABI,
Allow,
"No declared ABI for extern declaration"
}
2 changes: 2 additions & 0 deletions compiler/rustc_lint_defs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ use rustc_ast::node_id::{NodeId, NodeMap};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
use rustc_span::edition::Edition;
use rustc_span::{sym, symbol::Ident, MultiSpan, Span, Symbol};
use rustc_target::spec::abi::Abi;

pub mod builtin;

@@ -252,6 +253,7 @@ pub enum BuiltinLintDiagnostics {
UnusedImports(String, Vec<(Span, String)>),
RedundantImport(Vec<(Span, bool)>, Ident),
DeprecatedMacro(Option<Symbol>, Span),
MissingAbi(Span, Abi),
UnusedDocComment(Span),
PatternsInFnsWithoutBody(Span, Ident),
}
2 changes: 1 addition & 1 deletion compiler/rustc_llvm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@ pub fn initialize_available_targets() {
($cfg:meta, $($method:ident),*) => { {
#[cfg($cfg)]
fn init() {
extern {
extern "C" {
$(fn $method();)*
}
unsafe {
Loading