Skip to content

#[no_link] attribute does not prevent crate linking #152854

@martonmoro

Description

@martonmoro

View all comments

Problem: #[no_link] does not prevent an extern crate from being linked.

Steps to reproduce:

macro_crate/Cargo.toml:

[package]
name = "macro_crate"
version = "0.1.0"
edition = "2021"

macro_crate/src/lib.rs:

#[macro_export]
macro_rules! square {
    ($x:expr) => {
        $x * $x
    };
}

pub fn add(x: i32, y: i32) -> i32 {
    x + y
}

Cargo.toml:

[package]
name = "no_link_bug"
version = "0.1.0"
edition = "2024"

[dependencies]
macro_crate = { path = "../macro_crate" }

src/main.rs:

#[no_link]
extern crate macro_crate;

fn main() {
    let num = 4;
    let squared = macro_crate::square!(num);
    println!("{} squared is {}", num, squared);

    let sum = macro_crate::add(1, 2);
    println!("1 + 2 = {}", sum);
}

Expected behavior:
The build should fail with a linker error, for example:

rust-lld: error: undefined symbol: macro_crate::add::hd706c5be871dff6f

Actual behavior:
The build succeeds and produces a binary.

Meta:

rustc 1.95.0-nightly (c04308580 2026-02-18)
binary: rustc
commit-hash: c043085801b7a884054add21a94882216df5971c
commit-date: 2026-02-18
host: x86_64-unknown-linux-gnu
release: 1.95.0-nightly
LLVM version: 22.1.0

cargo 1.95.0-nightly (ce69df6f7 2026-02-12)

Linux x86_64

Verbose build output:

Verbose output
RUST_BACKTRACE=1 cargo build -vv
Compiling macro_crate v0.1.0 (/home/mmoro/Projects/no_link_bug/macro_crate)
     Running `CARGO=/home/mmoro/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo CARGO_CRATE_NAME=macro_crate CARGO_MANIFEST_DIR=/home/mmoro/Projects/no_link_bug/macro_crate CARGO_MANIFEST_PATH=/home/mmoro/Projects/no_link_bug/macro_crate/Cargo.toml CARGO_PKG_AUTHORS='' CARGO_PKG_DESCRIPTION='' CARGO_PKG_HOMEPAGE='' CARGO_PKG_LICENSE='' CARGO_PKG_LICENSE_FILE='' CARGO_PKG_NAME=macro_crate CARGO_PKG_README='' CARGO_PKG_REPOSITORY='' CARGO_PKG_RUST_VERSION='' CARGO_PKG_VERSION=0.1.0 CARGO_PKG_VERSION_MAJOR=0 CARGO_PKG_VERSION_MINOR=1 CARGO_PKG_VERSION_PATCH=0 CARGO_PKG_VERSION_PRE='' LD_LIBRARY_PATH='/home/mmoro/Projects/no_link_bug/target/debug/deps:/home/mmoro/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib' /home/mmoro/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc --crate-name macro_crate --edition=2021 macro_crate/src/lib.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --diagnostic-width=192 --crate-type rlib --emit=dep-info,metadata,link -C embed-bitcode=no -C debuginfo=2 --check-cfg 'cfg(docsrs,test)' --check-cfg 'cfg(feature, values())' -C metadata=1ffd961baa1102b0 -C extra-filename=-c0993a15f8871f25 --out-dir /home/mmoro/Projects/no_link_bug/target/debug/deps -C incremental=/home/mmoro/Projects/no_link_bug/target/debug/incremental -L dependency=/home/mmoro/Projects/no_link_bug/target/debug/deps`
   Compiling no_link_bug v0.1.0 (/home/mmoro/Projects/no_link_bug)
     Running `CARGO=/home/mmoro/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo CARGO_BIN_NAME=no_link_bug CARGO_CRATE_NAME=no_link_bug CARGO_MANIFEST_DIR=/home/mmoro/Projects/no_link_bug CARGO_MANIFEST_PATH=/home/mmoro/Projects/no_link_bug/Cargo.toml CARGO_PKG_AUTHORS='' CARGO_PKG_DESCRIPTION='' CARGO_PKG_HOMEPAGE='' CARGO_PKG_LICENSE='' CARGO_PKG_LICENSE_FILE='' CARGO_PKG_NAME=no_link_bug CARGO_PKG_README='' CARGO_PKG_REPOSITORY='' CARGO_PKG_RUST_VERSION='' CARGO_PKG_VERSION=0.1.0 CARGO_PKG_VERSION_MAJOR=0 CARGO_PKG_VERSION_MINOR=1 CARGO_PKG_VERSION_PATCH=0 CARGO_PKG_VERSION_PRE='' CARGO_PRIMARY_PACKAGE=1 LD_LIBRARY_PATH='/home/mmoro/Projects/no_link_bug/target/debug/deps:/home/mmoro/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib' /home/mmoro/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc --crate-name no_link_bug --edition=2024 src/main.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --diagnostic-width=192 --crate-type bin --emit=dep-info,link -C embed-bitcode=no -C debuginfo=2 --check-cfg 'cfg(docsrs,test)' --check-cfg 'cfg(feature, values())' -C metadata=6bf287f543d40f7f -C extra-filename=-838203c1bd20a69b --out-dir /home/mmoro/Projects/no_link_bug/target/debug/deps -C incremental=/home/mmoro/Projects/no_link_bug/target/debug/incremental -L dependency=/home/mmoro/Projects/no_link_bug/target/debug/deps --extern macro_crate=/home/mmoro/Projects/no_link_bug/target/debug/deps/libmacro_crate-c0993a15f8871f25.rlib`
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.25s

My thoughts:

When rustc is invoked with --extern macro_crate=…/*.rlib, the crate is initially loaded as Unconditional. Later, when the compiler sees #[no_link] extern crate macro_crate, it attempts to downgrade it to MacrosOnly. However, maybe_resolve_crate uses cmp::max when updating the crate’s dep_kind, so the Unconditional status is retained. As a result, the crate is still linked, and functions like macro_crate::add are available, causing the build to succeed rather than produce a linker error.

Metadata

Metadata

Assignees

Labels

A-linkageArea: linking into static, shared libraries and binariesC-bugCategory: This is a bug.I-prioritizeIssue: Indicates that prioritization has been requested for this issue.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.regression-from-stable-to-stablePerformance or correctness regression from one stable version to another.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions