1use std::cell::Cell;
2use std::fs::File;
3use std::io::{Error, ErrorKind, Read};
4use std::path::{Path, PathBuf};
5use std::{env, fs};
6
7use thiserror::Error;
8
9use crate::config::config_type::ConfigType;
10#[allow(unreachable_pub)]
11pub use crate::config::file_lines::{FileLines, FileName, Range};
12#[allow(unreachable_pub)]
13pub use crate::config::macro_names::MacroSelector;
14#[allow(unreachable_pub)]
15pub use crate::config::options::*;
16
17#[macro_use]
18pub(crate) mod config_type;
19#[macro_use]
20#[allow(unreachable_pub)]
21pub(crate) mod options;
22
23pub(crate) mod file_lines;
24#[allow(unreachable_pub)]
25pub(crate) mod lists;
26pub(crate) mod macro_names;
27pub(crate) mod style_edition;
28
29create_config! {
34 max_width: MaxWidth, true, "Maximum width of each line";
36 hard_tabs: HardTabs, true, "Use tab characters for indentation, spaces for alignment";
37 tab_spaces: TabSpaces, true, "Number of spaces per tab";
38 newline_style: NewlineStyleConfig, true, "Unix or Windows line endings";
39 indent_style: IndentStyleConfig, false, "How do we indent expressions or items";
40
41 use_small_heuristics: UseSmallHeuristics, true, "Whether to use different \
43 formatting for items and expressions if they satisfy a heuristic notion of 'small'";
44 width_heuristics: WidthHeuristicsConfig, false, "'small' heuristic values";
45 fn_call_width: FnCallWidth, true, "Maximum width of the args of a function call before \
46 falling back to vertical formatting.";
47 attr_fn_like_width: AttrFnLikeWidth, true, "Maximum width of the args of a function-like \
48 attributes before falling back to vertical formatting.";
49 struct_lit_width: StructLitWidth, true, "Maximum width in the body of a struct lit before \
50 falling back to vertical formatting.";
51 struct_variant_width: StructVariantWidth, true, "Maximum width in the body of a struct variant \
52 before falling back to vertical formatting.";
53 array_width: ArrayWidth, true, "Maximum width of an array literal before falling \
54 back to vertical formatting.";
55 chain_width: ChainWidth, true, "Maximum length of a chain to fit on a single line.";
56 single_line_if_else_max_width: SingleLineIfElseMaxWidth, true, "Maximum line length for single \
57 line if-else expressions. A value of zero means always break if-else expressions.";
58 single_line_let_else_max_width: SingleLineLetElseMaxWidth, true, "Maximum line length for \
59 single line let-else statements. A value of zero means always format the divergent `else` \
60 block over multiple lines.";
61
62 wrap_comments: WrapComments, false, "Break comments to fit on the line";
64 format_code_in_doc_comments: FormatCodeInDocComments, false, "Format the code snippet in \
65 doc comments.";
66 doc_comment_code_block_width: DocCommentCodeBlockWidth, false, "Maximum width for code \
67 snippets in doc comments. No effect unless format_code_in_doc_comments = true";
68 doc_comment_code_block_small_heuristics: DocUseSmallHeuristics, false,
69 "Value for use_small_heuristics for code blocks in doc comments. \
70 No effect unless format_code_in_doc_comments = true";
71 comment_width: CommentWidth, false,
72 "Maximum length of comments. No effect unless wrap_comments = true";
73 normalize_comments: NormalizeComments, false, "Convert /* */ comments to // comments where \
74 possible";
75 normalize_doc_attributes: NormalizeDocAttributes, false, "Normalize doc attributes as doc \
76 comments";
77 format_strings: FormatStrings, false, "Format string literals where necessary";
78 format_macro_matchers: FormatMacroMatchers, false,
79 "Format the metavariable matching patterns in macros";
80 format_macro_bodies: FormatMacroBodies, false,
81 "Format the bodies of declarative macro definitions";
82 skip_macro_invocations: SkipMacroInvocations, false,
83 "Skip formatting the bodies of macros invoked with the following names.";
84 hex_literal_case: HexLiteralCaseConfig, true, "Format hexadecimal integer literals";
85 float_literal_trailing_zero: FloatLiteralTrailingZeroConfig, false,
86 "Add or remove trailing zero in floating-point literals";
87
88 empty_item_single_line: EmptyItemSingleLine, false,
90 "Put empty-body functions and impls on a single line";
91 struct_lit_single_line: StructLitSingleLine, false,
92 "Put small struct literals on a single line";
93 fn_single_line: FnSingleLine, false, "Put single-expression functions on a single line";
94 where_single_line: WhereSingleLine, false, "Force where-clauses to be on a single line";
95
96 imports_indent: ImportsIndent, false, "Indent of imports";
98 imports_layout: ImportsLayout, false, "Item layout inside a import block";
99 imports_granularity: ImportsGranularityConfig, false,
100 "Merge or split imports to the provided granularity";
101 group_imports: GroupImportsTacticConfig, false,
102 "Controls the strategy for how imports are grouped together";
103 merge_imports: MergeImports, false, "(deprecated: use imports_granularity instead)";
104
105 reorder_imports: ReorderImports, true, "Reorder import and extern crate statements \
107 alphabetically";
108 reorder_modules: ReorderModules, true, "Reorder module statements alphabetically in group";
109 reorder_impl_items: ReorderImplItems, false, "Reorder impl items";
110
111 type_punctuation_density: TypePunctuationDensity, false,
113 "Determines if '+' or '=' are wrapped in spaces in the punctuation of types";
114 space_before_colon: SpaceBeforeColon, false, "Leave a space before the colon";
115 space_after_colon: SpaceAfterColon, false, "Leave a space after the colon";
116 spaces_around_ranges: SpacesAroundRanges, false, "Put spaces around the .. and ..= range \
117 operators";
118 binop_separator: BinopSeparator, false,
119 "Where to put a binary operator when a binary expression goes multiline";
120
121 remove_nested_parens: RemoveNestedParens, true, "Remove nested parens";
123 combine_control_expr: CombineControlExpr, false, "Combine control expressions with function \
124 calls";
125 short_array_element_width_threshold: ShortArrayElementWidthThreshold, true,
126 "Width threshold for an array element to be considered short";
127 overflow_delimited_expr: OverflowDelimitedExpr, false,
128 "Allow trailing bracket/brace delimited expressions to overflow";
129 struct_field_align_threshold: StructFieldAlignThreshold, false,
130 "Align struct fields if their diffs fits within threshold";
131 enum_discrim_align_threshold: EnumDiscrimAlignThreshold, false,
132 "Align enum variants discrims, if their diffs fit within threshold";
133 match_arm_blocks: MatchArmBlocks, false, "Wrap the body of arms in blocks when it does not fit \
134 on the same line with the pattern of arms";
135 match_arm_leading_pipes: MatchArmLeadingPipeConfig, true,
136 "Determines whether leading pipes are emitted on match arms";
137 match_arm_indent: MatchArmIndent, false,
138 "Determines whether match arms are indented";
139 force_multiline_blocks: ForceMultilineBlocks, false,
140 "Force multiline closure bodies and match arms to be wrapped in a block";
141 fn_args_layout: FnArgsLayout, true,
142 "(deprecated: use fn_params_layout instead)";
143 fn_params_layout: FnParamsLayout, true,
144 "Control the layout of parameters in function signatures.";
145 brace_style: BraceStyleConfig, false, "Brace style for items";
146 control_brace_style: ControlBraceStyleConfig, false,
147 "Brace style for control flow constructs";
148 trailing_semicolon: TrailingSemicolon, false,
149 "Add trailing semicolon after break, continue and return";
150 trailing_comma: TrailingComma, false,
151 "How to handle trailing commas for lists";
152 match_block_trailing_comma: MatchBlockTrailingComma, true,
153 "Put a trailing comma after a block based match arm (non-block arms are not affected)";
154 blank_lines_upper_bound: BlankLinesUpperBound, false,
155 "Maximum number of blank lines which can be put between items";
156 blank_lines_lower_bound: BlankLinesLowerBound, false,
157 "Minimum number of blank lines which must be put between items";
158 edition: EditionConfig, true, "The edition of the parser (RFC 2052)";
159 style_edition: StyleEditionConfig, true, "The edition of the Style Guide (RFC 3338)";
160 version: VersionConfig, false, "Version of formatting rules";
161 inline_attribute_width: InlineAttributeWidth, false,
162 "Write an item and its attribute on the same line \
163 if their combined width is below a threshold";
164 format_generated_files: FormatGeneratedFiles, false, "Format generated files";
165 generated_marker_line_search_limit: GeneratedMarkerLineSearchLimit, false, "Number of lines to \
166 check for a `@generated` marker when `format_generated_files` is enabled";
167
168 merge_derives: MergeDerives, true, "Merge multiple `#[derive(...)]` into a single one";
170 use_try_shorthand: UseTryShorthand, true, "Replace uses of the try! macro by the ? shorthand";
171 use_field_init_shorthand: UseFieldInitShorthand, true, "Use field initialization shorthand if \
172 possible";
173 force_explicit_abi: ForceExplicitAbi, true, "Always print the abi for extern items";
174 condense_wildcard_suffixes: CondenseWildcardSuffixes, false, "Replace strings of _ wildcards \
175 by a single .. in tuple patterns";
176
177 color: ColorConfig, false,
179 "What Color option to use when none is supplied: Always, Never, Auto";
180 required_version: RequiredVersion, false,
181 "Require a specific version of rustfmt";
182 unstable_features: UnstableFeatures, false,
183 "Enables unstable features. Only available on nightly channel";
184 disable_all_formatting: DisableAllFormatting, true, "Don't reformat anything";
185 skip_children: SkipChildren, false, "Don't reformat out of line modules";
186 hide_parse_errors: HideParseErrors, false, "Hide errors from the parser";
187 show_parse_errors: ShowParseErrors, false, "Show errors from the parser (unstable)";
188 error_on_line_overflow: ErrorOnLineOverflow, false, "Error if unable to get all lines within \
189 max_width";
190 error_on_unformatted: ErrorOnUnformatted, false,
191 "Error if unable to get comments or string literals within max_width, \
192 or they are left with trailing whitespaces";
193 ignore: Ignore, false,
194 "Skip formatting the specified files and directories";
195
196 verbose: Verbose, false, "How much to information to emit to the user";
198 file_lines: FileLinesConfig, false,
199 "Lines to format; this is not supported in rustfmt.toml, and can only be specified \
200 via the --file-lines option";
201 emit_mode: EmitModeConfig, false,
202 "What emit Mode to use when none is supplied";
203 make_backup: MakeBackup, false, "Backup changed files";
204 print_misformatted_file_names: PrintMisformattedFileNames, true,
205 "Prints the names of mismatched files that were formatted. Prints the names of \
206 files that would be formatted when used with `--check` mode. ";
207}
208
209#[derive(Error, Debug)]
210#[error("Could not output config: {0}")]
211pub struct ToTomlError(toml::ser::Error);
212
213impl PartialConfig {
214 pub fn to_toml(&self) -> Result<String, ToTomlError> {
215 let mut cloned = self.clone();
217 cloned.file_lines = None;
218 cloned.verbose = None;
219 cloned.width_heuristics = None;
220 cloned.print_misformatted_file_names = None;
221 cloned.merge_imports = None;
222 cloned.fn_args_layout = None;
223 cloned.hide_parse_errors = None;
224
225 ::toml::to_string(&cloned).map_err(ToTomlError)
226 }
227
228 pub(super) fn to_parsed_config(
229 self,
230 style_edition_override: Option<StyleEdition>,
231 edition_override: Option<Edition>,
232 version_override: Option<Version>,
233 dir: &Path,
234 ) -> Config {
235 Config::default_for_possible_style_edition(
236 style_edition_override.or(self.style_edition),
237 edition_override.or(self.edition),
238 version_override.or(self.version),
239 )
240 .fill_from_parsed_config(self, dir)
241 }
242}
243
244fn check_semver_version(range_requirement: &str, actual: &str) -> bool {
245 let version_req = match semver::VersionReq::parse(range_requirement) {
249 Ok(r) => r,
250 Err(e) => {
251 eprintln!("Error: failed to parse required version {range_requirement:?}: {e}");
252 return false;
253 }
254 };
255 let actual_version = match semver::Version::parse(actual) {
256 Ok(v) => v,
257 Err(e) => {
258 eprintln!("Error: failed to parse current version {actual:?}: {e}");
259 return false;
260 }
261 };
262
263 version_req.matches(&actual_version)
264}
265
266impl Config {
267 pub fn default_for_possible_style_edition(
268 style_edition: Option<StyleEdition>,
269 edition: Option<Edition>,
270 version: Option<Version>,
271 ) -> Config {
272 match (style_edition, version, edition) {
279 (Some(se), _, _) => Self::default_with_style_edition(se),
280 (None, Some(Version::Two), _) => {
281 Self::default_with_style_edition(StyleEdition::Edition2024)
282 }
283 (None, Some(Version::One), _) => {
284 Self::default_with_style_edition(StyleEdition::Edition2015)
285 }
286 (None, None, Some(e)) => Self::default_with_style_edition(e.into()),
287 (None, None, None) => Config::default(),
288 }
289 }
290
291 pub(crate) fn version_meets_requirement(&self) -> bool {
292 if self.was_set().required_version() {
293 let version = env!("CARGO_PKG_VERSION");
294 let required_version = self.required_version();
295 if !check_semver_version(&required_version, version) {
296 eprintln!(
297 "Error: rustfmt version ({}) doesn't match the required version ({})",
298 version, required_version
299 );
300 return false;
301 }
302 }
303
304 true
305 }
306
307 pub(super) fn from_toml_path(
315 file_path: &Path,
316 edition: Option<Edition>,
317 style_edition: Option<StyleEdition>,
318 version: Option<Version>,
319 ) -> Result<Config, Error> {
320 let mut file = File::open(&file_path)?;
321 let mut toml = String::new();
322 file.read_to_string(&mut toml)?;
323 Config::from_toml_for_style_edition(&toml, file_path, edition, style_edition, version)
324 .map_err(|err| Error::new(ErrorKind::InvalidData, err))
325 }
326
327 pub(super) fn from_resolved_toml_path(
337 dir: &Path,
338 edition: Option<Edition>,
339 style_edition: Option<StyleEdition>,
340 version: Option<Version>,
341 ) -> Result<(Config, Option<PathBuf>), Error> {
342 let path = match resolve_project_file(dir)? {
343 Some(path) => Some(path),
344 None => config_from_user_dirs()?,
345 };
346
347 match path {
348 None => Ok((
349 Config::default_for_possible_style_edition(style_edition, edition, version),
350 None,
351 )),
352 Some(path) => Config::from_toml_path(&path, edition, style_edition, version)
353 .map(|config| (config, Some(path))),
354 }
355 }
356
357 #[allow(dead_code)]
358 pub(super) fn from_toml(toml: &str, file_path: &Path) -> Result<Config, String> {
359 Self::from_toml_for_style_edition(toml, file_path, None, None, None)
360 }
361
362 pub(crate) fn from_toml_for_style_edition(
363 toml: &str,
364 file_path: &Path,
365 edition: Option<Edition>,
366 style_edition: Option<StyleEdition>,
367 version: Option<Version>,
368 ) -> Result<Config, String> {
369 let parsed: ::toml::Value =
370 toml::from_str(toml).map_err(|e| format!("Could not parse TOML: {}", e))?;
371 let mut err = String::new();
372 let table = parsed
373 .as_table()
374 .ok_or_else(|| String::from("Parsed config was not table"))?;
375 for key in table.keys() {
376 if !Config::is_valid_name(key) {
377 let msg = &format!("Warning: Unknown configuration option `{key}`\n");
378 err.push_str(msg)
379 }
380 }
381
382 match parsed.try_into::<PartialConfig>() {
383 Ok(parsed_config) => {
384 if !err.is_empty() {
385 eprint!("{err}");
386 }
387 let dir = file_path.parent().ok_or_else(|| {
388 format!("failed to get parent directory for {}", file_path.display())
389 })?;
390
391 Ok(parsed_config.to_parsed_config(style_edition, edition, version, dir))
392 }
393 Err(e) => {
394 let err_msg = format!(
395 "The file `{}` failed to parse.\nError details: {e}",
396 file_path.display()
397 );
398 err.push_str(&err_msg);
399 Err(err_msg)
400 }
401 }
402 }
403}
404
405fn resolve_project_file(dir: &Path) -> Result<Option<PathBuf>, Error> {
410 let mut current = if dir.is_relative() {
411 env::current_dir()?.join(dir)
412 } else {
413 dir.to_path_buf()
414 };
415
416 current = fs::canonicalize(current)?;
417
418 loop {
419 match get_toml_path(¤t) {
420 Ok(Some(path)) => return Ok(Some(path)),
421 Err(e) => return Err(e),
422 _ => (),
423 }
424
425 if !current.pop() {
427 break Ok(None);
428 }
429 }
430}
431
432fn config_from_user_dirs() -> Result<Option<PathBuf>, Error> {
437 for dir in [
438 dirs::home_dir(),
439 dirs::config_dir().map(|d| d.join("rustfmt")),
440 ]
441 .iter()
442 .flatten()
443 {
444 if let Some(path) = get_toml_path(&dir)? {
445 return Ok(Some(path));
446 }
447 }
448
449 Ok(None)
450}
451
452pub fn load_config<O: CliOptions>(
455 file_path: Option<&Path>,
456 options: Option<O>,
457) -> Result<(Config, Option<PathBuf>), Error> {
458 let (over_ride, edition, style_edition, version) = match options {
459 Some(ref opts) => (
460 config_path(opts)?,
461 opts.edition(),
462 opts.style_edition(),
463 opts.version(),
464 ),
465 None => (None, None, None, None),
466 };
467
468 let result = if let Some(over_ride) = over_ride {
469 Config::from_toml_path(over_ride.as_ref(), edition, style_edition, version)
470 .map(|p| (p, Some(over_ride.to_owned())))
471 } else if let Some(file_path) = file_path {
472 Config::from_resolved_toml_path(file_path, edition, style_edition, version)
473 } else {
474 Ok((
475 Config::default_for_possible_style_edition(style_edition, edition, version),
476 None,
477 ))
478 };
479
480 result.map(|(mut c, p)| {
481 if let Some(options) = options {
482 options.apply_to(&mut c);
483 }
484 (c, p)
485 })
486}
487
488fn get_toml_path(dir: &Path) -> Result<Option<PathBuf>, Error> {
492 const CONFIG_FILE_NAMES: [&str; 2] = [".rustfmt.toml", "rustfmt.toml"];
493 for config_file_name in &CONFIG_FILE_NAMES {
494 let config_file = dir.join(config_file_name);
495 match fs::metadata(&config_file) {
496 Ok(ref md) if md.is_file() => return Ok(Some(config_file.canonicalize()?)),
499 Err(e) => {
504 if !matches!(e.kind(), ErrorKind::NotFound | ErrorKind::NotADirectory) {
505 let ctx = format!("Failed to get metadata for config file {:?}", &config_file);
506 let err = anyhow::Error::new(e).context(ctx);
507 return Err(Error::new(ErrorKind::Other, err));
508 }
509 }
510 _ => {}
511 }
512 }
513 Ok(None)
514}
515
516fn config_path(options: &dyn CliOptions) -> Result<Option<PathBuf>, Error> {
517 let config_path_not_found = |path: &str| -> Result<Option<PathBuf>, Error> {
518 Err(Error::new(
519 ErrorKind::NotFound,
520 format!(
521 "Error: unable to find a config file for the given path: `{}`",
522 path
523 ),
524 ))
525 };
526
527 match options.config_path() {
530 Some(path) if !path.exists() => config_path_not_found(path.to_str().unwrap()),
531 Some(path) if path.is_dir() => resolve_project_file(path),
532 Some(path) => Ok(Some(
533 path.canonicalize()?,
535 )),
536 None => Ok(None),
537 }
538}
539
540#[cfg(test)]
541mod test {
542 use super::*;
543 use indoc::formatdoc;
544 use std::str;
545
546 use crate::config::macro_names::{MacroName, MacroSelectors};
547 use rustfmt_config_proc_macro::{nightly_only_test, stable_only_test};
548
549 #[allow(dead_code)]
550 mod mock {
551 use super::super::*;
552 use rustfmt_config_proc_macro::config_type;
553
554 #[config_type]
555 pub(crate) enum PartiallyUnstableOption {
556 V1,
557 V2,
558 #[unstable_variant]
559 V3,
560 }
561
562 config_option_with_style_edition_default!(
563 StableOption, bool, _ => false;
564 UnstableOption, bool, _ => false;
565 PartiallyUnstable, PartiallyUnstableOption, _ => PartiallyUnstableOption::V1;
566 );
567
568 create_config! {
569 max_width: MaxWidth, true, "Maximum width of each line";
571 required_version: RequiredVersion, false, "Require a specific version of rustfmt.";
572 ignore: Ignore, false, "Skip formatting the specified files and directories.";
573 verbose: Verbose, false, "How much to information to emit to the user";
574 file_lines: FileLinesConfig, false,
575 "Lines to format; this is not supported in rustfmt.toml, and can only be specified \
576 via the --file-lines option";
577
578 imports_granularity: ImportsGranularityConfig, false, "Merge imports";
580 merge_imports: MergeImports, false, "(deprecated: use imports_granularity instead)";
581
582 fn_args_layout: FnArgsLayout, true, "(deprecated: use fn_params_layout instead)";
584 fn_params_layout: FnParamsLayout, true,
585 "Control the layout of parameters in a function signatures.";
586
587 hide_parse_errors: HideParseErrors, false,
589 "(deprecated: use show_parse_errors instead)";
590 show_parse_errors: ShowParseErrors, false,
591 "Show errors from the parser (unstable)";
592
593
594 use_small_heuristics: UseSmallHeuristics, true,
596 "Whether to use different formatting for items and \
597 expressions if they satisfy a heuristic notion of 'small'.";
598 width_heuristics: WidthHeuristicsConfig, false, "'small' heuristic values";
599
600 fn_call_width: FnCallWidth, true, "Maximum width of the args of a function call before \
601 falling back to vertical formatting.";
602 attr_fn_like_width: AttrFnLikeWidth, true, "Maximum width of the args of a \
603 function-like attributes before falling back to vertical formatting.";
604 struct_lit_width: StructLitWidth, true, "Maximum width in the body of a struct lit \
605 before falling back to vertical formatting.";
606 struct_variant_width: StructVariantWidth, true, "Maximum width in the body of a struct \
607 variant before falling back to vertical formatting.";
608 array_width: ArrayWidth, true, "Maximum width of an array literal before falling \
609 back to vertical formatting.";
610 chain_width: ChainWidth, true, "Maximum length of a chain to fit on a single line.";
611 single_line_if_else_max_width: SingleLineIfElseMaxWidth, true, "Maximum line length \
612 for single line if-else expressions. A value of zero means always break if-else \
613 expressions.";
614 single_line_let_else_max_width: SingleLineLetElseMaxWidth, false, "Maximum line length \
615 for single line let-else statements. A value of zero means always format the \
616 divergent `else` block over multiple lines.";
617
618 stable_option: StableOption, true, "A stable option";
620 unstable_option: UnstableOption, false, "An unstable option";
621 partially_unstable_option: PartiallyUnstable, true, "A partially unstable option";
622 edition: EditionConfig, true, "blah";
623 style_edition: StyleEditionConfig, true, "blah";
624 version: VersionConfig, false, "blah blah"
625 }
626
627 #[cfg(test)]
628 mod partially_unstable_option {
629 use super::{Config, PartialConfig, PartiallyUnstableOption};
630 use rustfmt_config_proc_macro::{nightly_only_test, stable_only_test};
631 use std::path::Path;
632
633 #[test]
635 fn test_from_toml_stable_value() {
636 let toml = r#"
637 partially_unstable_option = "V2"
638 "#;
639 let partial_config: PartialConfig = toml::from_str(toml).unwrap();
640 let config = Config::default();
641 let config = config.fill_from_parsed_config(partial_config, Path::new(""));
642 assert_eq!(
643 config.partially_unstable_option(),
644 PartiallyUnstableOption::V2
645 );
646 }
647
648 #[stable_only_test]
650 #[test]
651 fn test_from_toml_unstable_value_on_stable() {
652 let toml = r#"
653 partially_unstable_option = "V3"
654 "#;
655 let partial_config: PartialConfig = toml::from_str(toml).unwrap();
656 let config = Config::default();
657 let config = config.fill_from_parsed_config(partial_config, Path::new(""));
658 assert_eq!(
659 config.partially_unstable_option(),
660 PartiallyUnstableOption::V1
662 );
663 }
664
665 #[nightly_only_test]
667 #[test]
668 fn test_from_toml_unstable_value_on_nightly() {
669 let toml = r#"
670 partially_unstable_option = "V3"
671 "#;
672 let partial_config: PartialConfig = toml::from_str(toml).unwrap();
673 let config = Config::default();
674 let config = config.fill_from_parsed_config(partial_config, Path::new(""));
675 assert_eq!(
676 config.partially_unstable_option(),
677 PartiallyUnstableOption::V3
678 );
679 }
680 }
681 }
682
683 #[test]
684 fn test_config_set() {
685 let mut config = Config::default();
686 config.set().verbose(Verbosity::Quiet);
687 assert_eq!(config.verbose(), Verbosity::Quiet);
688 config.set().verbose(Verbosity::Normal);
689 assert_eq!(config.verbose(), Verbosity::Normal);
690 }
691
692 #[test]
693 fn test_config_used_to_toml() {
694 let config = Config::default();
695
696 let merge_derives = config.merge_derives();
697 let skip_children = config.skip_children();
698
699 let used_options = config.used_options();
700 let toml = used_options.to_toml().unwrap();
701 assert_eq!(
702 toml,
703 formatdoc! {"
704 merge_derives = {merge_derives}
705 skip_children = {skip_children}
706 "}
707 );
708 }
709
710 #[test]
711 fn test_was_set() {
712 let config = Config::from_toml("hard_tabs = true", Path::new("./rustfmt.toml")).unwrap();
713
714 assert_eq!(config.was_set().hard_tabs(), true);
715 assert_eq!(config.was_set().verbose(), false);
716 }
717
718 const PRINT_DOCS_STABLE_OPTION: &str = "stable_option <boolean> Default: false";
719 const PRINT_DOCS_UNSTABLE_OPTION: &str = "unstable_option <boolean> Default: false (unstable)";
720 const PRINT_DOCS_PARTIALLY_UNSTABLE_OPTION: &str =
721 "partially_unstable_option [V1|V2|V3 (unstable)] Default: V1";
722
723 #[test]
724 fn test_print_docs_exclude_unstable() {
725 use self::mock::Config;
726
727 let mut output = Vec::new();
728 Config::print_docs(&mut output, false);
729
730 let s = str::from_utf8(&output).unwrap();
731 assert_eq!(s.contains(PRINT_DOCS_STABLE_OPTION), true);
732 assert_eq!(s.contains(PRINT_DOCS_UNSTABLE_OPTION), false);
733 assert_eq!(s.contains(PRINT_DOCS_PARTIALLY_UNSTABLE_OPTION), true);
734 }
735
736 #[test]
737 fn test_print_docs_include_unstable() {
738 use self::mock::Config;
739
740 let mut output = Vec::new();
741 Config::print_docs(&mut output, true);
742
743 let s = str::from_utf8(&output).unwrap();
744 assert_eq!(s.contains(PRINT_DOCS_STABLE_OPTION), true);
745 assert_eq!(s.contains(PRINT_DOCS_UNSTABLE_OPTION), true);
746 assert_eq!(s.contains(PRINT_DOCS_PARTIALLY_UNSTABLE_OPTION), true);
747 }
748
749 #[test]
750 fn test_dump_default_config() {
751 let default_config = format!(
752 r#"max_width = 100
753hard_tabs = false
754tab_spaces = 4
755newline_style = "Auto"
756indent_style = "Block"
757use_small_heuristics = "Default"
758fn_call_width = 60
759attr_fn_like_width = 70
760struct_lit_width = 18
761struct_variant_width = 35
762array_width = 60
763chain_width = 60
764single_line_if_else_max_width = 50
765single_line_let_else_max_width = 50
766wrap_comments = false
767format_code_in_doc_comments = false
768doc_comment_code_block_width = 100
769doc_comment_code_block_small_heuristics = "Inherit"
770comment_width = 80
771normalize_comments = false
772normalize_doc_attributes = false
773format_strings = false
774format_macro_matchers = false
775format_macro_bodies = true
776skip_macro_invocations = []
777hex_literal_case = "Preserve"
778float_literal_trailing_zero = "Preserve"
779empty_item_single_line = true
780struct_lit_single_line = true
781fn_single_line = false
782where_single_line = false
783imports_indent = "Block"
784imports_layout = "Mixed"
785imports_granularity = "Preserve"
786group_imports = "Preserve"
787reorder_imports = true
788reorder_modules = true
789reorder_impl_items = false
790type_punctuation_density = "Wide"
791space_before_colon = false
792space_after_colon = true
793spaces_around_ranges = false
794binop_separator = "Front"
795remove_nested_parens = true
796combine_control_expr = true
797short_array_element_width_threshold = 10
798overflow_delimited_expr = false
799struct_field_align_threshold = 0
800enum_discrim_align_threshold = 0
801match_arm_blocks = true
802match_arm_leading_pipes = "Never"
803match_arm_indent = true
804force_multiline_blocks = false
805fn_params_layout = "Tall"
806brace_style = "SameLineWhere"
807control_brace_style = "AlwaysSameLine"
808trailing_semicolon = true
809trailing_comma = "Vertical"
810match_block_trailing_comma = false
811blank_lines_upper_bound = 1
812blank_lines_lower_bound = 0
813edition = "2015"
814style_edition = "2015"
815version = "One"
816inline_attribute_width = 0
817format_generated_files = true
818generated_marker_line_search_limit = 5
819merge_derives = true
820use_try_shorthand = false
821use_field_init_shorthand = false
822force_explicit_abi = true
823condense_wildcard_suffixes = false
824color = "Auto"
825required_version = "{}"
826unstable_features = false
827disable_all_formatting = false
828skip_children = false
829show_parse_errors = true
830error_on_line_overflow = false
831error_on_unformatted = false
832ignore = []
833emit_mode = "Files"
834make_backup = false
835"#,
836 env!("CARGO_PKG_VERSION")
837 );
838 let toml = Config::default().all_options().to_toml().unwrap();
839 assert_eq!(&toml, &default_config);
840 }
841
842 #[test]
843 fn test_dump_style_edition_2024_config() {
844 let edition_2024_config = format!(
845 r#"max_width = 100
846hard_tabs = false
847tab_spaces = 4
848newline_style = "Auto"
849indent_style = "Block"
850use_small_heuristics = "Default"
851fn_call_width = 60
852attr_fn_like_width = 70
853struct_lit_width = 18
854struct_variant_width = 35
855array_width = 60
856chain_width = 60
857single_line_if_else_max_width = 50
858single_line_let_else_max_width = 50
859wrap_comments = false
860format_code_in_doc_comments = false
861doc_comment_code_block_width = 100
862doc_comment_code_block_small_heuristics = "Inherit"
863comment_width = 80
864normalize_comments = false
865normalize_doc_attributes = false
866format_strings = false
867format_macro_matchers = false
868format_macro_bodies = true
869skip_macro_invocations = []
870hex_literal_case = "Preserve"
871float_literal_trailing_zero = "Preserve"
872empty_item_single_line = true
873struct_lit_single_line = true
874fn_single_line = false
875where_single_line = false
876imports_indent = "Block"
877imports_layout = "Mixed"
878imports_granularity = "Preserve"
879group_imports = "Preserve"
880reorder_imports = true
881reorder_modules = true
882reorder_impl_items = false
883type_punctuation_density = "Wide"
884space_before_colon = false
885space_after_colon = true
886spaces_around_ranges = false
887binop_separator = "Front"
888remove_nested_parens = true
889combine_control_expr = true
890short_array_element_width_threshold = 10
891overflow_delimited_expr = false
892struct_field_align_threshold = 0
893enum_discrim_align_threshold = 0
894match_arm_blocks = true
895match_arm_leading_pipes = "Never"
896match_arm_indent = true
897force_multiline_blocks = false
898fn_params_layout = "Tall"
899brace_style = "SameLineWhere"
900control_brace_style = "AlwaysSameLine"
901trailing_semicolon = true
902trailing_comma = "Vertical"
903match_block_trailing_comma = false
904blank_lines_upper_bound = 1
905blank_lines_lower_bound = 0
906edition = "2015"
907style_edition = "2024"
908version = "Two"
909inline_attribute_width = 0
910format_generated_files = true
911generated_marker_line_search_limit = 5
912merge_derives = true
913use_try_shorthand = false
914use_field_init_shorthand = false
915force_explicit_abi = true
916condense_wildcard_suffixes = false
917color = "Auto"
918required_version = "{}"
919unstable_features = false
920disable_all_formatting = false
921skip_children = false
922show_parse_errors = true
923error_on_line_overflow = false
924error_on_unformatted = false
925ignore = []
926emit_mode = "Files"
927make_backup = false
928"#,
929 env!("CARGO_PKG_VERSION")
930 );
931 let toml = Config::default_with_style_edition(StyleEdition::Edition2024)
932 .all_options()
933 .to_toml()
934 .unwrap();
935 assert_eq!(&toml, &edition_2024_config);
936 }
937
938 #[test]
939 fn test_editions_2015_2018_2021_identical() {
940 let get_edition_toml = |style_edition: StyleEdition| {
941 Config::default_with_style_edition(style_edition)
942 .all_options()
943 .to_toml()
944 .unwrap()
945 };
946 let edition2015 = get_edition_toml(StyleEdition::Edition2015);
947 let edition2018 = get_edition_toml(StyleEdition::Edition2018);
948 let edition2021 = get_edition_toml(StyleEdition::Edition2021);
949 assert_eq!(edition2015, edition2018);
950 assert_eq!(edition2018, edition2021);
951 }
952
953 #[stable_only_test]
954 #[test]
955 fn test_as_not_nightly_channel() {
956 let mut config = Config::default();
957 assert_eq!(config.was_set().unstable_features(), false);
958 config.set().unstable_features(true);
959 assert_eq!(config.was_set().unstable_features(), false);
960 }
961
962 #[nightly_only_test]
963 #[test]
964 fn test_as_nightly_channel() {
965 let mut config = Config::default();
966 config.set().unstable_features(true);
967 assert_eq!(config.was_set().unstable_features(), false);
970 config.set().unstable_features(true);
971 assert_eq!(config.unstable_features(), true);
972 }
973
974 #[nightly_only_test]
975 #[test]
976 fn test_unstable_from_toml() {
977 let config =
978 Config::from_toml("unstable_features = true", Path::new("./rustfmt.toml")).unwrap();
979 assert_eq!(config.was_set().unstable_features(), true);
980 assert_eq!(config.unstable_features(), true);
981 }
982
983 #[test]
984 fn test_set_cli() {
985 let mut config = Config::default();
986 assert_eq!(config.was_set().edition(), false);
987 assert_eq!(config.was_set_cli().edition(), false);
988 config.set().edition(Edition::Edition2021);
989 assert_eq!(config.was_set().edition(), false);
990 assert_eq!(config.was_set_cli().edition(), false);
991 config.set_cli().edition(Edition::Edition2021);
992 assert_eq!(config.was_set().edition(), false);
993 assert_eq!(config.was_set_cli().edition(), true);
994 assert_eq!(config.was_set_cli().emit_mode(), false);
995 }
996
997 #[cfg(test)]
998 mod deprecated_option_merge_imports {
999 use super::*;
1000
1001 #[nightly_only_test]
1002 #[test]
1003 fn test_old_option_set() {
1004 let toml = r#"
1005 unstable_features = true
1006 merge_imports = true
1007 "#;
1008 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1009 assert_eq!(config.imports_granularity(), ImportGranularity::Crate);
1010 }
1011
1012 #[nightly_only_test]
1013 #[test]
1014 fn test_both_set() {
1015 let toml = r#"
1016 unstable_features = true
1017 merge_imports = true
1018 imports_granularity = "Preserve"
1019 "#;
1020 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1021 assert_eq!(config.imports_granularity(), ImportGranularity::Preserve);
1022 }
1023
1024 #[nightly_only_test]
1025 #[test]
1026 fn test_new_overridden() {
1027 let toml = r#"
1028 unstable_features = true
1029 merge_imports = true
1030 "#;
1031 let mut config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1032 config.override_value("imports_granularity", "Preserve");
1033 assert_eq!(config.imports_granularity(), ImportGranularity::Preserve);
1034 }
1035
1036 #[nightly_only_test]
1037 #[test]
1038 fn test_old_overridden() {
1039 let toml = r#"
1040 unstable_features = true
1041 imports_granularity = "Module"
1042 "#;
1043 let mut config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1044 config.override_value("merge_imports", "true");
1045 assert_eq!(config.imports_granularity(), ImportGranularity::Module);
1047 }
1048 }
1049
1050 #[cfg(test)]
1051 mod use_small_heuristics {
1052 use super::*;
1053
1054 #[test]
1055 fn test_default_sets_correct_widths() {
1056 let toml = r#"
1057 use_small_heuristics = "Default"
1058 max_width = 200
1059 "#;
1060 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1061 assert_eq!(config.array_width(), 120);
1062 assert_eq!(config.attr_fn_like_width(), 140);
1063 assert_eq!(config.chain_width(), 120);
1064 assert_eq!(config.fn_call_width(), 120);
1065 assert_eq!(config.single_line_if_else_max_width(), 100);
1066 assert_eq!(config.struct_lit_width(), 36);
1067 assert_eq!(config.struct_variant_width(), 70);
1068 }
1069
1070 #[test]
1071 fn test_max_sets_correct_widths() {
1072 let toml = r#"
1073 use_small_heuristics = "Max"
1074 max_width = 120
1075 "#;
1076 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1077 assert_eq!(config.array_width(), 120);
1078 assert_eq!(config.attr_fn_like_width(), 120);
1079 assert_eq!(config.chain_width(), 120);
1080 assert_eq!(config.fn_call_width(), 120);
1081 assert_eq!(config.single_line_if_else_max_width(), 120);
1082 assert_eq!(config.struct_lit_width(), 120);
1083 assert_eq!(config.struct_variant_width(), 120);
1084 }
1085
1086 #[test]
1087 fn test_off_sets_correct_widths() {
1088 let toml = r#"
1089 use_small_heuristics = "Off"
1090 max_width = 100
1091 "#;
1092 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1093 assert_eq!(config.array_width(), usize::MAX);
1094 assert_eq!(config.attr_fn_like_width(), usize::MAX);
1095 assert_eq!(config.chain_width(), usize::MAX);
1096 assert_eq!(config.fn_call_width(), usize::MAX);
1097 assert_eq!(config.single_line_if_else_max_width(), 0);
1098 assert_eq!(config.struct_lit_width(), 0);
1099 assert_eq!(config.struct_variant_width(), 0);
1100 }
1101
1102 #[test]
1103 fn test_override_works_with_default() {
1104 let toml = r#"
1105 use_small_heuristics = "Default"
1106 array_width = 20
1107 attr_fn_like_width = 40
1108 chain_width = 20
1109 fn_call_width = 90
1110 single_line_if_else_max_width = 40
1111 struct_lit_width = 30
1112 struct_variant_width = 34
1113 "#;
1114 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1115 assert_eq!(config.array_width(), 20);
1116 assert_eq!(config.attr_fn_like_width(), 40);
1117 assert_eq!(config.chain_width(), 20);
1118 assert_eq!(config.fn_call_width(), 90);
1119 assert_eq!(config.single_line_if_else_max_width(), 40);
1120 assert_eq!(config.struct_lit_width(), 30);
1121 assert_eq!(config.struct_variant_width(), 34);
1122 }
1123
1124 #[test]
1125 fn test_override_with_max() {
1126 let toml = r#"
1127 use_small_heuristics = "Max"
1128 array_width = 20
1129 attr_fn_like_width = 40
1130 chain_width = 20
1131 fn_call_width = 90
1132 single_line_if_else_max_width = 40
1133 struct_lit_width = 30
1134 struct_variant_width = 34
1135 "#;
1136 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1137 assert_eq!(config.array_width(), 20);
1138 assert_eq!(config.attr_fn_like_width(), 40);
1139 assert_eq!(config.chain_width(), 20);
1140 assert_eq!(config.fn_call_width(), 90);
1141 assert_eq!(config.single_line_if_else_max_width(), 40);
1142 assert_eq!(config.struct_lit_width(), 30);
1143 assert_eq!(config.struct_variant_width(), 34);
1144 }
1145
1146 #[test]
1147 fn test_override_with_off() {
1148 let toml = r#"
1149 use_small_heuristics = "Off"
1150 array_width = 20
1151 attr_fn_like_width = 40
1152 chain_width = 20
1153 fn_call_width = 90
1154 single_line_if_else_max_width = 40
1155 struct_lit_width = 30
1156 struct_variant_width = 34
1157 "#;
1158 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1159 assert_eq!(config.array_width(), 20);
1160 assert_eq!(config.attr_fn_like_width(), 40);
1161 assert_eq!(config.chain_width(), 20);
1162 assert_eq!(config.fn_call_width(), 90);
1163 assert_eq!(config.single_line_if_else_max_width(), 40);
1164 assert_eq!(config.struct_lit_width(), 30);
1165 assert_eq!(config.struct_variant_width(), 34);
1166 }
1167
1168 #[test]
1169 fn test_fn_call_width_config_exceeds_max_width() {
1170 let toml = r#"
1171 max_width = 90
1172 fn_call_width = 95
1173 "#;
1174 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1175 assert_eq!(config.fn_call_width(), 90);
1176 }
1177
1178 #[test]
1179 fn test_attr_fn_like_width_config_exceeds_max_width() {
1180 let toml = r#"
1181 max_width = 80
1182 attr_fn_like_width = 90
1183 "#;
1184 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1185 assert_eq!(config.attr_fn_like_width(), 80);
1186 }
1187
1188 #[test]
1189 fn test_struct_lit_config_exceeds_max_width() {
1190 let toml = r#"
1191 max_width = 78
1192 struct_lit_width = 90
1193 "#;
1194 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1195 assert_eq!(config.struct_lit_width(), 78);
1196 }
1197
1198 #[test]
1199 fn test_struct_variant_width_config_exceeds_max_width() {
1200 let toml = r#"
1201 max_width = 80
1202 struct_variant_width = 90
1203 "#;
1204 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1205 assert_eq!(config.struct_variant_width(), 80);
1206 }
1207
1208 #[test]
1209 fn test_array_width_config_exceeds_max_width() {
1210 let toml = r#"
1211 max_width = 60
1212 array_width = 80
1213 "#;
1214 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1215 assert_eq!(config.array_width(), 60);
1216 }
1217
1218 #[test]
1219 fn test_chain_width_config_exceeds_max_width() {
1220 let toml = r#"
1221 max_width = 80
1222 chain_width = 90
1223 "#;
1224 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1225 assert_eq!(config.chain_width(), 80);
1226 }
1227
1228 #[test]
1229 fn test_single_line_if_else_max_width_config_exceeds_max_width() {
1230 let toml = r#"
1231 max_width = 70
1232 single_line_if_else_max_width = 90
1233 "#;
1234 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1235 assert_eq!(config.single_line_if_else_max_width(), 70);
1236 }
1237
1238 #[test]
1239 fn test_override_fn_call_width_exceeds_max_width() {
1240 let mut config = Config::default();
1241 config.override_value("fn_call_width", "101");
1242 assert_eq!(config.fn_call_width(), 100);
1243 }
1244
1245 #[test]
1246 fn test_override_attr_fn_like_width_exceeds_max_width() {
1247 let mut config = Config::default();
1248 config.override_value("attr_fn_like_width", "101");
1249 assert_eq!(config.attr_fn_like_width(), 100);
1250 }
1251
1252 #[test]
1253 fn test_override_struct_lit_exceeds_max_width() {
1254 let mut config = Config::default();
1255 config.override_value("struct_lit_width", "101");
1256 assert_eq!(config.struct_lit_width(), 100);
1257 }
1258
1259 #[test]
1260 fn test_override_struct_variant_width_exceeds_max_width() {
1261 let mut config = Config::default();
1262 config.override_value("struct_variant_width", "101");
1263 assert_eq!(config.struct_variant_width(), 100);
1264 }
1265
1266 #[test]
1267 fn test_override_array_width_exceeds_max_width() {
1268 let mut config = Config::default();
1269 config.override_value("array_width", "101");
1270 assert_eq!(config.array_width(), 100);
1271 }
1272
1273 #[test]
1274 fn test_override_chain_width_exceeds_max_width() {
1275 let mut config = Config::default();
1276 config.override_value("chain_width", "101");
1277 assert_eq!(config.chain_width(), 100);
1278 }
1279
1280 #[test]
1281 fn test_override_single_line_if_else_max_width_exceeds_max_width() {
1282 let mut config = Config::default();
1283 config.override_value("single_line_if_else_max_width", "101");
1284 assert_eq!(config.single_line_if_else_max_width(), 100);
1285 }
1286 }
1287
1288 #[cfg(test)]
1289 mod partially_unstable_option {
1290 use super::mock::{Config, PartiallyUnstableOption};
1291
1292 #[test]
1294 fn test_override_stable_value() {
1295 let mut config = Config::default();
1296 config.override_value("partially_unstable_option", "V2");
1297 assert_eq!(
1298 config.partially_unstable_option(),
1299 PartiallyUnstableOption::V2
1300 );
1301 }
1302
1303 #[test]
1305 fn test_override_unstable_value() {
1306 let mut config = Config::default();
1307 config.override_value("partially_unstable_option", "V3");
1308 assert_eq!(
1309 config.partially_unstable_option(),
1310 PartiallyUnstableOption::V3
1311 );
1312 }
1313 }
1314
1315 #[test]
1316 fn test_override_skip_macro_invocations() {
1317 let mut config = Config::default();
1318 config.override_value("skip_macro_invocations", r#"["*", "println"]"#);
1319 assert_eq!(
1320 config.skip_macro_invocations(),
1321 MacroSelectors(vec![
1322 MacroSelector::All,
1323 MacroSelector::Name(MacroName::new("println".to_owned()))
1324 ])
1325 );
1326 }
1327
1328 #[cfg(test)]
1329 mod required_version {
1330 use super::*;
1331
1332 #[allow(dead_code)] fn get_current_version() -> semver::Version {
1334 semver::Version::parse(env!("CARGO_PKG_VERSION")).unwrap()
1335 }
1336
1337 #[nightly_only_test]
1338 #[test]
1339 fn test_required_version_default() {
1340 let config = Config::default();
1341 assert!(config.version_meets_requirement());
1342 }
1343
1344 #[nightly_only_test]
1345 #[test]
1346 fn test_current_required_version() {
1347 let toml = format!("required_version=\"{}\"", env!("CARGO_PKG_VERSION"));
1348 let config = Config::from_toml(&toml, Path::new("./rustfmt.toml")).unwrap();
1349
1350 assert!(config.version_meets_requirement());
1351 }
1352
1353 #[nightly_only_test]
1354 #[test]
1355 fn test_required_version_above() {
1356 let toml = "required_version=\"1000.0.0\"";
1357 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1358
1359 assert!(!config.version_meets_requirement());
1360 }
1361
1362 #[nightly_only_test]
1363 #[test]
1364 fn test_required_version_below() {
1365 let versions = vec!["0.0.0", "0.0.1", "0.1.0"];
1366
1367 for version in versions {
1368 let toml = format!("required_version=\"{}\"", version.to_string());
1369 let config = Config::from_toml(&toml, Path::new("./rustfmt.toml")).unwrap();
1370
1371 assert!(!config.version_meets_requirement());
1372 }
1373 }
1374
1375 #[nightly_only_test]
1376 #[test]
1377 fn test_required_version_tilde() {
1378 let toml = format!("required_version=\"~{}\"", env!("CARGO_PKG_VERSION"));
1379 let config = Config::from_toml(&toml, Path::new("./rustfmt.toml")).unwrap();
1380
1381 assert!(config.version_meets_requirement());
1382 }
1383
1384 #[nightly_only_test]
1385 #[test]
1386 fn test_required_version_caret() {
1387 let current_version = get_current_version();
1388
1389 for minor in current_version.minor..0 {
1390 let toml = format!(
1391 "required_version=\"^{}.{}.0\"",
1392 current_version.major.to_string(),
1393 minor.to_string()
1394 );
1395 let config = Config::from_toml(&toml, Path::new("./rustfmt.toml")).unwrap();
1396
1397 assert!(!config.version_meets_requirement());
1398 }
1399 }
1400
1401 #[nightly_only_test]
1402 #[test]
1403 fn test_required_version_greater_than() {
1404 let toml = "required_version=\">1.0.0\"";
1405 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1406
1407 assert!(config.version_meets_requirement());
1408 }
1409
1410 #[nightly_only_test]
1411 #[test]
1412 fn test_required_version_less_than() {
1413 let toml = "required_version=\"<1.0.0\"";
1414 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1415
1416 assert!(!config.version_meets_requirement());
1417 }
1418
1419 #[nightly_only_test]
1420 #[test]
1421 fn test_required_version_range() {
1422 let current_version = get_current_version();
1423
1424 let toml = format!(
1425 "required_version=\">={}.0.0, <{}.0.0\"",
1426 current_version.major,
1427 current_version.major + 1
1428 );
1429 let config = Config::from_toml(&toml, Path::new("./rustfmt.toml")).unwrap();
1430
1431 assert!(config.version_meets_requirement());
1432 }
1433
1434 #[nightly_only_test]
1435 #[test]
1436 fn test_required_version_exact_boundary() {
1437 let toml = format!("required_version=\"{}\"", get_current_version().to_string());
1438 let config = Config::from_toml(&toml, Path::new("./rustfmt.toml")).unwrap();
1439
1440 assert!(config.version_meets_requirement());
1441 }
1442
1443 #[nightly_only_test]
1444 #[test]
1445 fn test_required_version_pre_release() {
1446 let toml = format!(
1447 "required_version=\"^{}-alpha\"",
1448 get_current_version().to_string()
1449 );
1450 let config = Config::from_toml(&toml, Path::new("./rustfmt.toml")).unwrap();
1451
1452 assert!(config.version_meets_requirement());
1453 }
1454
1455 #[nightly_only_test]
1456 #[test]
1457 fn test_required_version_with_build_metadata() {
1458 let toml = format!(
1459 "required_version=\"{}+build.1\"",
1460 get_current_version().to_string()
1461 );
1462
1463 let config = Config::from_toml(&toml, Path::new("./rustfmt.toml")).unwrap();
1464
1465 assert!(config.version_meets_requirement());
1466 }
1467
1468 #[nightly_only_test]
1469 #[test]
1470 fn test_required_version_invalid_specification() {
1471 let toml = "required_version=\"not.a.version\"";
1472 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1473
1474 assert!(!config.version_meets_requirement())
1475 }
1476
1477 #[nightly_only_test]
1478 #[test]
1479 fn test_required_version_complex_range() {
1480 let current_version = get_current_version();
1481
1482 let toml = format!(
1483 "required_version=\">={}.0.0, <{}.0.0, ~{}.{}.0\"",
1484 current_version.major,
1485 current_version.major + 1,
1486 current_version.major,
1487 current_version.minor
1488 );
1489 let config = Config::from_toml(&toml, Path::new("./rustfmt.toml")).unwrap();
1490
1491 assert!(config.version_meets_requirement());
1492 }
1493
1494 #[nightly_only_test]
1495 #[test]
1496 fn test_required_version_wildcard_major() {
1497 let toml = "required_version=\"1.x\"";
1498 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1499
1500 assert!(config.version_meets_requirement());
1501 }
1502
1503 #[nightly_only_test]
1504 #[test]
1505 fn test_required_version_wildcard_any() {
1506 let toml = "required_version=\"*\"";
1507 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1508
1509 assert!(config.version_meets_requirement());
1510 }
1511
1512 #[nightly_only_test]
1513 #[test]
1514 fn test_required_version_major_version_zero() {
1515 let toml = "required_version=\"0.1.0\"";
1516 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1517
1518 assert!(!config.version_meets_requirement());
1519 }
1520
1521 #[nightly_only_test]
1522 #[test]
1523 fn test_required_version_future_major_version() {
1524 let toml = "required_version=\"3.0.0\"";
1525 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1526
1527 assert!(!config.version_meets_requirement());
1528 }
1529
1530 #[nightly_only_test]
1531 #[test]
1532 fn test_required_version_fail_different_operator() {
1533 let toml = "required_version=\"!=1.0.0\"";
1535 let config = Config::from_toml(toml, Path::new("./rustfmt.toml")).unwrap();
1536
1537 assert!(!config.version_meets_requirement());
1538 }
1539 }
1540
1541 #[cfg(test)]
1542 mod check_semver_version {
1543 use super::*;
1544
1545 #[test]
1546 fn test_default_caret_match() {
1547 assert!(check_semver_version("1.0.0", "1.0.0"));
1550 assert!(check_semver_version("1.0.0", "1.1.0"));
1551 assert!(check_semver_version("1.0.0", "1.0.1"));
1552 assert!(!check_semver_version("1.0.0", "2.1.0"));
1553 assert!(!check_semver_version("1.0.0", "0.1.0"));
1554 assert!(!check_semver_version("1.0.0", "0.0.1"));
1555 }
1556
1557 #[test]
1558 fn test_explicit_exact_match() {
1559 assert!(check_semver_version("=1.0.0", "1.0.0"));
1560 assert!(!check_semver_version("=1.0.0", "1.1.0"));
1561 assert!(!check_semver_version("=1.0.0", "1.0.1"));
1562 assert!(!check_semver_version("=1.0.0", "2.1.0"));
1563 assert!(!check_semver_version("=1.0.0", "0.1.0"));
1564 assert!(!check_semver_version("=1.0.0", "0.0.1"));
1565 }
1566
1567 #[test]
1568 fn test_version_mismatch() {
1569 assert!(!check_semver_version("2.0.0", "1.0.0"));
1570 }
1571
1572 #[test]
1573 fn test_patch_version_greater() {
1574 assert!(check_semver_version("^1.0.0", "1.0.1"));
1575 }
1576
1577 #[test]
1578 fn test_minor_version_greater() {
1579 assert!(check_semver_version("^1.0.0", "1.1.0"));
1580 }
1581
1582 #[test]
1583 fn test_major_version_less() {
1584 assert!(!check_semver_version("1.0.0", "0.9.0"));
1585 }
1586
1587 #[test]
1588 fn test_prerelease_less_than_release() {
1589 assert!(!check_semver_version("1.0.0", "1.0.0-alpha"));
1590 }
1591
1592 #[test]
1593 fn test_prerelease_version_specific_match() {
1594 assert!(check_semver_version("1.0.0-alpha", "1.0.0-alpha"));
1595 }
1596
1597 #[test]
1598 fn test_build_metadata_ignored() {
1599 assert!(check_semver_version("1.0.0", "1.0.0+build.1"));
1600 }
1601
1602 #[test]
1603 fn test_greater_than_requirement() {
1604 assert!(check_semver_version(">1.0.0", "1.1.0"));
1605 }
1606
1607 #[test]
1608 fn test_less_than_requirement_fails_when_greater() {
1609 assert!(!check_semver_version("<1.0.0", "1.1.0"));
1610 }
1611
1612 #[test]
1613 fn test_caret_requirement_matches_minor_update() {
1614 assert!(check_semver_version("^1.1.0", "1.2.0"));
1615 }
1616
1617 #[test]
1618 fn test_tilde_requirement_matches_patch_update() {
1619 assert!(check_semver_version("~1.0.0", "1.0.1"));
1620 }
1621
1622 #[test]
1623 fn test_range_requirement_inclusive() {
1624 assert!(check_semver_version(">=1.0.0, <2.0.0", "1.5.0"));
1625 }
1626
1627 #[test]
1628 fn test_pre_release_specific_match() {
1629 assert!(check_semver_version("1.0.0-alpha.1", "1.0.0-alpha.1"));
1630 }
1631
1632 #[test]
1633 fn test_pre_release_non_match_when_requiring_release() {
1634 assert!(!check_semver_version("1.0.0", "1.0.0-alpha.1"));
1635 }
1636
1637 #[test]
1640 fn test_invalid_or() {
1641 assert!(!check_semver_version("1.0.0 || 2.0.0", "1.0.0"));
1642 assert!(!check_semver_version("1.0.0 || 2.0.0", "2.0.0"));
1643 assert!(!check_semver_version("1.0.0 || 2.0.0", "3.0.0"));
1644 }
1645
1646 #[test]
1647 fn test_wildcard_match_minor() {
1648 assert!(check_semver_version("1.*", "1.1.0"));
1649 assert!(check_semver_version("1.*, <2.0.0", "1.1.0"));
1650 }
1651
1652 #[test]
1653 fn test_wildcard_mismatch() {
1654 assert!(!check_semver_version("1.*, <2.0.0", "2.1.0"));
1655 assert!(!check_semver_version("1.*, <2.0.0", "2.0.0"));
1656 assert!(!check_semver_version("1.*, <2.*", "2.1.0"));
1657 assert!(!check_semver_version("1.*, <2.*", "2.0.0"));
1658
1659 assert!(!check_semver_version("1.*, >2.0.0", "1.1.0"));
1660 assert!(!check_semver_version("1.*, >2.0.0", "1.0.0"));
1661 assert!(!check_semver_version("1.*, >2.*", "1.1.0"));
1662 assert!(!check_semver_version("1.*, >2.*", "1.0.0"));
1663
1664 assert!(!check_semver_version("<1.5.0, >1.10.*", "1.6.0"));
1665 }
1666
1667 #[test]
1668 fn test_wildcard_match_major() {
1669 assert!(check_semver_version("2.*", "2.0.0"));
1670 }
1671
1672 #[test]
1673 fn test_wildcard_match_patch() {
1674 assert!(check_semver_version("1.0.*", "1.0.1"));
1675 }
1676
1677 #[test]
1678 fn test_invalid_inputs() {
1679 assert!(!check_semver_version("not.a.requirement", "1.0.0"));
1680 assert!(!check_semver_version("1.0.0", "not.a.version"));
1681 }
1682
1683 #[test]
1684 fn test_version_with_pre_release_and_build() {
1685 assert!(check_semver_version("1.0.0-alpha", "1.0.0-alpha+001"));
1686 }
1687
1688 #[test]
1690 fn test_pre_release_numeric_vs_alphanumeric() {
1691 assert!(!check_semver_version("^1.0.0-alpha.beta", "1.0.0-alpha.1"));
1692 assert!(check_semver_version("^1.0.0-alpha.1", "1.0.0-alpha.beta"));
1693 }
1694
1695 #[test]
1697 fn test_wildcard_any() {
1698 assert!(check_semver_version("*", "1.0.0"));
1699 assert!(check_semver_version("*", "1.0.0+build"));
1700 }
1701
1702 #[test]
1704 fn test_pre_release_lexicographic_ordering() {
1705 assert!(check_semver_version(
1706 "^1.0.0-alpha.alpha",
1707 "1.0.0-alpha.beta",
1708 ));
1709 assert!(!check_semver_version(
1710 "^1.0.0-alpha.beta",
1711 "1.0.0-alpha.alpha",
1712 ));
1713 }
1714
1715 #[test]
1717 fn test_wildcard_any_with_range() {
1718 assert!(!check_semver_version("*, <2.0.0", "1.0.0"));
1719 assert!(!check_semver_version("*, 1.0.0", "1.5.0"));
1720 }
1721 }
1722}