Skip to content

Commit 9943712

Browse files
committed
Add lint warn about clashing function names with fundamental functions
1 parent 9642c0e commit 9943712

File tree

9 files changed

+180
-17
lines changed

9 files changed

+180
-17
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4042,6 +4042,7 @@ dependencies = [
40424042
"rustc_parse_format",
40434043
"rustc_session",
40444044
"rustc_span",
4045+
"rustc_symbol_mangling",
40454046
"rustc_target",
40464047
"rustc_trait_selection",
40474048
"smallvec",

compiler/rustc_lint/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ rustc_middle = { path = "../rustc_middle" }
2222
rustc_parse_format = { path = "../rustc_parse_format" }
2323
rustc_session = { path = "../rustc_session" }
2424
rustc_span = { path = "../rustc_span" }
25+
rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }
2526
rustc_target = { path = "../rustc_target" }
2627
rustc_trait_selection = { path = "../rustc_trait_selection" }
2728
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }

compiler/rustc_lint/messages.ftl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,11 @@ lint_cfg_attr_no_attributes =
195195
196196
lint_check_name_unknown_tool = unknown lint tool: `{$tool_name}`
197197
198+
lint_clashing_function_names_with_fundamental_functions = this function symbol name `{$symbol_name}` clashes with the fundamental functions expected with `core` and `std`
199+
.match_exactly = extra care must be taken when exposing a function with those symbol names, they must match exactly (ABI, function arguments, function return type, behavior, ...)
200+
.learn_more = see <https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library> for the more details
201+
.help = remove any `#[unsafe(no_mangle)]`, `#[unsafe(link_name = "{$symbol_name}")]` or `#[unsafe(export_name = "{$symbol_name}")]` if present
202+
198203
lint_closure_returning_async_block = closure returning async block can be made into an async closure
199204
.label = this async block can be removed, and the closure can be turned into an async closure
200205
.suggestion = turn this into an async closure
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
use rustc_hir as hir;
2+
use rustc_session::{declare_lint, declare_lint_pass};
3+
4+
use crate::lints::ClashingFunctionNamesWithFundamentalFunctions;
5+
use crate::{LateContext, LateLintPass, LintContext};
6+
7+
declare_lint! {
8+
/// The `clashing_function_names_with_fundamental_functions` lint checks for function
9+
/// name whose name clash with a fundamental functions expected by `core` and `std`.
10+
///
11+
/// ### Example
12+
///
13+
/// ```rust,no_run
14+
/// #[undafe(no_mangle)]
15+
/// pub fn strlen() {} // clash with the libc `strlen` function
16+
/// // care must be taken when implementing this function
17+
/// ```
18+
///
19+
/// {{produces}}
20+
///
21+
/// ### Explanation
22+
///
23+
/// FIXME: Document this.
24+
pub CLASHING_FUNCTION_NAMES_WITH_FUNDAMENTAL_FUNCTIONS,
25+
Warn,
26+
"using a function name that clashes with fundamental function names"
27+
}
28+
29+
declare_lint_pass!(FundamentalFunctions => [CLASHING_FUNCTION_NAMES_WITH_FUNDAMENTAL_FUNCTIONS]);
30+
31+
static CORE_FUNDAMENTAL_FUNCTION_NAMES: &[&str] =
32+
&["memcpy", "memmove", "memset", "memcmp", "bcmp", "strlen"];
33+
34+
static STD_FUNDAMENTAL_FUNCTION_NAMES: &[&str] = &["open", "read", "write", "close"];
35+
36+
impl<'tcx> LateLintPass<'tcx> for FundamentalFunctions {
37+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
38+
let hir::ItemKind::Fn { sig: _, ident: _, generics: _, body: _, has_body: true } =
39+
item.kind
40+
else {
41+
return;
42+
};
43+
44+
let Some(symbol_name) = rustc_symbol_mangling::symbol_name_without_mangling(
45+
cx.tcx,
46+
rustc_middle::ty::InstanceKind::Item(item.owner_id.to_def_id()),
47+
) else {
48+
return;
49+
};
50+
51+
if CORE_FUNDAMENTAL_FUNCTION_NAMES.contains(&&*symbol_name)
52+
|| STD_FUNDAMENTAL_FUNCTION_NAMES.contains(&&*symbol_name)
53+
{
54+
cx.emit_span_lint(
55+
CLASHING_FUNCTION_NAMES_WITH_FUNDAMENTAL_FUNCTIONS,
56+
item.span,
57+
ClashingFunctionNamesWithFundamentalFunctions { symbol_name },
58+
);
59+
}
60+
}
61+
}

compiler/rustc_lint/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ mod errors;
4848
mod expect;
4949
mod for_loops_over_fallibles;
5050
mod foreign_modules;
51+
mod fundamental_functions;
5152
mod if_let_rescope;
5253
mod impl_trait_overcaptures;
5354
mod internal;
@@ -92,6 +93,7 @@ use deref_into_dyn_supertrait::*;
9293
use drop_forget_useless::*;
9394
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
9495
use for_loops_over_fallibles::*;
96+
use fundamental_functions::*;
9597
use if_let_rescope::IfLetRescope;
9698
use impl_trait_overcaptures::ImplTraitOvercaptures;
9799
use internal::*;
@@ -240,6 +242,7 @@ late_lint_methods!(
240242
AsyncClosureUsage: AsyncClosureUsage,
241243
AsyncFnInTrait: AsyncFnInTrait,
242244
NonLocalDefinitions: NonLocalDefinitions::default(),
245+
FundamentalFunctions: FundamentalFunctions,
243246
ImplTraitOvercaptures: ImplTraitOvercaptures,
244247
IfLetRescope: IfLetRescope::default(),
245248
StaticMutRefs: StaticMutRefs,

compiler/rustc_lint/src/lints.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,16 @@ pub(crate) enum UseLetUnderscoreIgnoreSuggestion {
705705
},
706706
}
707707

708+
// fundamental_functions.rs
709+
#[derive(LintDiagnostic)]
710+
#[diag(lint_clashing_function_names_with_fundamental_functions)]
711+
#[note(lint_match_exactly)]
712+
#[note(lint_learn_more)]
713+
#[help]
714+
pub(crate) struct ClashingFunctionNamesWithFundamentalFunctions {
715+
pub symbol_name: String,
716+
}
717+
708718
// drop_forget_useless.rs
709719
#[derive(LintDiagnostic)]
710720
#[diag(lint_dropping_references)]

compiler/rustc_symbol_mangling/src/lib.rs

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
100100
use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
101101
use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
102102
use rustc_middle::query::Providers;
103-
use rustc_middle::ty::{self, Instance, TyCtxt};
103+
use rustc_middle::ty::{self, Instance, InstanceKind, TyCtxt};
104104
use rustc_session::config::SymbolManglingVersion;
105105
use tracing::debug;
106106

@@ -158,29 +158,22 @@ pub fn typeid_for_trait_ref<'tcx>(
158158
v0::mangle_typeid_for_trait_ref(tcx, trait_ref)
159159
}
160160

161-
/// Computes the symbol name for the given instance. This function will call
162-
/// `compute_instantiating_crate` if it needs to factor the instantiating crate
163-
/// into the symbol name.
164-
fn compute_symbol_name<'tcx>(
161+
pub fn symbol_name_without_mangling<'tcx>(
165162
tcx: TyCtxt<'tcx>,
166-
instance: Instance<'tcx>,
167-
compute_instantiating_crate: impl FnOnce() -> CrateNum,
168-
) -> String {
169-
let def_id = instance.def_id();
170-
let args = instance.args;
171-
172-
debug!("symbol_name(def_id={:?}, args={:?})", def_id, args);
163+
instance_kind: InstanceKind<'tcx>,
164+
) -> Option<String> {
165+
let def_id = instance_kind.def_id();
173166

174167
if let Some(def_id) = def_id.as_local() {
175168
if tcx.proc_macro_decls_static(()) == Some(def_id) {
176169
let stable_crate_id = tcx.stable_crate_id(LOCAL_CRATE);
177-
return tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id);
170+
return Some(tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id));
178171
}
179172
}
180173

181174
// FIXME(eddyb) Precompute a custom symbol name based on attributes.
182175
let attrs = if tcx.def_kind(def_id).has_codegen_attrs() {
183-
&tcx.codegen_instance_attrs(instance.def)
176+
&tcx.codegen_instance_attrs(instance_kind)
184177
} else {
185178
CodegenFnAttrs::EMPTY
186179
};
@@ -206,7 +199,7 @@ fn compute_symbol_name<'tcx>(
206199
// legacy symbol mangling scheme.
207200
let name = if let Some(name) = attrs.symbol_name { name } else { tcx.item_name(def_id) };
208201

209-
return v0::mangle_internal_symbol(tcx, name.as_str());
202+
return Some(v0::mangle_internal_symbol(tcx, name.as_str()));
210203
}
211204

212205
let wasm_import_module_exception_force_mangling = {
@@ -234,15 +227,35 @@ fn compute_symbol_name<'tcx>(
234227
if !wasm_import_module_exception_force_mangling {
235228
if let Some(name) = attrs.symbol_name {
236229
// Use provided name
237-
return name.to_string();
230+
return Some(name.to_string());
238231
}
239232

240233
if attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE) {
241234
// Don't mangle
242-
return tcx.item_name(def_id).to_string();
235+
return Some(tcx.item_name(def_id).to_string());
243236
}
244237
}
245238

