Auto merge of #70946 - jumbatm:clashing-extern-decl, r=nagisa
Add a lint to catch clashing `extern` fn declarations. Closes #69390. Adds lint `clashing_extern_decl` to detect when, within a single crate, an extern function of the same name is declared with different types. Because two symbols of the same name cannot be resolved to two different functions at link time, and one function cannot possibly have two types, a clashing extern declaration is almost certainly a mistake. This lint does not run between crates because a project may have dependencies which both rely on the same extern function, but declare it in a different (but valid) way. For example, they may both declare an opaque type for one or more of the arguments (which would end up distinct types), or use types that are valid conversions in the language the extern fn is defined in. In these cases, we can't say that the clashing declaration is incorrect. r? @eddyb
This commit is contained in:
commit
228a0ed7b0
14 changed files with 568 additions and 11 deletions
|
|
@ -277,6 +277,9 @@ pub mod primitive;
|
|||
// crate uses the this crate as its libcore.
|
||||
#[path = "../stdarch/crates/core_arch/src/mod.rs"]
|
||||
#[allow(missing_docs, missing_debug_implementations, dead_code, unused_imports)]
|
||||
// FIXME: This annotation should be moved into rust-lang/stdarch after clashing_extern_decl is
|
||||
// merged. It currently cannot because bootstrap fails as the lint hasn't been defined yet.
|
||||
#[cfg_attr(not(bootstrap), allow(clashing_extern_decl))]
|
||||
#[unstable(feature = "stdsimd", issue = "48556")]
|
||||
mod core_arch;
|
||||
|
||||
|
|
|
|||
|
|
@ -26,15 +26,15 @@ use rustc_ast::attr::{self, HasAttrs};
|
|||
use rustc_ast::tokenstream::{TokenStream, TokenTree};
|
||||
use rustc_ast::visit::{FnCtxt, FnKind};
|
||||
use rustc_ast_pretty::pprust::{self, expr_to_string};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder};
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
|
||||
use rustc_feature::{deprecated_attributes, AttributeGate, AttributeTemplate, AttributeType};
|
||||
use rustc_feature::{GateIssue, Stability};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{GenericParamKind, PatKind};
|
||||
use rustc_hir::{HirIdSet, Node};
|
||||
use rustc_hir::{ForeignItemKind, GenericParamKind, PatKind};
|
||||
use rustc_hir::{HirId, HirIdSet, Node};
|
||||
use rustc_middle::lint::LintDiagnosticBuilder;
|
||||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
|
|
@ -48,7 +48,7 @@ use rustc_trait_selection::traits::misc::can_type_implement_copy;
|
|||
|
||||
use crate::nonstandard_style::{method_context, MethodLateContext};
|
||||
|
||||
use log::debug;
|
||||
use log::{debug, trace};
|
||||
use std::fmt::Write;
|
||||
|
||||
// hardwired lints from librustc_middle
|
||||
|
|
@ -2053,3 +2053,224 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for InvalidValue {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
pub CLASHING_EXTERN_DECL,
|
||||
Warn,
|
||||
"detects when an extern fn has been declared with the same name but different types"
|
||||
}
|
||||
|
||||
pub struct ClashingExternDecl {
|
||||
seen_decls: FxHashMap<Symbol, HirId>,
|
||||
}
|
||||
|
||||
/// Differentiate between whether the name for an extern decl came from the link_name attribute or
|
||||
/// just from declaration itself. This is important because we don't want to report clashes on
|
||||
/// symbol name if they don't actually clash because one or the other links against a symbol with a
|
||||
/// different name.
|
||||
enum SymbolName {
|
||||
/// The name of the symbol + the span of the annotation which introduced the link name.
|
||||
Link(Symbol, Span),
|
||||
/// No link name, so just the name of the symbol.
|
||||
Normal(Symbol),
|
||||
}
|
||||
|
||||
impl SymbolName {
|
||||
fn get_name(&self) -> Symbol {
|
||||
match self {
|
||||
SymbolName::Link(s, _) | SymbolName::Normal(s) => *s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ClashingExternDecl {
|
||||
crate fn new() -> Self {
|
||||
ClashingExternDecl { seen_decls: FxHashMap::default() }
|
||||
}
|
||||
/// Insert a new foreign item into the seen set. If a symbol with the same name already exists
|
||||
/// for the item, return its HirId without updating the set.
|
||||
fn insert(&mut self, tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> Option<HirId> {
|
||||
let hid = fi.hir_id;
|
||||
|
||||
let name =
|
||||
&tcx.codegen_fn_attrs(tcx.hir().local_def_id(hid)).link_name.unwrap_or(fi.ident.name);
|
||||
|
||||
if self.seen_decls.contains_key(name) {
|
||||
// Avoid updating the map with the new entry when we do find a collision. We want to
|
||||
// make sure we're always pointing to the first definition as the previous declaration.
|
||||
// This lets us avoid emitting "knock-on" diagnostics.
|
||||
Some(*self.seen_decls.get(name).unwrap())
|
||||
} else {
|
||||
self.seen_decls.insert(*name, hid)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the name of the symbol that's linked against for a given extern declaration. That is,
|
||||
/// the name specified in a #[link_name = ...] attribute if one was specified, else, just the
|
||||
/// symbol's name.
|
||||
fn name_of_extern_decl(tcx: TyCtxt<'_>, fi: &hir::ForeignItem<'_>) -> SymbolName {
|
||||
let did = tcx.hir().local_def_id(fi.hir_id);
|
||||
if let Some((overridden_link_name, overridden_link_name_span)) =
|
||||
tcx.codegen_fn_attrs(did).link_name.map(|overridden_link_name| {
|
||||
// FIXME: Instead of searching through the attributes again to get span
|
||||
// information, we could have codegen_fn_attrs also give span information back for
|
||||
// where the attribute was defined. However, until this is found to be a
|
||||
// bottleneck, this does just fine.
|
||||
(
|
||||
overridden_link_name,
|
||||
tcx.get_attrs(did.to_def_id())
|
||||
.iter()
|
||||
.find(|at| at.check_name(sym::link_name))
|
||||
.unwrap()
|
||||
.span,
|
||||
)
|
||||
})
|
||||
{
|
||||
SymbolName::Link(overridden_link_name, overridden_link_name_span)
|
||||
} else {
|
||||
SymbolName::Normal(fi.ident.name)
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether two types are structurally the same enough that the declarations shouldn't
|
||||
/// clash. We need this so we don't emit a lint when two modules both declare an extern struct,
|
||||
/// with the same members (as the declarations shouldn't clash).
|
||||
fn structurally_same_type<'a, 'tcx>(
|
||||
cx: &LateContext<'a, 'tcx>,
|
||||
a: Ty<'tcx>,
|
||||
b: Ty<'tcx>,
|
||||
) -> bool {
|
||||
let tcx = cx.tcx;
|
||||
if a == b || rustc_middle::ty::TyS::same_type(a, b) {
|
||||
// All nominally-same types are structurally same, too.
|
||||
true
|
||||
} else {
|
||||
// Do a full, depth-first comparison between the two.
|
||||
use rustc_middle::ty::TyKind::*;
|
||||
let a_kind = &a.kind;
|
||||
let b_kind = &b.kind;
|
||||
|
||||
match (a_kind, b_kind) {
|
||||
(Adt(..), Adt(..)) => {
|
||||
// Adts are pretty straightforward: just compare the layouts.
|
||||
use rustc_target::abi::LayoutOf;
|
||||
let a_layout = cx.layout_of(a).unwrap().layout;
|
||||
let b_layout = cx.layout_of(b).unwrap().layout;
|
||||
a_layout == b_layout
|
||||
}
|
||||
(Array(a_ty, a_const), Array(b_ty, b_const)) => {
|
||||
// For arrays, we also check the constness of the type.
|
||||
a_const.val == b_const.val
|
||||
&& Self::structurally_same_type(cx, a_const.ty, b_const.ty)
|
||||
&& Self::structurally_same_type(cx, a_ty, b_ty)
|
||||
}
|
||||
(Slice(a_ty), Slice(b_ty)) => Self::structurally_same_type(cx, a_ty, b_ty),
|
||||
(RawPtr(a_tymut), RawPtr(b_tymut)) => {
|
||||
a_tymut.mutbl == a_tymut.mutbl
|
||||
&& Self::structurally_same_type(cx, &a_tymut.ty, &b_tymut.ty)
|
||||
}
|
||||
(Ref(_a_region, a_ty, a_mut), Ref(_b_region, b_ty, b_mut)) => {
|
||||
// For structural sameness, we don't need the region to be same.
|
||||
a_mut == b_mut && Self::structurally_same_type(cx, a_ty, b_ty)
|
||||
}
|
||||
(FnDef(..), FnDef(..)) => {
|
||||
// As we don't compare regions, skip_binder is fine.
|
||||
let a_poly_sig = a.fn_sig(tcx);
|
||||
let b_poly_sig = b.fn_sig(tcx);
|
||||
|
||||
let a_sig = a_poly_sig.skip_binder();
|
||||
let b_sig = b_poly_sig.skip_binder();
|
||||
|
||||
(a_sig.abi, a_sig.unsafety, a_sig.c_variadic)
|
||||
== (b_sig.abi, b_sig.unsafety, b_sig.c_variadic)
|
||||
&& a_sig.inputs().iter().eq_by(b_sig.inputs().iter(), |a, b| {
|
||||
Self::structurally_same_type(cx, a, b)
|
||||
})
|
||||
&& Self::structurally_same_type(cx, a_sig.output(), b_sig.output())
|
||||
}
|
||||
(Tuple(a_substs), Tuple(b_substs)) => {
|
||||
a_substs.types().eq_by(b_substs.types(), |a_ty, b_ty| {
|
||||
Self::structurally_same_type(cx, a_ty, b_ty)
|
||||
})
|
||||
}
|
||||
// For these, it's not quite as easy to define structural-sameness quite so easily.
|
||||
// For the purposes of this lint, take the conservative approach and mark them as
|
||||
// not structurally same.
|
||||
(Dynamic(..), Dynamic(..))
|
||||
| (Error(..), Error(..))
|
||||
| (Closure(..), Closure(..))
|
||||
| (Generator(..), Generator(..))
|
||||
| (GeneratorWitness(..), GeneratorWitness(..))
|
||||
| (Projection(..), Projection(..))
|
||||
| (Opaque(..), Opaque(..)) => false,
|
||||
// These definitely should have been caught above.
|
||||
(Bool, Bool) | (Char, Char) | (Never, Never) | (Str, Str) => unreachable!(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ClashingExternDecl => [CLASHING_EXTERN_DECL]);
|
||||
|
||||
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ClashingExternDecl {
|
||||
fn check_foreign_item(&mut self, cx: &LateContext<'a, 'tcx>, this_fi: &hir::ForeignItem<'_>) {
|
||||
trace!("ClashingExternDecl: check_foreign_item: {:?}", this_fi);
|
||||
if let ForeignItemKind::Fn(..) = this_fi.kind {
|
||||
let tcx = *&cx.tcx;
|
||||
if let Some(existing_hid) = self.insert(tcx, this_fi) {
|
||||
let existing_decl_ty = tcx.type_of(tcx.hir().local_def_id(existing_hid));
|
||||
let this_decl_ty = tcx.type_of(tcx.hir().local_def_id(this_fi.hir_id));
|
||||
debug!(
|
||||
"ClashingExternDecl: Comparing existing {:?}: {:?} to this {:?}: {:?}",
|
||||
existing_hid, existing_decl_ty, this_fi.hir_id, this_decl_ty
|
||||
);
|
||||
// Check that the declarations match.
|
||||
if !Self::structurally_same_type(cx, existing_decl_ty, this_decl_ty) {
|
||||
let orig_fi = tcx.hir().expect_foreign_item(existing_hid);
|
||||
let orig = Self::name_of_extern_decl(tcx, orig_fi);
|
||||
|
||||
// We want to ensure that we use spans for both decls that include where the
|
||||
// name was defined, whether that was from the link_name attribute or not.
|
||||
let get_relevant_span =
|
||||
|fi: &hir::ForeignItem<'_>| match Self::name_of_extern_decl(tcx, fi) {
|
||||
SymbolName::Normal(_) => fi.span,
|
||||
SymbolName::Link(_, annot_span) => fi.span.to(annot_span),
|
||||
};
|
||||
// Finally, emit the diagnostic.
|
||||
tcx.struct_span_lint_hir(
|
||||
CLASHING_EXTERN_DECL,
|
||||
this_fi.hir_id,
|
||||
get_relevant_span(this_fi),
|
||||
|lint| {
|
||||
let mut expected_str = DiagnosticStyledString::new();
|
||||
expected_str.push(existing_decl_ty.fn_sig(tcx).to_string(), false);
|
||||
let mut found_str = DiagnosticStyledString::new();
|
||||
found_str.push(this_decl_ty.fn_sig(tcx).to_string(), true);
|
||||
|
||||
lint.build(&format!(
|
||||
"`{}` redeclare{} with a different signature",
|
||||
this_fi.ident.name,
|
||||
if orig.get_name() == this_fi.ident.name {
|
||||
"d".to_string()
|
||||
} else {
|
||||
format!("s `{}`", orig.get_name())
|
||||
}
|
||||
))
|
||||
.span_label(
|
||||
get_relevant_span(orig_fi),
|
||||
&format!("`{}` previously declared here", orig.get_name()),
|
||||
)
|
||||
.span_label(
|
||||
get_relevant_span(this_fi),
|
||||
"this signature doesn't match the previous declaration",
|
||||
)
|
||||
.note_expected_found(&"", expected_str, &"", found_str)
|
||||
.emit()
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#![feature(bool_to_option)]
|
||||
#![feature(box_syntax)]
|
||||
#![feature(crate_visibility_modifier)]
|
||||
#![feature(iter_order_by)]
|
||||
#![feature(never_type)]
|
||||
#![feature(nll)]
|
||||
#![feature(or_patterns)]
|
||||
|
|
@ -154,6 +155,7 @@ macro_rules! late_lint_passes {
|
|||
// and change this to a module lint pass
|
||||
MissingDebugImplementations: MissingDebugImplementations::default(),
|
||||
ArrayIntoIter: ArrayIntoIter,
|
||||
ClashingExternDecl: ClashingExternDecl::new(),
|
||||
]
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -205,6 +205,7 @@ mod imp {
|
|||
#[cfg(target_arch = "aarch64")]
|
||||
extern "C" {
|
||||
fn objc_msgSend(obj: NsId, sel: Sel) -> NsId;
|
||||
#[cfg_attr(not(bootstrap), allow(clashing_extern_decl))]
|
||||
#[link_name = "objc_msgSend"]
|
||||
fn objc_msgSend_ul(obj: NsId, sel: Sel, i: libc::c_ulong) -> NsId;
|
||||
}
|
||||
|
|
@ -212,6 +213,7 @@ mod imp {
|
|||
#[cfg(not(target_arch = "aarch64"))]
|
||||
extern "C" {
|
||||
fn objc_msgSend(obj: NsId, sel: Sel, ...) -> NsId;
|
||||
#[cfg_attr(not(bootstrap), allow(clashing_extern_decl))]
|
||||
#[link_name = "objc_msgSend"]
|
||||
fn objc_msgSend_ul(obj: NsId, sel: Sel, ...) -> NsId;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// build-pass
|
||||
#![allow(dead_code)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![warn(clashing_extern_decl)]
|
||||
|
||||
// pretty-expanded FIXME #23616
|
||||
|
||||
|
|
@ -20,6 +21,7 @@ mod b {
|
|||
use super::rust_task;
|
||||
extern {
|
||||
pub fn rust_task_is_unwinding(rt: *const rust_task) -> bool;
|
||||
//~^ WARN `rust_task_is_unwinding` redeclared with a different signature
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
19
src/test/ui/issues/issue-1866.stderr
Normal file
19
src/test/ui/issues/issue-1866.stderr
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
warning: `rust_task_is_unwinding` redeclared with a different signature
|
||||
--> $DIR/issue-1866.rs:23:13
|
||||
|
|
||||
LL | pub fn rust_task_is_unwinding(rt: *const rust_task) -> bool;
|
||||
| ------------------------------------------------------------ `rust_task_is_unwinding` previously declared here
|
||||
...
|
||||
LL | pub fn rust_task_is_unwinding(rt: *const rust_task) -> bool;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/issue-1866.rs:4:9
|
||||
|
|
||||
LL | #![warn(clashing_extern_decl)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
= note: expected `unsafe extern "C" fn(*const usize) -> bool`
|
||||
found `unsafe extern "C" fn(*const bool) -> bool`
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
||||
|
|
@ -1,11 +1,13 @@
|
|||
// run-pass
|
||||
#![allow(dead_code)]
|
||||
#![warn(clashing_extern_decl)]
|
||||
// pretty-expanded FIXME #23616
|
||||
|
||||
extern {
|
||||
#[link_name = "malloc"]
|
||||
fn malloc1(len: i32) -> *const u8;
|
||||
#[link_name = "malloc"]
|
||||
//~^ WARN `malloc2` redeclares `malloc` with a different signature
|
||||
fn malloc2(len: i32, foo: i32) -> *const u8;
|
||||
}
|
||||
|
||||
|
|
|
|||
21
src/test/ui/issues/issue-5791.stderr
Normal file
21
src/test/ui/issues/issue-5791.stderr
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
warning: `malloc2` redeclares `malloc` with a different signature
|
||||
--> $DIR/issue-5791.rs:9:5
|
||||
|
|
||||
LL | / #[link_name = "malloc"]
|
||||
LL | | fn malloc1(len: i32) -> *const u8;
|
||||
| |______________________________________- `malloc` previously declared here
|
||||
LL | / #[link_name = "malloc"]
|
||||
LL | |
|
||||
LL | | fn malloc2(len: i32, foo: i32) -> *const u8;
|
||||
| |________________________________________________^ this signature doesn't match the previous declaration
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/issue-5791.rs:3:9
|
||||
|
|
||||
LL | #![warn(clashing_extern_decl)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
= note: expected `unsafe extern "C" fn(i32) -> *const u8`
|
||||
found `unsafe extern "C" fn(i32, i32) -> *const u8`
|
||||
|
||||
warning: 1 warning emitted
|
||||
|
||||
3
src/test/ui/lint/auxiliary/external_extern_fn.rs
Normal file
3
src/test/ui/lint/auxiliary/external_extern_fn.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
extern {
|
||||
pub fn extern_fn(x: u8);
|
||||
}
|
||||
159
src/test/ui/lint/clashing-extern-fn.rs
Normal file
159
src/test/ui/lint/clashing-extern-fn.rs
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
// check-pass
|
||||
// aux-build:external_extern_fn.rs
|
||||
#![crate_type = "lib"]
|
||||
#![warn(clashing_extern_decl)]
|
||||
|
||||
extern crate external_extern_fn;
|
||||
|
||||
extern {
|
||||
fn clash(x: u8);
|
||||
fn no_clash(x: u8);
|
||||
}
|
||||
|
||||
fn redeclared_different_signature() {
|
||||
extern {
|
||||
fn clash(x: u64); //~ WARN `clash` redeclared with a different signature
|
||||
}
|
||||
|
||||
unsafe {
|
||||
clash(123);
|
||||
no_clash(123);
|
||||
}
|
||||
}
|
||||
|
||||
fn redeclared_same_signature() {
|
||||
extern {
|
||||
fn no_clash(x: u8);
|
||||
}
|
||||
unsafe {
|
||||
no_clash(123);
|
||||
}
|
||||
}
|
||||
|
||||
extern {
|
||||
fn extern_fn(x: u64);
|
||||
}
|
||||
|
||||
fn extern_clash() {
|
||||
extern {
|
||||
fn extern_fn(x: u32); //~ WARN `extern_fn` redeclared with a different signature
|
||||
}
|
||||
unsafe {
|
||||
extern_fn(123);
|
||||
}
|
||||
}
|
||||
|
||||
fn extern_no_clash() {
|
||||
unsafe {
|
||||
external_extern_fn::extern_fn(123);
|
||||
crate::extern_fn(123);
|
||||
}
|
||||
}
|
||||
extern {
|
||||
fn some_other_new_name(x: i16);
|
||||
|
||||
#[link_name = "extern_link_name"]
|
||||
fn some_new_name(x: i16);
|
||||
|
||||
#[link_name = "link_name_same"]
|
||||
fn both_names_different(x: i16);
|
||||
}
|
||||
|
||||
fn link_name_clash() {
|
||||
extern {
|
||||
fn extern_link_name(x: u32);
|
||||
//~^ WARN `extern_link_name` redeclared with a different signature
|
||||
|
||||
#[link_name = "some_other_new_name"]
|
||||
//~^ WARN `some_other_extern_link_name` redeclares `some_other_new_name` with a different
|
||||
fn some_other_extern_link_name(x: u32);
|
||||
|
||||
#[link_name = "link_name_same"]
|
||||
//~^ WARN `other_both_names_different` redeclares `link_name_same` with a different
|
||||
fn other_both_names_different(x: u32);
|
||||
}
|
||||
}
|
||||
|
||||
mod a {
|
||||
extern {
|
||||
fn different_mod(x: u8);
|
||||
}
|
||||
}
|
||||
mod b {
|
||||
extern {
|
||||
fn different_mod(x: u64); //~ WARN `different_mod` redeclared with a different signature
|
||||
}
|
||||
}
|
||||
|
||||
extern {
|
||||
fn variadic_decl(x: u8, ...);
|
||||
}
|
||||
|
||||
fn variadic_clash() {
|
||||
extern {
|
||||
fn variadic_decl(x: u8); //~ WARN `variadic_decl` redeclared with a different signature
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn no_mangle_name(x: u8) { }
|
||||
|
||||
extern {
|
||||
#[link_name = "unique_link_name"]
|
||||
fn link_name_specified(x: u8);
|
||||
}
|
||||
|
||||
fn tricky_no_clash() {
|
||||
extern {
|
||||
// Shouldn't warn, because the declaration above actually declares a different symbol (and
|
||||
// Rust's name resolution rules around shadowing will handle this gracefully).
|
||||
fn link_name_specified() -> u32;
|
||||
|
||||
// The case of a no_mangle name colliding with an extern decl (see #28179) is related but
|
||||
// shouldn't be reported by ClashingExternDecl, because this is an example of unmangled
|
||||
// name clash causing bad behaviour in functions with a defined body.
|
||||
fn no_mangle_name() -> u32;
|
||||
}
|
||||
}
|
||||
|
||||
mod banana {
|
||||
mod one {
|
||||
#[repr(C)] struct Banana { weight: u32, length: u16 }
|
||||
extern "C" { fn weigh_banana(count: *const Banana) -> u64; }
|
||||
}
|
||||
|
||||
mod two {
|
||||
#[repr(C)] struct Banana { weight: u32, length: u16 } // note: distinct type
|
||||
// This should not trigger the lint because two::Banana is structurally equivalent to
|
||||
// one::Banana.
|
||||
extern "C" { fn weigh_banana(count: *const Banana) -> u64; }
|
||||
}
|
||||
|
||||
mod three {
|
||||
// This _should_ trigger the lint, because repr(packed) should generate a struct that has a
|
||||
// different layout.
|
||||
#[repr(packed)] struct Banana { weight: u32, length: u16 }
|
||||
#[allow(improper_ctypes)]
|
||||
extern "C" { fn weigh_banana(count: *const Banana) -> u64; }
|
||||
//~^ WARN `weigh_banana` redeclared with a different signature
|
||||
}
|
||||
}
|
||||
|
||||
mod sameish_members {
|
||||
mod a {
|
||||
#[repr(C)]
|
||||
struct Point { x: i16, y: i16 }
|
||||
|
||||
extern "C" { fn draw_point(p: Point); }
|
||||
}
|
||||
mod b {
|
||||
#[repr(C)]
|
||||
struct Point { coordinates: [i16; 2] }
|
||||
|
||||
// It's possible we are overconservative for this case, as accessing the elements of the
|
||||
// coordinates array might end up correctly accessing `.x` and `.y`. However, this may not
|
||||
// always be the case, for every architecture and situation. This is also a really odd
|
||||
// thing to do anyway.
|
||||
extern "C" { fn draw_point(p: Point); } //~ WARN `draw_point` redeclared with a different
|
||||
}
|
||||
}
|
||||
121
src/test/ui/lint/clashing-extern-fn.stderr
Normal file
121
src/test/ui/lint/clashing-extern-fn.stderr
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
warning: `clash` redeclared with a different signature
|
||||
--> $DIR/clashing-extern-fn.rs:15:9
|
||||
|
|
||||
LL | fn clash(x: u8);
|
||||
| ---------------- `clash` previously declared here
|
||||
...
|
||||
LL | fn clash(x: u64);
|
||||
| ^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/clashing-extern-fn.rs:4:9
|
||||
|
|
||||
LL | #![warn(clashing_extern_decl)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
= note: expected `unsafe extern "C" fn(u8)`
|
||||
found `unsafe extern "C" fn(u64)`
|
||||
|
||||
warning: `extern_fn` redeclared with a different signature
|
||||
--> $DIR/clashing-extern-fn.rs:39:9
|
||||
|
|
||||
LL | fn extern_fn(x: u64);
|
||||
| --------------------- `extern_fn` previously declared here
|
||||
...
|
||||
LL | fn extern_fn(x: u32);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
|
||||
|
|
||||
= note: expected `unsafe extern "C" fn(u64)`
|
||||
found `unsafe extern "C" fn(u32)`
|
||||
|
||||
warning: `extern_link_name` redeclared with a different signature
|
||||
--> $DIR/clashing-extern-fn.rs:64:9
|
||||
|
|
||||
LL | / #[link_name = "extern_link_name"]
|
||||
LL | | fn some_new_name(x: i16);
|
||||
| |_____________________________- `extern_link_name` previously declared here
|
||||
...
|
||||
LL | fn extern_link_name(x: u32);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
|
||||
|
|
||||
= note: expected `unsafe extern "C" fn(i16)`
|
||||
found `unsafe extern "C" fn(u32)`
|
||||
|
||||
warning: `some_other_extern_link_name` redeclares `some_other_new_name` with a different signature
|
||||
--> $DIR/clashing-extern-fn.rs:67:9
|
||||
|
|
||||
LL | fn some_other_new_name(x: i16);
|
||||
| ------------------------------- `some_other_new_name` previously declared here
|
||||
...
|
||||
LL | / #[link_name = "some_other_new_name"]
|
||||
LL | |
|
||||
LL | | fn some_other_extern_link_name(x: u32);
|
||||
| |_______________________________________________^ this signature doesn't match the previous declaration
|
||||
|
|
||||
= note: expected `unsafe extern "C" fn(i16)`
|
||||
found `unsafe extern "C" fn(u32)`
|
||||
|
||||
warning: `other_both_names_different` redeclares `link_name_same` with a different signature
|
||||
--> $DIR/clashing-extern-fn.rs:71:9
|
||||
|
|
||||
LL | / #[link_name = "link_name_same"]
|
||||
LL | | fn both_names_different(x: i16);
|
||||
| |____________________________________- `link_name_same` previously declared here
|
||||
...
|
||||
LL | / #[link_name = "link_name_same"]
|
||||
LL | |
|
||||
LL | | fn other_both_names_different(x: u32);
|
||||
| |______________________________________________^ this signature doesn't match the previous declaration
|
||||
|
|
||||
= note: expected `unsafe extern "C" fn(i16)`
|
||||
found `unsafe extern "C" fn(u32)`
|
||||
|
||||
warning: `different_mod` redeclared with a different signature
|
||||
--> $DIR/clashing-extern-fn.rs:84:9
|
||||
|
|
||||
LL | fn different_mod(x: u8);
|
||||
| ------------------------ `different_mod` previously declared here
|
||||
...
|
||||
LL | fn different_mod(x: u64);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
|
||||
|
|
||||
= note: expected `unsafe extern "C" fn(u8)`
|
||||
found `unsafe extern "C" fn(u64)`
|
||||
|
||||
warning: `variadic_decl` redeclared with a different signature
|
||||
--> $DIR/clashing-extern-fn.rs:94:9
|
||||
|
|
||||
LL | fn variadic_decl(x: u8, ...);
|
||||
| ----------------------------- `variadic_decl` previously declared here
|
||||
...
|
||||
LL | fn variadic_decl(x: u8);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
|
||||
|
|
||||
= note: expected `unsafe extern "C" fn(u8, ...)`
|
||||
found `unsafe extern "C" fn(u8)`
|
||||
|
||||
warning: `weigh_banana` redeclared with a different signature
|
||||
--> $DIR/clashing-extern-fn.rs:137:22
|
||||
|
|
||||
LL | extern "C" { fn weigh_banana(count: *const Banana) -> u64; }
|
||||
| --------------------------------------------- `weigh_banana` previously declared here
|
||||
...
|
||||
LL | extern "C" { fn weigh_banana(count: *const Banana) -> u64; }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
|
||||
|
|
||||
= note: expected `unsafe extern "C" fn(*const banana::one::Banana) -> u64`
|
||||
found `unsafe extern "C" fn(*const banana::three::Banana) -> u64`
|
||||
|
||||
warning: `draw_point` redeclared with a different signature
|
||||
--> $DIR/clashing-extern-fn.rs:157:22
|
||||
|
|
||||
LL | extern "C" { fn draw_point(p: Point); }
|
||||
| ------------------------ `draw_point` previously declared here
|
||||
...
|
||||
LL | extern "C" { fn draw_point(p: Point); }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
|
||||
|
|
||||
= note: expected `unsafe extern "C" fn(sameish_members::a::Point)`
|
||||
found `unsafe extern "C" fn(sameish_members::b::Point)`
|
||||
|
||||
warning: 9 warnings emitted
|
||||
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
#![allow(unused_variables)]
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(clashing_extern_decl)]
|
||||
#![deny(dead_code)]
|
||||
|
||||
#![crate_type="lib"]
|
||||
|
|
|
|||
|
|
@ -1,35 +1,35 @@
|
|||
error: struct is never constructed: `Foo`
|
||||
--> $DIR/lint-dead-code-3.rs:13:8
|
||||
--> $DIR/lint-dead-code-3.rs:14:8
|
||||
|
|
||||
LL | struct Foo;
|
||||
| ^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/lint-dead-code-3.rs:3:9
|
||||
--> $DIR/lint-dead-code-3.rs:4:9
|
||||
|
|
||||
LL | #![deny(dead_code)]
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: associated function is never used: `foo`
|
||||
--> $DIR/lint-dead-code-3.rs:15:8
|
||||
--> $DIR/lint-dead-code-3.rs:16:8
|
||||
|
|
||||
LL | fn foo(&self) {
|
||||
| ^^^
|
||||
|
||||
error: function is never used: `bar`
|
||||
--> $DIR/lint-dead-code-3.rs:20:4
|
||||
--> $DIR/lint-dead-code-3.rs:21:4
|
||||
|
|
||||
LL | fn bar() {
|
||||
| ^^^
|
||||
|
||||
error: enum is never used: `c_void`
|
||||
--> $DIR/lint-dead-code-3.rs:59:6
|
||||
--> $DIR/lint-dead-code-3.rs:60:6
|
||||
|
|
||||
LL | enum c_void {}
|
||||
| ^^^^^^
|
||||
|
||||
error: function is never used: `free`
|
||||
--> $DIR/lint-dead-code-3.rs:61:5
|
||||
--> $DIR/lint-dead-code-3.rs:62:5
|
||||
|
|
||||
LL | fn free(p: *const c_void);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#![allow(clashing_extern_decl)]
|
||||
// check-pass
|
||||
|
||||
// In this test we check that the parser accepts an ABI string when it
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue