rustfmt_nightly/config/
options.rs

1#![allow(unused_imports)]
2
3use std::collections::{HashSet, hash_set};
4use std::fmt;
5use std::path::{Path, PathBuf};
6use std::str::FromStr;
7
8use itertools::Itertools;
9use rustfmt_config_proc_macro::config_type;
10use serde::de::{SeqAccess, Visitor};
11use serde::ser::SerializeSeq;
12use serde::{Deserialize, Deserializer, Serialize, Serializer};
13
14use crate::config::Config;
15use crate::config::file_lines::FileLines;
16use crate::config::lists::*;
17use crate::config::macro_names::MacroSelectors;
18
19#[config_type]
20pub enum NewlineStyle {
21    /// Auto-detect based on the raw source input.
22    Auto,
23    /// Force CRLF (`\r\n`).
24    Windows,
25    /// Force CR (`\n`).
26    Unix,
27    /// `\r\n` in Windows, `\n` on other platforms.
28    Native,
29}
30
31#[config_type]
32/// Where to put the opening brace of items (`fn`, `impl`, etc.).
33pub enum BraceStyle {
34    /// Put the opening brace on the next line.
35    AlwaysNextLine,
36    /// Put the opening brace on the same line, if possible.
37    PreferSameLine,
38    /// Prefer the same line except where there is a where-clause, in which
39    /// case force the brace to be put on the next line.
40    SameLineWhere,
41}
42
43#[config_type]
44/// Where to put the opening brace of conditional expressions (`if`, `match`, etc.).
45pub enum ControlBraceStyle {
46    /// K&R style, Rust community default
47    AlwaysSameLine,
48    /// Stroustrup style
49    ClosingNextLine,
50    /// Allman style
51    AlwaysNextLine,
52}
53
54#[config_type]
55/// How to indent.
56pub enum IndentStyle {
57    /// First line on the same line as the opening brace, all lines aligned with
58    /// the first line.
59    Visual,
60    /// First line is on a new line and all lines align with **block** indent.
61    Block,
62}
63
64#[config_type]
65/// How to place a list-like items.
66/// FIXME: Issue-3581: this should be renamed to ItemsLayout when publishing 2.0
67pub enum Density {
68    /// Fit as much on one line as possible.
69    Compressed,
70    /// Items are placed horizontally if sufficient space, vertically otherwise.
71    Tall,
72    /// Place every item on a separate line.
73    Vertical,
74}
75
76#[config_type]
77/// Spacing around type combinators.
78pub enum TypeDensity {
79    /// No spaces around "=" and "+"
80    Compressed,
81    /// Spaces around " = " and " + "
82    Wide,
83}
84
85#[config_type]
86/// Heuristic settings that can be used to simply
87/// the configuration of the granular width configurations
88/// like `struct_lit_width`, `array_width`, etc.
89pub enum Heuristics {
90    /// Turn off any heuristics
91    Off,
92    /// Turn on max heuristics
93    Max,
94    /// Use scaled values based on the value of `max_width`
95    Default,
96}
97
98impl Density {
99    pub fn to_list_tactic(self, len: usize) -> ListTactic {
100        match self {
101            Density::Compressed => ListTactic::Mixed,
102            Density::Tall => ListTactic::HorizontalVertical,
103            Density::Vertical if len == 1 => ListTactic::Horizontal,
104            Density::Vertical => ListTactic::Vertical,
105        }
106    }
107}
108
109#[config_type]
110/// Configuration for import groups, i.e. sets of imports separated by newlines.
111pub enum GroupImportsTactic {
112    /// Keep groups as they are.
113    Preserve,
114    /// Discard existing groups, and create new groups for
115    ///  1. `std` / `core` / `alloc` imports
116    ///  2. other imports
117    ///  3. `self` / `crate` / `super` imports
118    StdExternalCrate,
119    /// Discard existing groups, and create a single group for everything
120    One,
121}
122
123#[config_type]
124/// How to merge imports.
125pub enum ImportGranularity {
126    /// Do not merge imports.
127    Preserve,
128    /// Use one `use` statement per crate.
129    Crate,
130    /// Use one `use` statement per module.
131    Module,
132    /// Use one `use` statement per imported item.
133    Item,
134    /// Use one `use` statement including all items.
135    One,
136}
137
138/// Controls how rustfmt should handle case in hexadecimal literals.
139#[config_type]
140pub enum HexLiteralCase {
141    /// Leave the literal as-is
142    Preserve,
143    /// Ensure all literals use uppercase lettering
144    Upper,
145    /// Ensure all literals use lowercase lettering
146    Lower,
147}
148
149#[config_type]
150pub enum ReportTactic {
151    Always,
152    Unnumbered,
153    Never,
154}
155
156/// What Rustfmt should emit. Mostly corresponds to the `--emit` command line
157/// option.
158#[config_type]
159pub enum EmitMode {
160    /// Emits to files.
161    Files,
162    /// Writes the output to stdout.
163    Stdout,
164    /// Displays how much of the input file was processed
165    Coverage,
166    /// Unfancy stdout
167    Checkstyle,
168    /// Writes the resulting diffs in a JSON format. Returns an empty array
169    /// `[]` if there were no diffs.
170    Json,
171    /// Output the changed lines (for internal value only)
172    ModifiedLines,
173    /// Checks if a diff can be generated. If so, rustfmt outputs a diff and
174    /// quits with exit code 1.
175    /// This option is designed to be run in CI where a non-zero exit signifies
176    /// non-standard code formatting. Used for `--check`.
177    Diff,
178}
179
180/// Client-preference for coloured output.
181#[config_type]
182pub enum Color {
183    /// Always use color, whether it is a piped or terminal output
184    Always,
185    /// Never use color
186    Never,
187    /// Automatically use color, if supported by terminal
188    Auto,
189}
190
191#[config_type]
192/// rustfmt format style version.
193pub enum Version {
194    /// 1.x.y. When specified, rustfmt will format in the same style as 1.0.0.
195    One,
196    /// 2.x.y. When specified, rustfmt will format in the latest style.
197    Two,
198}
199
200impl Color {
201    /// Whether we should use a coloured terminal.
202    pub fn use_colored_tty(self) -> bool {
203        match self {
204            Color::Always | Color::Auto => true,
205            Color::Never => false,
206        }
207    }
208}
209
210/// How chatty should Rustfmt be?
211#[config_type]
212pub enum Verbosity {
213    /// Emit more.
214    Verbose,
215    /// Default.
216    Normal,
217    /// Emit as little as possible.
218    Quiet,
219}
220
221#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
222pub struct WidthHeuristics {
223    // Maximum width of the args of a function call before falling back
224    // to vertical formatting.
225    pub(crate) fn_call_width: usize,
226    // Maximum width of the args of a function-like attributes before falling
227    // back to vertical formatting.
228    pub(crate) attr_fn_like_width: usize,
229    // Maximum width in the body of a struct lit before falling back to
230    // vertical formatting.
231    pub(crate) struct_lit_width: usize,
232    // Maximum width in the body of a struct variant before falling back
233    // to vertical formatting.
234    pub(crate) struct_variant_width: usize,
235    // Maximum width of an array literal before falling back to vertical
236    // formatting.
237    pub(crate) array_width: usize,
238    // Maximum length of a chain to fit on a single line.
239    pub(crate) chain_width: usize,
240    // Maximum line length for single line if-else expressions. A value
241    // of zero means always break if-else expressions.
242    pub(crate) single_line_if_else_max_width: usize,
243    // Maximum line length for single line let-else statements. A value of zero means
244    // always format the divergent `else` block over multiple lines.
245    pub(crate) single_line_let_else_max_width: usize,
246}
247
248impl fmt::Display for WidthHeuristics {
249    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250        write!(f, "{self:?}")
251    }
252}
253
254impl WidthHeuristics {
255    // Using this WidthHeuristics means we ignore heuristics.
256    pub fn null() -> WidthHeuristics {
257        WidthHeuristics {
258            fn_call_width: usize::MAX,
259            attr_fn_like_width: usize::MAX,
260            struct_lit_width: 0,
261            struct_variant_width: 0,
262            array_width: usize::MAX,
263            chain_width: usize::MAX,
264            single_line_if_else_max_width: 0,
265            single_line_let_else_max_width: 0,
266        }
267    }
268
269    pub fn set(max_width: usize) -> WidthHeuristics {
270        WidthHeuristics {
271            fn_call_width: max_width,
272            attr_fn_like_width: max_width,
273            struct_lit_width: max_width,
274            struct_variant_width: max_width,
275            array_width: max_width,
276            chain_width: max_width,
277            single_line_if_else_max_width: max_width,
278            single_line_let_else_max_width: max_width,
279        }
280    }
281
282    // scale the default WidthHeuristics according to max_width
283    pub fn scaled(max_width: usize) -> WidthHeuristics {
284        const DEFAULT_MAX_WIDTH: usize = 100;
285        let max_width_ratio = if max_width > DEFAULT_MAX_WIDTH {
286            let ratio = max_width as f32 / DEFAULT_MAX_WIDTH as f32;
287            // round to the closest 0.1
288            (ratio * 10.0).round() / 10.0
289        } else {
290            1.0
291        };
292        WidthHeuristics {
293            fn_call_width: (60.0 * max_width_ratio).round() as usize,
294            attr_fn_like_width: (70.0 * max_width_ratio).round() as usize,
295            struct_lit_width: (18.0 * max_width_ratio).round() as usize,
296            struct_variant_width: (35.0 * max_width_ratio).round() as usize,
297            array_width: (60.0 * max_width_ratio).round() as usize,
298            chain_width: (60.0 * max_width_ratio).round() as usize,
299            single_line_if_else_max_width: (50.0 * max_width_ratio).round() as usize,
300            single_line_let_else_max_width: (50.0 * max_width_ratio).round() as usize,
301        }
302    }
303}
304
305impl ::std::str::FromStr for WidthHeuristics {
306    type Err = &'static str;
307
308    fn from_str(_: &str) -> Result<Self, Self::Err> {
309        Err("WidthHeuristics is not parsable")
310    }
311}
312
313impl Default for EmitMode {
314    fn default() -> EmitMode {
315        EmitMode::Files
316    }
317}
318
319/// A set of directories, files and modules that rustfmt should ignore.
320#[derive(Default, Clone, Debug, PartialEq)]
321pub struct IgnoreList {
322    /// A set of path specified in rustfmt.toml.
323    path_set: HashSet<PathBuf>,
324    /// A path to rustfmt.toml.
325    rustfmt_toml_path: PathBuf,
326}
327
328impl fmt::Display for IgnoreList {
329    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330        write!(
331            f,
332            "[{}]",
333            self.path_set
334                .iter()
335                .format_with(", ", |path, f| f(&format_args!(
336                    "{}",
337                    path.to_string_lossy()
338                )))
339        )
340    }
341}
342
343impl Serialize for IgnoreList {
344    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
345    where
346        S: Serializer,
347    {
348        let mut seq = serializer.serialize_seq(Some(self.path_set.len()))?;
349        for e in &self.path_set {
350            seq.serialize_element(e)?;
351        }
352        seq.end()
353    }
354}
355
356impl<'de> Deserialize<'de> for IgnoreList {
357    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
358    where
359        D: Deserializer<'de>,
360    {
361        struct HashSetVisitor;
362        impl<'v> Visitor<'v> for HashSetVisitor {
363            type Value = HashSet<PathBuf>;
364
365            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
366                formatter.write_str("a sequence of path")
367            }
368
369            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
370            where
371                A: SeqAccess<'v>,
372            {
373                let mut path_set = HashSet::new();
374                while let Some(elem) = seq.next_element()? {
375                    path_set.insert(elem);
376                }
377                Ok(path_set)
378            }
379        }
380        Ok(IgnoreList {
381            path_set: deserializer.deserialize_seq(HashSetVisitor)?,
382            rustfmt_toml_path: PathBuf::new(),
383        })
384    }
385}
386
387impl<'a> IntoIterator for &'a IgnoreList {
388    type Item = &'a PathBuf;
389    type IntoIter = hash_set::Iter<'a, PathBuf>;
390
391    fn into_iter(self) -> Self::IntoIter {
392        self.path_set.iter()
393    }
394}
395
396impl IgnoreList {
397    pub fn add_prefix(&mut self, dir: &Path) {
398        self.rustfmt_toml_path = dir.to_path_buf();
399    }
400
401    pub fn rustfmt_toml_path(&self) -> &Path {
402        &self.rustfmt_toml_path
403    }
404}
405
406impl FromStr for IgnoreList {
407    type Err = &'static str;
408
409    fn from_str(_: &str) -> Result<Self, Self::Err> {
410        Err("IgnoreList is not parsable")
411    }
412}
413
414/// Maps client-supplied options to Rustfmt's internals, mostly overriding
415/// values in a config with values from the command line.
416pub trait CliOptions {
417    fn apply_to(self, config: &mut Config);
418
419    /// It is ok if the returned path doesn't exist or is not canonicalized
420    /// (i.e. the callers are expected to handle such cases).
421    fn config_path(&self) -> Option<&Path>;
422    fn edition(&self) -> Option<Edition>;
423    fn style_edition(&self) -> Option<StyleEdition>;
424    fn version(&self) -> Option<Version>;
425}
426
427/// The edition of the syntax and semantics of code (RFC 2052).
428#[config_type]
429pub enum Edition {
430    #[value = "2015"]
431    #[doc_hint = "2015"]
432    /// Edition 2015.
433    Edition2015,
434    #[value = "2018"]
435    #[doc_hint = "2018"]
436    /// Edition 2018.
437    Edition2018,
438    #[value = "2021"]
439    #[doc_hint = "2021"]
440    /// Edition 2021.
441    Edition2021,
442    #[value = "2024"]
443    #[doc_hint = "2024"]
444    /// Edition 2024.
445    Edition2024,
446}
447
448impl Default for Edition {
449    fn default() -> Edition {
450        Edition::Edition2015
451    }
452}
453
454impl From<Edition> for rustc_span::edition::Edition {
455    fn from(edition: Edition) -> Self {
456        match edition {
457            Edition::Edition2015 => Self::Edition2015,
458            Edition::Edition2018 => Self::Edition2018,
459            Edition::Edition2021 => Self::Edition2021,
460            Edition::Edition2024 => Self::Edition2024,
461        }
462    }
463}
464
465impl From<Edition> for StyleEdition {
466    fn from(edition: Edition) -> Self {
467        match edition {
468            Edition::Edition2015 => StyleEdition::Edition2015,
469            Edition::Edition2018 => StyleEdition::Edition2018,
470            Edition::Edition2021 => StyleEdition::Edition2021,
471            Edition::Edition2024 => StyleEdition::Edition2024,
472        }
473    }
474}
475
476impl PartialOrd for Edition {
477    fn partial_cmp(&self, other: &Edition) -> Option<std::cmp::Ordering> {
478        rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into())
479    }
480}
481
482/// Controls how rustfmt should handle leading pipes on match arms.
483#[config_type]
484pub enum MatchArmLeadingPipe {
485    /// Place leading pipes on all match arms
486    Always,
487    /// Never emit leading pipes on match arms
488    Never,
489    /// Preserve any existing leading pipes
490    Preserve,
491}
492
493/// Defines the default values for each config according to the edition of the
494/// [Style Guide] as per [RFC 3338]. Rustfmt output may differ between Style editions.
495///
496/// [Style Guide]: https://doc.rust-lang.org/nightly/style-guide/
497/// [RFC 3338]: https://rust-lang.github.io/rfcs/3338-style-evolution.html
498#[config_type]
499pub enum StyleEdition {
500    #[value = "2015"]
501    #[doc_hint = "2015"]
502    /// [Edition 2015]()
503    Edition2015,
504    #[value = "2018"]
505    #[doc_hint = "2018"]
506    /// [Edition 2018]()
507    Edition2018,
508    #[value = "2021"]
509    #[doc_hint = "2021"]
510    /// [Edition 2021]()
511    Edition2021,
512    #[value = "2024"]
513    #[doc_hint = "2024"]
514    /// [Edition 2024]().
515    Edition2024,
516}
517
518impl From<StyleEdition> for rustc_span::edition::Edition {
519    fn from(edition: StyleEdition) -> Self {
520        match edition {
521            StyleEdition::Edition2015 => Self::Edition2015,
522            StyleEdition::Edition2018 => Self::Edition2018,
523            StyleEdition::Edition2021 => Self::Edition2021,
524            StyleEdition::Edition2024 => Self::Edition2024,
525        }
526    }
527}
528
529impl PartialOrd for StyleEdition {
530    fn partial_cmp(&self, other: &StyleEdition) -> Option<std::cmp::Ordering> {
531        rustc_span::edition::Edition::partial_cmp(&(*self).into(), &(*other).into())
532    }
533}
534
535/// Defines unit structs to implement `StyleEditionDefault` for.
536#[macro_export]
537macro_rules! config_option_with_style_edition_default {
538    ($name:ident, $config_ty:ty, _ => $default:expr) => {
539        #[allow(unreachable_pub)]
540        pub struct $name;
541        $crate::style_edition_default!($name, $config_ty, _ => $default);
542    };
543    ($name:ident, $config_ty:ty, Edition2024 => $default_2024:expr, _ => $default_2015:expr) => {
544        pub struct $name;
545        $crate::style_edition_default!(
546            $name,
547            $config_ty,
548            Edition2024 => $default_2024,
549            _ => $default_2015
550        );
551    };
552    (
553        $($name:ident, $config_ty:ty, $(Edition2024 => $default_2024:expr,)? _ => $default:expr);*
554        $(;)*
555    ) => {
556        $(
557            config_option_with_style_edition_default!(
558                $name, $config_ty, $(Edition2024 => $default_2024,)? _ => $default
559            );
560        )*
561    };
562}
563
564// TODO(ytmimi) Some of the configuration values have a `Config` suffix, while others don't.
565// I chose to add a `Config` suffix in cases where a type for the config option was already
566// defined. For example, `NewlineStyle` and `NewlineStyleConfig`. There was some discussion
567// about using the `Config` suffix more consistently.
568config_option_with_style_edition_default!(
569    // Fundamental stuff
570    MaxWidth, usize, _ => 100;
571    HardTabs, bool, _ => false;
572    TabSpaces, usize, _ => 4;
573    NewlineStyleConfig, NewlineStyle, _ => NewlineStyle::Auto;
574    IndentStyleConfig, IndentStyle, _ => IndentStyle::Block;
575
576    // Width Heuristics
577    UseSmallHeuristics, Heuristics, _ => Heuristics::Default;
578    WidthHeuristicsConfig, WidthHeuristics, _ => WidthHeuristics::scaled(100);
579    FnCallWidth, usize, _ => 60;
580    AttrFnLikeWidth, usize, _ => 70;
581    StructLitWidth, usize, _ => 18;
582    StructVariantWidth, usize, _ => 35;
583    ArrayWidth, usize, _ => 60;
584    ChainWidth, usize, _ => 60;
585    SingleLineIfElseMaxWidth, usize, _ => 50;
586    SingleLineLetElseMaxWidth, usize, _ => 50;
587
588    // Comments. macros, and strings
589    WrapComments, bool, _ => false;
590    FormatCodeInDocComments, bool, _ => false;
591    DocCommentCodeBlockWidth, usize, _ => 100;
592    CommentWidth, usize, _ => 80;
593    NormalizeComments, bool, _ => false;
594    NormalizeDocAttributes, bool, _ => false;
595    FormatStrings, bool, _ => false;
596    FormatMacroMatchers, bool, _ => false;
597    FormatMacroBodies, bool, _ => true;
598    SkipMacroInvocations, MacroSelectors, _ => MacroSelectors::default();
599    HexLiteralCaseConfig, HexLiteralCase, _ => HexLiteralCase::Preserve;
600
601    // Single line expressions and items
602    EmptyItemSingleLine, bool, _ => true;
603    StructLitSingleLine, bool, _ => true;
604    FnSingleLine, bool, _ => false;
605    WhereSingleLine, bool, _ => false;
606
607    // Imports
608    ImportsIndent, IndentStyle, _ => IndentStyle::Block;
609    ImportsLayout, ListTactic, _ => ListTactic::Mixed;
610    ImportsGranularityConfig, ImportGranularity, _ => ImportGranularity::Preserve;
611    GroupImportsTacticConfig, GroupImportsTactic, _ => GroupImportsTactic::Preserve;
612    MergeImports, bool, _ => false;
613
614    // Ordering
615    ReorderImports, bool, _ => true;
616    ReorderModules, bool, _ => true;
617    ReorderImplItems, bool, _ => false;
618
619    // Spaces around punctuation
620    TypePunctuationDensity, TypeDensity, _ => TypeDensity::Wide;
621    SpaceBeforeColon, bool, _ => false;
622    SpaceAfterColon, bool, _ => true;
623    SpacesAroundRanges, bool, _ => false;
624    BinopSeparator, SeparatorPlace, _ => SeparatorPlace::Front;
625
626    // Misc.
627    RemoveNestedParens, bool, _ => true;
628    CombineControlExpr, bool, _ => true;
629    ShortArrayElementWidthThreshold, usize, _ => 10;
630    OverflowDelimitedExpr, bool, _ => false;
631    StructFieldAlignThreshold, usize, _ => 0;
632    EnumDiscrimAlignThreshold, usize, _ => 0;
633    MatchArmBlocks, bool, _ => true;
634    MatchArmLeadingPipeConfig, MatchArmLeadingPipe, _ => MatchArmLeadingPipe::Never;
635    ForceMultilineBlocks, bool, _ => false;
636    FnArgsLayout, Density, _ => Density::Tall;
637    FnParamsLayout, Density, _ => Density::Tall;
638    BraceStyleConfig, BraceStyle, _ => BraceStyle::SameLineWhere;
639    ControlBraceStyleConfig, ControlBraceStyle, _ => ControlBraceStyle::AlwaysSameLine;
640    TrailingSemicolon, bool, _ => true;
641    TrailingComma, SeparatorTactic, _ => SeparatorTactic::Vertical;
642    MatchBlockTrailingComma, bool, _ => false;
643    BlankLinesUpperBound, usize, _ => 1;
644    BlankLinesLowerBound, usize, _ => 0;
645    EditionConfig, Edition, _ => Edition::Edition2015;
646    StyleEditionConfig, StyleEdition,
647        Edition2024 => StyleEdition::Edition2024, _ => StyleEdition::Edition2015;
648    VersionConfig, Version, Edition2024 => Version::Two, _ => Version::One;
649    InlineAttributeWidth, usize, _ => 0;
650    FormatGeneratedFiles, bool, _ => true;
651    GeneratedMarkerLineSearchLimit, usize, _ => 5;
652
653    // Options that can change the source code beyond whitespace/blocks (somewhat linty things)
654    MergeDerives, bool, _ => true;
655    UseTryShorthand, bool, _ => false;
656    UseFieldInitShorthand, bool, _ => false;
657    ForceExplicitAbi, bool, _ => true;
658    CondenseWildcardSuffixes, bool, _ => false;
659
660    // Control options (changes the operation of rustfmt, rather than the formatting)
661    ColorConfig, Color, _ => Color::Auto;
662    RequiredVersion, String, _ => env!("CARGO_PKG_VERSION").to_owned();
663    UnstableFeatures, bool, _ => false;
664    DisableAllFormatting, bool, _ => false;
665    SkipChildren, bool, _ => false;
666    HideParseErrors, bool, _ => false;
667    ShowParseErrors, bool, _ => true;
668    ErrorOnLineOverflow, bool, _ => false;
669    ErrorOnUnformatted, bool, _ => false;
670    Ignore, IgnoreList, _ => IgnoreList::default();
671
672    // Not user-facing
673    Verbose, Verbosity, _ => Verbosity::Normal;
674    FileLinesConfig, FileLines, _ => FileLines::all();
675    EmitModeConfig, EmitMode, _ => EmitMode::Files;
676    MakeBackup, bool, _ => false;
677    PrintMisformattedFileNames, bool, _ => false;
678);