2022-04-07 12:39:59 -05:00
|
|
|
use clippy_utils::diagnostics::span_lint_and_sugg;
|
|
|
|
use rustc_ast::ast::{AttrKind, Attribute, Item, ItemKind};
|
|
|
|
use rustc_ast::token::{Token, TokenKind};
|
|
|
|
use rustc_ast::tokenstream::{TokenStream, TokenTree};
|
|
|
|
use rustc_errors::Applicability;
|
|
|
|
use rustc_lint::{EarlyContext, EarlyLintPass};
|
2023-12-01 11:21:58 -06:00
|
|
|
use rustc_session::declare_lint_pass;
|
2023-07-17 03:19:29 -05:00
|
|
|
use rustc_span::symbol::sym;
|
|
|
|
use rustc_span::Span;
|
2022-04-07 12:39:59 -05:00
|
|
|
|
|
|
|
declare_clippy_lint! {
|
|
|
|
/// ### What it does
|
2023-04-23 06:03:09 -05:00
|
|
|
/// Checks for usage of `crate` as opposed to `$crate` in a macro definition.
|
2022-04-07 12:39:59 -05:00
|
|
|
///
|
|
|
|
/// ### Why is this bad?
|
|
|
|
/// `crate` refers to the macro call's crate, whereas `$crate` refers to the macro definition's
|
|
|
|
/// crate. Rarely is the former intended. See:
|
|
|
|
/// https://doc.rust-lang.org/reference/macros-by-example.html#hygiene
|
|
|
|
///
|
|
|
|
/// ### Example
|
2023-11-02 11:35:56 -05:00
|
|
|
/// ```no_run
|
2022-04-07 12:39:59 -05:00
|
|
|
/// #[macro_export]
|
|
|
|
/// macro_rules! print_message {
|
|
|
|
/// () => {
|
|
|
|
/// println!("{}", crate::MESSAGE);
|
|
|
|
/// };
|
|
|
|
/// }
|
|
|
|
/// pub const MESSAGE: &str = "Hello!";
|
|
|
|
/// ```
|
|
|
|
/// Use instead:
|
2023-11-02 11:35:56 -05:00
|
|
|
/// ```no_run
|
2022-04-07 12:39:59 -05:00
|
|
|
/// #[macro_export]
|
|
|
|
/// macro_rules! print_message {
|
|
|
|
/// () => {
|
|
|
|
/// println!("{}", $crate::MESSAGE);
|
|
|
|
/// };
|
|
|
|
/// }
|
|
|
|
/// pub const MESSAGE: &str = "Hello!";
|
|
|
|
/// ```
|
|
|
|
///
|
|
|
|
/// Note that if the use of `crate` is intentional, an `allow` attribute can be applied to the
|
|
|
|
/// macro definition, e.g.:
|
|
|
|
/// ```rust,ignore
|
|
|
|
/// #[allow(clippy::crate_in_macro_def)]
|
|
|
|
/// macro_rules! ok { ... crate::foo ... }
|
|
|
|
/// ```
|
2022-07-18 02:39:37 -05:00
|
|
|
#[clippy::version = "1.62.0"]
|
2022-04-07 12:39:59 -05:00
|
|
|
pub CRATE_IN_MACRO_DEF,
|
|
|
|
suspicious,
|
|
|
|
"using `crate` in a macro definition"
|
|
|
|
}
|
|
|
|
declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]);
|
|
|
|
|
|
|
|
impl EarlyLintPass for CrateInMacroDef {
|
|
|
|
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
|
2023-11-16 12:13:24 -06:00
|
|
|
if item.attrs.iter().any(is_macro_export)
|
|
|
|
&& let ItemKind::MacroDef(macro_def) = &item.kind
|
|
|
|
&& let tts = macro_def.body.tokens.clone()
|
|
|
|
&& let Some(span) = contains_unhygienic_crate_reference(&tts)
|
|
|
|
{
|
|
|
|
span_lint_and_sugg(
|
|
|
|
cx,
|
|
|
|
CRATE_IN_MACRO_DEF,
|
|
|
|
span,
|
|
|
|
"`crate` references the macro call's crate",
|
|
|
|
"to reference the macro definition's crate, use",
|
|
|
|
String::from("$crate"),
|
|
|
|
Applicability::MachineApplicable,
|
|
|
|
);
|
2022-04-07 12:39:59 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_macro_export(attr: &Attribute) -> bool {
|
2023-11-16 12:13:24 -06:00
|
|
|
if let AttrKind::Normal(normal) = &attr.kind
|
|
|
|
&& let [segment] = normal.item.path.segments.as_slice()
|
|
|
|
{
|
|
|
|
segment.ident.name == sym::macro_export
|
|
|
|
} else {
|
|
|
|
false
|
2022-04-07 12:39:59 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option<Span> {
|
|
|
|
let mut prev_is_dollar = false;
|
|
|
|
let mut cursor = tts.trees();
|
|
|
|
while let Some(curr) = cursor.next() {
|
2023-11-16 12:13:24 -06:00
|
|
|
if !prev_is_dollar
|
|
|
|
&& let Some(span) = is_crate_keyword(curr)
|
|
|
|
&& let Some(next) = cursor.look_ahead(0)
|
|
|
|
&& is_token(next, &TokenKind::ModSep)
|
|
|
|
{
|
|
|
|
return Some(span);
|
2022-04-07 12:39:59 -05:00
|
|
|
}
|
2023-10-11 23:36:14 -05:00
|
|
|
if let TokenTree::Delimited(.., tts) = &curr {
|
2022-04-07 12:39:59 -05:00
|
|
|
let span = contains_unhygienic_crate_reference(tts);
|
|
|
|
if span.is_some() {
|
|
|
|
return span;
|
|
|
|
}
|
|
|
|
}
|
2022-06-04 06:34:07 -05:00
|
|
|
prev_is_dollar = is_token(curr, &TokenKind::Dollar);
|
2022-04-07 12:39:59 -05:00
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_crate_keyword(tt: &TokenTree) -> Option<Span> {
|
2023-11-16 12:13:24 -06:00
|
|
|
if let TokenTree::Token(
|
|
|
|
Token {
|
|
|
|
kind: TokenKind::Ident(symbol, _),
|
|
|
|
span,
|
|
|
|
},
|
|
|
|
_,
|
|
|
|
) = tt
|
|
|
|
&& symbol.as_str() == "crate"
|
|
|
|
{
|
|
|
|
Some(*span)
|
|
|
|
} else {
|
|
|
|
None
|
2022-04-07 12:39:59 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_token(tt: &TokenTree, kind: &TokenKind) -> bool {
|
Remove `TreeAndSpacing`.
A `TokenStream` contains a `Lrc<Vec<(TokenTree, Spacing)>>`. But this is
not quite right. `Spacing` makes sense for `TokenTree::Token`, but does
not make sense for `TokenTree::Delimited`, because a
`TokenTree::Delimited` cannot be joined with another `TokenTree`.
This commit fixes this problem, by adding `Spacing` to `TokenTree::Token`,
changing `TokenStream` to contain a `Lrc<Vec<TokenTree>>`, and removing the
`TreeAndSpacing` typedef.
The commit removes these two impls:
- `impl From<TokenTree> for TokenStream`
- `impl From<TokenTree> for TreeAndSpacing`
These were useful, but also resulted in code with many `.into()` calls
that was hard to read, particularly for anyone not highly familiar with
the relevant types. This commit makes some other changes to compensate:
- `TokenTree::token()` becomes `TokenTree::token_{alone,joint}()`.
- `TokenStream::token_{alone,joint}()` are added.
- `TokenStream::delimited` is added.
This results in things like this:
```rust
TokenTree::token(token::Semi, stmt.span).into()
```
changing to this:
```rust
TokenStream::token_alone(token::Semi, stmt.span)
```
This makes the type of the result, and its spacing, clearer.
These changes also simplifies `Cursor` and `CursorRef`, because they no longer
need to distinguish between `next` and `next_with_spacing`.
2022-07-27 19:31:04 -05:00
|
|
|
if let TokenTree::Token(Token { kind: other, .. }, _) = tt {
|
2022-04-07 12:39:59 -05:00
|
|
|
kind == other
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|