-impl MiscEarly {
- fn check_lit(self, cx: &EarlyContext<'_>, lit: &Lit) {
- if_chain! {
- if let LitKind::Int(value, ..) = lit.node;
- if let Some(src) = snippet_opt(cx, lit.span);
- if let Some(firstch) = src.chars().next();
- if char::to_digit(firstch, 10).is_some();
- then {
- let mut prev = '\0';
- for ch in src.chars() {
- if ch == 'i' || ch == 'u' {
- if prev != '_' {
- span_lint(cx, UNSEPARATED_LITERAL_SUFFIX, lit.span,
- "integer type suffix should be separated by an underscore");
- }
- break;
- }
- prev = ch;
- }
- if src.starts_with("0x") {
- let mut seen = (false, false);
- for ch in src.chars() {
- match ch {
- 'a' ..= 'f' => seen.0 = true,
- 'A' ..= 'F' => seen.1 = true,
- 'i' | 'u' => break, // start of suffix already
- _ => ()
- }
+impl MiscEarlyLints {
+ fn check_lit(cx: &EarlyContext<'_>, lit: &Lit) {
+ // We test if first character in snippet is a number, because the snippet could be an expansion
+ // from a built-in macro like `line!()` or a proc-macro like `#[wasm_bindgen]`.
+ // Note that this check also covers special case that `line!()` is eagerly expanded by compiler.
+ // See <https://github.com/rust-lang/rust-clippy/issues/4507> for a regression.
+ // FIXME: Find a better way to detect those cases.
+ let lit_snip = match snippet_opt(cx, lit.span) {
+ Some(snip) if snip.chars().next().map_or(false, |c| c.is_digit(10)) => snip,
+ _ => return,
+ };
+
+ if let LitKind::Int(value, lit_int_type) = lit.kind {
+ let suffix = match lit_int_type {
+ LitIntType::Signed(ty) => ty.name_str(),
+ LitIntType::Unsigned(ty) => ty.name_str(),
+ LitIntType::Unsuffixed => "",
+ };
+
+ let maybe_last_sep_idx = lit_snip.len() - suffix.len() - 1;
+ // Do not lint when literal is unsuffixed.
+ if !suffix.is_empty() && lit_snip.as_bytes()[maybe_last_sep_idx] != b'_' {
+ span_lint_and_sugg(
+ cx,
+ UNSEPARATED_LITERAL_SUFFIX,
+ lit.span,
+ "integer type suffix should be separated by an underscore",
+ "add an underscore",
+ format!("{}_{}", &lit_snip[..=maybe_last_sep_idx], suffix),
+ Applicability::MachineApplicable,
+ );
+ }
+
+ if lit_snip.starts_with("0x") {
+ let mut seen = (false, false);
+ for ch in lit_snip.as_bytes()[2..=maybe_last_sep_idx].iter() {
+ match ch {
+ b'a'..=b'f' => seen.0 = true,
+ b'A'..=b'F' => seen.1 = true,
+ _ => {},