|
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