239+
None
240+
}
241+
242+
/// Computes the symbol name for the given instance. This function will call
243+
/// `compute_instantiating_crate` if it needs to factor the instantiating crate
244+
/// into the symbol name.
245+
fn compute_symbol_name<'tcx>(
246+
tcx: TyCtxt<'tcx>,
247+
instance: Instance<'tcx>,
248+
compute_instantiating_crate: impl FnOnce() -> CrateNum,
249+
) -> String {
250+
let def_id = instance.def_id();
251+
let args = instance.args;
252+
253+
debug!("symbol_name(def_id={:?}, args={:?})", def_id, args);
254+
255+
if let Some(symbol) = symbol_name_without_mangling(tcx, instance.def) {
256+
return symbol;
257+
}
258+
246259
// If we're dealing with an instance of a function that's inlined from
247260
// another crate but we're marking it as globally shared to our
248261
// compilation (aka we're not making an internal copy in each of our
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//@ check-pass
2+
//@ edition: 2021
3+
4+
#[no_mangle]
5+
pub fn strlen() {}
6+
//~^ WARN `strlen` clashes
7+
8+
#[no_mangle]
9+
pub fn open() {}
10+
//~^ WARN `open` clashes
11+
12+
#[export_name = "write"]
13+
pub fn write1() {}
14+
//~^ WARN `write` clashes
15+
16+
extern "C" {
17+
pub fn close(a: i32) -> i32;
18+
}
19+
20+
#[export_name = "read"]
21+
pub async fn read1() {}
22+
//~^ WARN `read` clashes
23+
24+
// FIXME: Add more fundamental functions
25+
26+
fn main() {}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
warning: this function symbol name `strlen` clashes with the fundamental functions expected with `core` and `std`
2+
--> $DIR/clashing-fn-names-with-fundamental-functions.rs:5:1
3+
|
4+
LL | pub fn strlen() {}
5+
| ^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: extra care must be taken when exposing a function with those symbol names, they must match exactly (ABI, function arguments, function return type, behavior, ...)
8+
= note: see <https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library> for the more details
9+
= help: remove any `#[unsafe(no_mangle)]`, `#[unsafe(link_name = "strlen")]` or `#[unsafe(export_name = "strlen")]` if present
10+
= note: `#[warn(clashing_function_names_with_fundamental_functions)]` on by default
11+
12+
warning: this function symbol name `open` clashes with the fundamental functions expected with `core` and `std`
13+
--> $DIR/clashing-fn-names-with-fundamental-functions.rs:9:1
14+
|
15+
LL | pub fn open() {}
16+
| ^^^^^^^^^^^^^^^^
17+
|
18+
= note: extra care must be taken when exposing a function with those symbol names, they must match exactly (ABI, function arguments, function return type, behavior, ...)
19+
= note: see <https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library> for the more details
20+
= help: remove any `#[unsafe(no_mangle)]`, `#[unsafe(link_name = "open")]` or `#[unsafe(export_name = "open")]` if present
21+
22+
warning: this function symbol name `write` clashes with the fundamental functions expected with `core` and `std`
23+
--> $DIR/clashing-fn-names-with-fundamental-functions.rs:13:1
24+
|
25+
LL | pub fn write1() {}
26+
| ^^^^^^^^^^^^^^^^^^
27+
|
28+
= note: extra care must be taken when exposing a function with those symbol names, they must match exactly (ABI, function arguments, function return type, behavior, ...)
29+
= note: see <https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library> for the more details
30+
= help: remove any `#[unsafe(no_mangle)]`, `#[unsafe(link_name = "write")]` or `#[unsafe(export_name = "write")]` if present
31+
32+
warning: this function symbol name `read` clashes with the fundamental functions expected with `core` and `std`
33+
--> $DIR/clashing-fn-names-with-fundamental-functions.rs:21:1
34+
|
35+
LL | pub async fn read1() {}
36+
| ^^^^^^^^^^^^^^^^^^^^^^^
37+
|
38+
= note: extra care must be taken when exposing a function with those symbol names, they must match exactly (ABI, function arguments, function return type, behavior, ...)
39+
= note: see <https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library> for the more details
40+
= help: remove any `#[unsafe(no_mangle)]`, `#[unsafe(link_name = "read")]` or `#[unsafe(export_name = "read")]` if present
41+
42+
warning: 4 warnings emitted
43+

0 commit comments

Comments
 (0)