Skip to main content

ide_assists/handlers/
convert_integer_literal.rs

1use syntax::{AstToken, ast, ast::Radix};
2
3use crate::{AssistContext, AssistId, Assists, GroupLabel};
4
5// Assist: convert_integer_literal
6//
7// Converts the base of integer literals to other bases.
8//
9// ```
10// const _: i32 = 10$0;
11// ```
12// ->
13// ```
14// const _: i32 = 0b1010;
15// ```
16pub(crate) fn convert_integer_literal(
17    acc: &mut Assists,
18    ctx: &AssistContext<'_, '_>,
19) -> Option<()> {
20    if !ctx.has_empty_selection() {
21        return None;
22    }
23    let literal = ctx.find_node_at_offset::<ast::Literal>()?;
24    let literal = match literal.kind() {
25        ast::LiteralKind::IntNumber(it) => it,
26        _ => return None,
27    };
28    let radix = literal.radix();
29    let value = literal.value().ok()?;
30    let suffix = literal.suffix();
31
32    let range = literal.syntax().text_range();
33    let group_id = GroupLabel("Convert integer base".into());
34
35    for &target_radix in Radix::ALL {
36        if target_radix == radix {
37            continue;
38        }
39
40        let mut converted = match target_radix {
41            Radix::Binary => format!("0b{value:b}"),
42            Radix::Octal => format!("0o{value:o}"),
43            Radix::Decimal => value.to_string(),
44            Radix::Hexadecimal => format!("0x{value:X}"),
45        };
46
47        // Appends the type suffix back into the new literal if it exists.
48        if let Some(suffix) = suffix {
49            converted.push_str(suffix);
50        }
51
52        let label = format!("Convert {literal} to {converted}");
53
54        acc.add_group(
55            &group_id,
56            AssistId::refactor_rewrite("convert_integer_literal"),
57            label,
58            range,
59            |builder| builder.replace(range, converted),
60        );
61    }
62
63    Some(())
64}
65
66#[cfg(test)]
67mod tests {
68    use crate::tests::{check_assist_by_label, check_assist_not_applicable, check_assist_target};
69
70    use super::*;
71
72    #[test]
73    fn binary_target() {
74        check_assist_target(convert_integer_literal, "const _: i32 = 0b1010$0;", "0b1010");
75    }
76
77    #[test]
78    fn octal_target() {
79        check_assist_target(convert_integer_literal, "const _: i32 = 0o12$0;", "0o12");
80    }
81
82    #[test]
83    fn decimal_target() {
84        check_assist_target(convert_integer_literal, "const _: i32 = 10$0;", "10");
85    }
86
87    #[test]
88    fn hexadecimal_target() {
89        check_assist_target(convert_integer_literal, "const _: i32 = 0xA$0;", "0xA");
90    }
91
92    #[test]
93    fn binary_target_with_underscores() {
94        check_assist_target(convert_integer_literal, "const _: i32 = 0b10_10$0;", "0b10_10");
95    }
96
97    #[test]
98    fn octal_target_with_underscores() {
99        check_assist_target(convert_integer_literal, "const _: i32 = 0o1_2$0;", "0o1_2");
100    }
101
102    #[test]
103    fn decimal_target_with_underscores() {
104        check_assist_target(convert_integer_literal, "const _: i32 = 1_0$0;", "1_0");
105    }
106
107    #[test]
108    fn hexadecimal_target_with_underscores() {
109        check_assist_target(convert_integer_literal, "const _: i32 = 0x_A$0;", "0x_A");
110    }
111
112    #[test]
113    fn convert_decimal_integer() {
114        let before = "const _: i32 = 1000$0;";
115
116        check_assist_by_label(
117            convert_integer_literal,
118            before,
119            "const _: i32 = 0b1111101000;",
120            "Convert 1000 to 0b1111101000",
121        );
122
123        check_assist_by_label(
124            convert_integer_literal,
125            before,
126            "const _: i32 = 0o1750;",
127            "Convert 1000 to 0o1750",
128        );
129
130        check_assist_by_label(
131            convert_integer_literal,
132            before,
133            "const _: i32 = 0x3E8;",
134            "Convert 1000 to 0x3E8",
135        );
136    }
137
138    #[test]
139    fn convert_hexadecimal_integer() {
140        let before = "const _: i32 = 0xFF$0;";
141
142        check_assist_by_label(
143            convert_integer_literal,
144            before,
145            "const _: i32 = 0b11111111;",
146            "Convert 0xFF to 0b11111111",
147        );
148
149        check_assist_by_label(
150            convert_integer_literal,
151            before,
152            "const _: i32 = 0o377;",
153            "Convert 0xFF to 0o377",
154        );
155
156        check_assist_by_label(
157            convert_integer_literal,
158            before,
159            "const _: i32 = 255;",
160            "Convert 0xFF to 255",
161        );
162    }
163
164    #[test]
165    fn convert_binary_integer() {
166        let before = "const _: i32 = 0b11111111$0;";
167
168        check_assist_by_label(
169            convert_integer_literal,
170            before,
171            "const _: i32 = 0o377;",
172            "Convert 0b11111111 to 0o377",
173        );
174
175        check_assist_by_label(
176            convert_integer_literal,
177            before,
178            "const _: i32 = 255;",
179            "Convert 0b11111111 to 255",
180        );
181
182        check_assist_by_label(
183            convert_integer_literal,
184            before,
185            "const _: i32 = 0xFF;",
186            "Convert 0b11111111 to 0xFF",
187        );
188    }
189
190    #[test]
191    fn convert_octal_integer() {
192        let before = "const _: i32 = 0o377$0;";
193
194        check_assist_by_label(
195            convert_integer_literal,
196            before,
197            "const _: i32 = 0b11111111;",
198            "Convert 0o377 to 0b11111111",
199        );
200
201        check_assist_by_label(
202            convert_integer_literal,
203            before,
204            "const _: i32 = 255;",
205            "Convert 0o377 to 255",
206        );
207
208        check_assist_by_label(
209            convert_integer_literal,
210            before,
211            "const _: i32 = 0xFF;",
212            "Convert 0o377 to 0xFF",
213        );
214    }
215
216    #[test]
217    fn convert_integer_with_underscores() {
218        let before = "const _: i32 = 1_00_0$0;";
219
220        check_assist_by_label(
221            convert_integer_literal,
222            before,
223            "const _: i32 = 0b1111101000;",
224            "Convert 1_00_0 to 0b1111101000",
225        );
226
227        check_assist_by_label(
228            convert_integer_literal,
229            before,
230            "const _: i32 = 0o1750;",
231            "Convert 1_00_0 to 0o1750",
232        );
233
234        check_assist_by_label(
235            convert_integer_literal,
236            before,
237            "const _: i32 = 0x3E8;",
238            "Convert 1_00_0 to 0x3E8",
239        );
240    }
241
242    #[test]
243    fn convert_integer_with_suffix() {
244        let before = "const _: i32 = 1000i32$0;";
245
246        check_assist_by_label(
247            convert_integer_literal,
248            before,
249            "const _: i32 = 0b1111101000i32;",
250            "Convert 1000i32 to 0b1111101000i32",
251        );
252
253        check_assist_by_label(
254            convert_integer_literal,
255            before,
256            "const _: i32 = 0o1750i32;",
257            "Convert 1000i32 to 0o1750i32",
258        );
259
260        check_assist_by_label(
261            convert_integer_literal,
262            before,
263            "const _: i32 = 0x3E8i32;",
264            "Convert 1000i32 to 0x3E8i32",
265        );
266    }
267
268    #[test]
269    fn convert_overflowing_literal() {
270        let before = "const _: i32 =
271            111111111111111111111111111111111111111111111111111111111111111111111111$0;";
272        check_assist_not_applicable(convert_integer_literal, before);
273    }
274
275    #[test]
276    fn convert_non_empty_selection_literal() {
277        check_assist_not_applicable(convert_integer_literal, "const _: i32 = $00b1010$0;");
278    }
279}