|
1 | 1 | //! Lints concerned with the grouping of digits with underscores in integral or |
2 | 2 | //! floating-point literal expressions. |
3 | 3 |
|
4 | | -use crate::utils::{in_macro, snippet_opt, span_lint_and_sugg}; |
| 4 | +use crate::utils::{ |
| 5 | + in_macro, |
| 6 | + numeric_literal::{NumericLiteral, Radix}, |
| 7 | + snippet_opt, span_lint_and_sugg, |
| 8 | +}; |
5 | 9 | use if_chain::if_chain; |
6 | 10 | use rustc::lint::in_external_macro; |
7 | | -use rustc_ast::ast::{Expr, ExprKind, Lit, LitFloatType, LitIntType, LitKind}; |
| 11 | +use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind}; |
8 | 12 | use rustc_errors::Applicability; |
9 | 13 | use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; |
10 | 14 | use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; |
@@ -103,224 +107,6 @@ declare_clippy_lint! { |
103 | 107 | "using decimal representation when hexadecimal would be better" |
104 | 108 | } |
105 | 109 |
|
106 | | -#[derive(Debug, PartialEq)] |
107 | | -pub(super) enum Radix { |
108 | | - Binary, |
109 | | - Octal, |
110 | | - Decimal, |
111 | | - Hexadecimal, |
112 | | -} |
113 | | - |
114 | | -impl Radix { |
115 | | - /// Returns a reasonable digit group size for this radix. |
116 | | - #[must_use] |
117 | | - fn suggest_grouping(&self) -> usize { |
118 | | - match *self { |
119 | | - Self::Binary | Self::Hexadecimal => 4, |
120 | | - Self::Octal | Self::Decimal => 3, |
121 | | - } |
122 | | - } |
123 | | -} |
124 | | - |
125 | | -/// A helper method to format numeric literals with digit grouping. |
126 | | -/// `lit` must be a valid numeric literal without suffix. |
127 | | -pub fn format_numeric_literal(lit: &str, type_suffix: Option<&str>, float: bool) -> String { |
128 | | - NumericLiteral::new(lit, type_suffix, float).format() |
129 | | -} |
130 | | - |
131 | | -#[derive(Debug)] |
132 | | -pub(super) struct NumericLiteral<'a> { |
133 | | - /// Which radix the literal was represented in. |
134 | | - radix: Radix, |
135 | | - /// The radix prefix, if present. |
136 | | - prefix: Option<&'a str>, |
137 | | - |
138 | | - /// The integer part of the number. |
139 | | - integer: &'a str, |
140 | | - /// The fraction part of the number. |
141 | | - fraction: Option<&'a str>, |
142 | | - /// The character used as exponent seperator (b'e' or b'E') and the exponent part. |
143 | | - exponent: Option<(char, &'a str)>, |
144 | | - |
145 | | - /// The type suffix, including preceding underscore if present. |
146 | | - suffix: Option<&'a str>, |
147 | | -} |
148 | | - |
149 | | -impl<'a> NumericLiteral<'a> { |
150 | | - fn from_lit(src: &'a str, lit: &Lit) -> Option<NumericLiteral<'a>> { |
151 | | - if lit.kind.is_numeric() && src.chars().next().map_or(false, |c| c.is_digit(10)) { |
152 | | - let (unsuffixed, suffix) = split_suffix(&src, &lit.kind); |
153 | | - let float = if let LitKind::Float(..) = lit.kind { true } else { false }; |
154 | | - Some(NumericLiteral::new(unsuffixed, suffix, float)) |
155 | | - } else { |
156 | | - None |
157 | | - } |
158 | | - } |
159 | | - |
160 | | - #[must_use] |
161 | | - fn new(lit: &'a str, suffix: Option<&'a str>, float: bool) -> Self { |
162 | | - // Determine delimiter for radix prefix, if present, and radix. |
163 | | - let radix = if lit.starts_with("0x") { |
164 | | - Radix::Hexadecimal |
165 | | - } else if lit.starts_with("0b") { |
166 | | - Radix::Binary |
167 | | - } else if lit.starts_with("0o") { |
168 | | - Radix::Octal |
169 | | - } else { |
170 | | - Radix::Decimal |
171 | | - }; |
172 | | - |
173 | | - // Grab part of the literal after prefix, if present. |
174 | | - let (prefix, mut sans_prefix) = if let Radix::Decimal = radix { |
175 | | - (None, lit) |
176 | | - } else { |
177 | | - let (p, s) = lit.split_at(2); |
178 | | - (Some(p), s) |
179 | | - }; |
180 | | - |
181 | | - if suffix.is_some() && sans_prefix.ends_with('_') { |
182 | | - // The '_' before the suffix isn't part of the digits |
183 | | - sans_prefix = &sans_prefix[..sans_prefix.len() - 1]; |
184 | | - } |
185 | | - |
186 | | - let (integer, fraction, exponent) = Self::split_digit_parts(sans_prefix, float); |
187 | | - |
188 | | - Self { |
189 | | - radix, |
190 | | - prefix, |
191 | | - integer, |
192 | | - fraction, |
193 | | - exponent, |
194 | | - suffix, |
195 | | - } |
196 | | - } |
197 | | - |
198 | | - fn split_digit_parts(digits: &str, float: bool) -> (&str, Option<&str>, Option<(char, &str)>) { |
199 | | - let mut integer = digits; |
200 | | - let mut fraction = None; |
201 | | - let mut exponent = None; |
202 | | - |
203 | | - if float { |
204 | | - for (i, c) in digits.char_indices() { |
205 | | - match c { |
206 | | - '.' => { |
207 | | - integer = &digits[..i]; |
208 | | - fraction = Some(&digits[i + 1..]); |
209 | | - }, |
210 | | - 'e' | 'E' => { |
211 | | - if integer.len() > i { |
212 | | - integer = &digits[..i]; |
213 | | - } else { |
214 | | - fraction = Some(&digits[integer.len() + 1..i]); |
215 | | - }; |
216 | | - exponent = Some((c, &digits[i + 1..])); |
217 | | - break; |
218 | | - }, |
219 | | - _ => {}, |
220 | | - } |
221 | | - } |
222 | | - } |
223 | | - |
224 | | - (integer, fraction, exponent) |
225 | | - } |
226 | | - |
227 | | - /// Returns literal formatted in a sensible way. |
228 | | - fn format(&self) -> String { |
229 | | - let mut output = String::new(); |
230 | | - |
231 | | - if let Some(prefix) = self.prefix { |
232 | | - output.push_str(prefix); |
233 | | - } |
234 | | - |
235 | | - let group_size = self.radix.suggest_grouping(); |
236 | | - |
237 | | - Self::group_digits( |
238 | | - &mut output, |
239 | | - self.integer, |
240 | | - group_size, |
241 | | - true, |
242 | | - self.radix == Radix::Hexadecimal, |
243 | | - ); |
244 | | - |
245 | | - if let Some(fraction) = self.fraction { |
246 | | - output.push('.'); |
247 | | - Self::group_digits(&mut output, fraction, group_size, false, false); |
248 | | - } |
249 | | - |
250 | | - if let Some((separator, exponent)) = self.exponent { |
251 | | - output.push(separator); |
252 | | - Self::group_digits(&mut output, exponent, group_size, true, false); |
253 | | - } |
254 | | - |
255 | | - if let Some(suffix) = self.suffix { |
256 | | - output.push('_'); |
257 | | - output.push_str(suffix); |
258 | | - } |
259 | | - |
260 | | - output |
261 | | - } |
262 | | - |
263 | | - fn group_digits(output: &mut String, input: &str, group_size: usize, partial_group_first: bool, pad: bool) { |
264 | | - debug_assert!(group_size > 0); |
265 | | - |
266 | | - let mut digits = input.chars().filter(|&c| c != '_'); |
267 | | - |
268 | | - let first_group_size; |
269 | | - |
270 | | - if partial_group_first { |
271 | | - first_group_size = (digits.clone().count() - 1) % group_size + 1; |
272 | | - if pad { |
273 | | - for _ in 0..group_size - first_group_size { |
274 | | - output.push('0'); |
275 | | - } |
276 | | - } |
277 | | - } else { |
278 | | - first_group_size = group_size; |
279 | | - } |
280 | | - |
281 | | - for _ in 0..first_group_size { |
282 | | - if let Some(digit) = digits.next() { |
283 | | - output.push(digit); |
284 | | - } |
285 | | - } |
286 | | - |
287 | | - for (c, i) in digits.zip((0..group_size).cycle()) { |
288 | | - if i == 0 { |
289 | | - output.push('_'); |
290 | | - } |
291 | | - output.push(c); |
292 | | - } |
293 | | - } |
294 | | -} |
295 | | - |
296 | | -fn split_suffix<'a>(src: &'a str, lit_kind: &LitKind) -> (&'a str, Option<&'a str>) { |
297 | | - debug_assert!(lit_kind.is_numeric()); |
298 | | - if let Some(suffix_length) = lit_suffix_length(lit_kind) { |
299 | | - let (unsuffixed, suffix) = src.split_at(src.len() - suffix_length); |
300 | | - (unsuffixed, Some(suffix)) |
301 | | - } else { |
302 | | - (src, None) |
303 | | - } |
304 | | -} |
305 | | - |
306 | | -fn lit_suffix_length(lit_kind: &LitKind) -> Option<usize> { |
307 | | - debug_assert!(lit_kind.is_numeric()); |
308 | | - let suffix = match lit_kind { |
309 | | - LitKind::Int(_, int_lit_kind) => match int_lit_kind { |
310 | | - LitIntType::Signed(int_ty) => Some(int_ty.name_str()), |
311 | | - LitIntType::Unsigned(uint_ty) => Some(uint_ty.name_str()), |
312 | | - LitIntType::Unsuffixed => None, |
313 | | - }, |
314 | | - LitKind::Float(_, float_lit_kind) => match float_lit_kind { |
315 | | - LitFloatType::Suffixed(float_ty) => Some(float_ty.name_str()), |
316 | | - LitFloatType::Unsuffixed => None, |
317 | | - }, |
318 | | - _ => None, |
319 | | - }; |
320 | | - |
321 | | - suffix.map(str::len) |
322 | | -} |
323 | | - |
324 | 110 | enum WarningType { |
325 | 111 | UnreadableLiteral, |
326 | 112 | InconsistentDigitGrouping, |
|
0 commit comments