New lint: relative_path_in_macro_definition · Issue #14472 · rust-lang/rust-clippy (original) (raw)

What it does

Check for relative paths (such as core::ops::BitOr) being used in macros.

When writing declarative macros, one should always refer to items using their absolute path (paths starting with :: or using $crate or be keywords such as Self). That is because users might declare a module with the same name as the first component of the path and thus change the meaning of the macro.

Advantage

This change prevents macros from changing their meaning depending on the context:

macro_rules! derive_bit_or { ($t:ident) => { impl core::ops::BitOr for $t { type Output = Self; fn bitor(self, rhs: Self) -> Self { Self(self.0 | rhs.0) } } } }

mod bar { struct Bar(u32); derive_bit_or!(Bar); mod core {} }

This code fails to compile, because the path core::ops::BitOr is relative and thus the custom core module defined in mod bar is used instead of the core crate.

Drawbacks

Increase verbosity and thus decrease readability of declarative macros.

Example

macro_rules! derive_bit_or { ($t:ident) => { impl core::ops::BitOr for $t { type Output = Self; fn bitor(self, rhs: Self) -> Self { Self(self.0 | rhs.0) } } } }

Could be written as:

macro_rules! derive_bit_or { ($t:ident) => { impl ::core::ops::BitOr for $t { type Output = Self; fn bitor(self, rhs: Self) -> Self { Self(self.0 | rhs.0) } } } }