#[macro_use]
extern crate rustc_macros;
pub use self::Level::*;
use rustc_ast::node_id::{NodeId, NodeMap};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey};
use rustc_span::edition::Edition;
use rustc_span::{sym, symbol::Ident, MultiSpan, Span, Symbol};
pub mod builtin;
#[macro_export]
macro_rules! pluralize {
($x:expr) => {
if $x != 1 { "s" } else { "" }
};
}
#[derive(Copy, Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
pub enum Applicability {
MachineApplicable,
MaybeIncorrect,
HasPlaceholders,
Unspecified,
}
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
pub enum Level {
Allow,
Warn,
Deny,
Forbid,
}
rustc_data_structures::impl_stable_hash_via_hash!(Level);
impl Level {
pub fn as_str(self) -> &'static str {
match self {
Level::Allow => "allow",
Level::Warn => "warn",
Level::Deny => "deny",
Level::Forbid => "forbid",
}
}
pub fn from_str(x: &str) -> Option<Level> {
match x {
"allow" => Some(Level::Allow),
"warn" => Some(Level::Warn),
"deny" => Some(Level::Deny),
"forbid" => Some(Level::Forbid),
_ => None,
}
}
pub fn from_symbol(x: Symbol) -> Option<Level> {
match x {
sym::allow => Some(Level::Allow),
sym::warn => Some(Level::Warn),
sym::deny => Some(Level::Deny),
sym::forbid => Some(Level::Forbid),
_ => None,
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct Lint {
pub name: &'static str,
pub default_level: Level,
pub desc: &'static str,
pub edition_lint_opts: Option<(Edition, Level)>,
pub report_in_external_macro: bool,
pub future_incompatible: Option<FutureIncompatibleInfo>,
pub is_plugin: bool,
pub feature_gate: Option<Symbol>,
pub crate_level_only: bool,
}
#[derive(Copy, Clone, Debug)]
pub struct FutureIncompatibleInfo {
pub reference: &'static str,
pub edition: Option<Edition>,
pub future_breakage: Option<FutureBreakage>,
}
#[derive(Copy, Clone, Debug)]
pub struct FutureBreakage {
pub date: Option<&'static str>,
}
impl FutureIncompatibleInfo {
pub const fn default_fields_for_macro() -> Self {
FutureIncompatibleInfo { reference: "", edition: None, future_breakage: None }
}
}
impl Lint {
pub const fn default_fields_for_macro() -> Self {
Lint {
name: "",
default_level: Level::Forbid,
desc: "",
edition_lint_opts: None,
is_plugin: false,
report_in_external_macro: false,
future_incompatible: None,
feature_gate: None,
crate_level_only: false,
}
}
pub fn name_lower(&self) -> String {
self.name.to_ascii_lowercase()
}
pub fn default_level(&self, edition: Edition) -> Level {
self.edition_lint_opts
.filter(|(e, _)| *e <= edition)
.map(|(_, l)| l)
.unwrap_or(self.default_level)
}
}
#[derive(Clone, Copy, Debug)]
pub struct LintId {
pub lint: &'static Lint,
}
impl PartialEq for LintId {
fn eq(&self, other: &LintId) -> bool {
std::ptr::eq(self.lint, other.lint)
}
}
impl Eq for LintId {}
impl std::hash::Hash for LintId {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
let ptr = self.lint as *const Lint;
ptr.hash(state);
}
}
impl LintId {
pub fn of(lint: &'static Lint) -> LintId {
LintId { lint }
}
pub fn lint_name_raw(&self) -> &'static str {
self.lint.name
}
pub fn to_string(&self) -> String {
self.lint.name_lower()
}
}
impl<HCX> HashStable<HCX> for LintId {
#[inline]
fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
self.lint_name_raw().hash_stable(hcx, hasher);
}
}
impl<HCX> ToStableHashKey<HCX> for LintId {
type KeyType = &'static str;
#[inline]
fn to_stable_hash_key(&self, _: &HCX) -> &'static str {
self.lint_name_raw()
}
}
#[derive(PartialEq)]
pub enum BuiltinLintDiagnostics {
Normal,
BareTraitObject(Span, bool),
AbsPathWithModule(Span),
ProcMacroDeriveResolutionFallback(Span),
MacroExpandedMacroExportsAccessedByAbsolutePaths(Span),
ElidedLifetimesInPaths(usize, Span, bool, Span, String),
UnknownCrateTypes(Span, String, String),
UnusedImports(String, Vec<(Span, String)>),
RedundantImport(Vec<(Span, bool)>, Ident),
DeprecatedMacro(Option<Symbol>, Span),
UnusedDocComment(Span),
}
#[derive(PartialEq)]
pub struct BufferedEarlyLint {
pub span: MultiSpan,
pub msg: String,
pub node_id: NodeId,
pub lint_id: LintId,
pub diagnostic: BuiltinLintDiagnostics,
}
#[derive(Default)]
pub struct LintBuffer {
pub map: NodeMap<Vec<BufferedEarlyLint>>,
}
impl LintBuffer {
pub fn add_early_lint(&mut self, early_lint: BufferedEarlyLint) {
let arr = self.map.entry(early_lint.node_id).or_default();
if !arr.contains(&early_lint) {
arr.push(early_lint);
}
}
pub fn add_lint(
&mut self,
lint: &'static Lint,
node_id: NodeId,
span: MultiSpan,
msg: &str,
diagnostic: BuiltinLintDiagnostics,
) {
let lint_id = LintId::of(lint);
let msg = msg.to_string();
self.add_early_lint(BufferedEarlyLint { lint_id, node_id, span, msg, diagnostic });
}
pub fn take(&mut self, id: NodeId) -> Vec<BufferedEarlyLint> {
self.map.remove(&id).unwrap_or_default()
}
pub fn buffer_lint(
&mut self,
lint: &'static Lint,
id: NodeId,
sp: impl Into<MultiSpan>,
msg: &str,
) {
self.add_lint(lint, id, sp.into(), msg, BuiltinLintDiagnostics::Normal)
}
pub fn buffer_lint_with_diagnostic(
&mut self,
lint: &'static Lint,
id: NodeId,
sp: impl Into<MultiSpan>,
msg: &str,
diagnostic: BuiltinLintDiagnostics,
) {
self.add_lint(lint, id, sp.into(), msg, diagnostic)
}
}
#[macro_export]
macro_rules! declare_lint {
($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr) => (
$crate::declare_lint!(
$(#[$attr])* $vis $NAME, $Level, $desc,
);
);
($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr,
$(@feature_gate = $gate:expr;)?
$(@future_incompatible = FutureIncompatibleInfo { $($field:ident : $val:expr),* $(,)* }; )?
$($v:ident),*) => (
$(#[$attr])*
$vis static $NAME: &$crate::Lint = &$crate::Lint {
name: stringify!($NAME),
default_level: $crate::$Level,
desc: $desc,
edition_lint_opts: None,
is_plugin: false,
$($v: true,)*
$(feature_gate: Some($gate),)*
$(future_incompatible: Some($crate::FutureIncompatibleInfo {
$($field: $val,)*
..$crate::FutureIncompatibleInfo::default_fields_for_macro()
}),)*
..$crate::Lint::default_fields_for_macro()
};
);
($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr,
$lint_edition: expr => $edition_level: ident
) => (
$(#[$attr])*
$vis static $NAME: &$crate::Lint = &$crate::Lint {
name: stringify!($NAME),
default_level: $crate::$Level,
desc: $desc,
edition_lint_opts: Some(($lint_edition, $crate::Level::$edition_level)),
report_in_external_macro: false,
is_plugin: false,
};
);
}
#[macro_export]
macro_rules! declare_tool_lint {
(
$(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level: ident, $desc: expr
) => (
$crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, false}
);
(
$(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr,
report_in_external_macro: $rep:expr
) => (
$crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, $rep}
);
(
$(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr,
$external:expr
) => (
$(#[$attr])*
$vis static $NAME: &$crate::Lint = &$crate::Lint {
name: &concat!(stringify!($tool), "::", stringify!($NAME)),
default_level: $crate::$Level,
desc: $desc,
edition_lint_opts: None,
report_in_external_macro: $external,
future_incompatible: None,
is_plugin: true,
feature_gate: None,
crate_level_only: false,
};
);
}
#[macro_export]
macro_rules! lint_array {
($( $lint:expr ),* ,) => { lint_array!( $($lint),* ) };
($( $lint:expr ),*) => {{
vec![$($lint),*]
}}
}
pub type LintArray = Vec<&'static Lint>;
pub trait LintPass {
fn name(&self) -> &'static str;
}
#[macro_export]
macro_rules! impl_lint_pass {
($ty:ty => [$($lint:expr),* $(,)?]) => {
impl $crate::LintPass for $ty {
fn name(&self) -> &'static str { stringify!($ty) }
}
impl $ty {
pub fn get_lints() -> $crate::LintArray { $crate::lint_array!($($lint),*) }
}
};
}
#[macro_export]
macro_rules! declare_lint_pass {
($(#[$m:meta])* $name:ident => [$($lint:expr),* $(,)?]) => {
$(#[$m])* #[derive(Copy, Clone)] pub struct $name;
$crate::impl_lint_pass!($name => [$($lint),*]);
};
}