Skip to content

Commit 352bf91

Browse files
feat: don't format w/invalid attributes
1 parent cf1f0cf commit 352bf91

File tree

2 files changed

+149
-2
lines changed

2 files changed

+149
-2
lines changed

src/formatting/report.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ impl FormatResult {
7878
pub(crate) fn formatted_snippet(&self) -> &FormattedSnippet {
7979
&self.formatted_snippet
8080
}
81+
82+
pub(crate) fn has_error_kind(&self, kind: ErrorKind) -> bool {
83+
self.all_errors().any(|e| e.kind() == kind)
84+
}
8185
}
8286

8387
impl FormatReport {
@@ -154,6 +158,16 @@ impl FormatReport {
154158
.insert(format_error);
155159
}
156160

161+
fn has_error_kind(&self, kind: ErrorKind) -> bool {
162+
RefCell::borrow(&self.format_result)
163+
.iter()
164+
.any(|(_, format_result)| format_result.has_error_kind(kind))
165+
}
166+
167+
pub fn has_invalid_rustfmt_attribute_errors(&self) -> bool {
168+
self.has_error_kind(ErrorKind::BadAttr)
169+
}
170+
157171
pub fn has_errors(&self) -> bool {
158172
RefCell::borrow(&self.format_result)
159173
.iter()

src/rustfmt/main.rs

Lines changed: 135 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ struct Opt {
9292
/// Print verbose output.
9393
#[structopt(short, long)]
9494
verbose: bool,
95+
/// Continue with formatting even if there are invalid rustfmt attributes.
96+
///
97+
/// Exits with 1 if there are any invalid rustfmt attributes.
98+
#[structopt(long = "format-with-invalid-attrs")]
99+
format_with_invalid_attributes: bool,
95100

96101
// Nightly-only options.
97102
/// Limit formatting to specified ranges.
@@ -413,6 +418,10 @@ fn format_string(input: String, opt: Opt) -> Result<i32> {
413418
verbosity: Verbosity::Quiet,
414419
};
415420
let report = rustfmt_nightly::format(Input::Text(input), &config, setting)?;
421+
if report.has_invalid_rustfmt_attribute_errors() && !opt.format_with_invalid_attributes {
422+
eprintln!("Input contained invalid rustfmt attributes");
423+
return Ok(1);
424+
}
416425
let has_diff = emit_format_report(report, out, opt.emitter_config(EmitMode::Stdout))?;
417426
Ok(if opt.check && has_diff { 1 } else { 0 })
418427
}
@@ -521,6 +530,10 @@ fn format(opt: Opt) -> Result<i32> {
521530
);
522531
}
523532

533+
if format_report.has_invalid_rustfmt_attribute_errors() && !opt.format_with_invalid_attributes {
534+
return Ok(1);
535+
}
536+
524537
let has_diff = emit_format_report(
525538
format_report,
526539
&mut stdout(),
@@ -543,7 +556,16 @@ mod test {
543556
path: PathBuf,
544557
}
545558

546-
fn make_temp_file(file_name: &'static str) -> TempFile {
559+
fn assert_temp_file_contents(expected_contents: &[u8], actual_file: TempFile) {
560+
assert_eq!(
561+
expected_contents,
562+
std::fs::read_to_string(&actual_file.path)
563+
.expect("couldn't read temp file")
564+
.as_bytes(),
565+
);
566+
}
567+
568+
fn make_temp_file_with_contents(file_name: &'static str, content: &[u8]) -> TempFile {
547569
use std::env::var;
548570
use std::fs::File;
549571

@@ -552,11 +574,14 @@ mod test {
552574
let path = Path::new(&target_dir).join(file_name);
553575

554576
let mut file = File::create(&path).expect("couldn't create temp file");
555-
let content = b"fn main() {}\n";
556577
file.write_all(content).expect("couldn't write temp file");
557578
TempFile { path }
558579
}
559580

581+
fn make_temp_file(file_name: &'static str) -> TempFile {
582+
make_temp_file_with_contents(file_name, b"fn main() {}\n")
583+
}
584+
560585
impl Drop for TempFile {
561586
fn drop(&mut self) {
562587
use std::fs::remove_file;
@@ -680,4 +705,112 @@ mod test {
680705
.expect("Failed to wait on rustfmt child");
681706
assert!(output.status.success());
682707
}
708+
709+
#[cfg(test)]
710+
mod format_with_invalid_attributes {
711+
use super::*;
712+
713+
const CONTENTS: &[u8] = br#"
714+
#![rustfmt::max_width(120)]
715+
fn foo() {
716+
println!("bar");
717+
}
718+
"#;
719+
720+
const FORMATTED_CONTENTS: &[u8] = br#"
721+
#![rustfmt::max_width(120)]
722+
fn foo() {
723+
println!("bar");
724+
}
725+
"#;
726+
727+
#[test]
728+
fn verify_default() {
729+
init_log();
730+
let temp_file = make_temp_file_with_contents("temp_invalid_attrs.rs", CONTENTS);
731+
732+
let child = Command::new(rustfmt())
733+
.arg(&temp_file.path)
734+
.stderr(Stdio::piped())
735+
.spawn()
736+
.expect("run with --format-with-invalid-attrs option failed");
737+
let output = child
738+
.wait_with_output()
739+
.expect("Failed to wait on rustfmt child");
740+
741+
assert!(!output.status.success());
742+
assert_temp_file_contents(CONTENTS, temp_file);
743+
}
744+
745+
#[test]
746+
fn verify_enabled() {
747+
init_log();
748+
let temp_file = make_temp_file_with_contents("temp_invalid_attrs_enabled.rs", CONTENTS);
749+
750+
let child = Command::new(rustfmt())
751+
.arg(&temp_file.path)
752+
.arg("--format-with-invalid-attrs")
753+
.stderr(Stdio::piped())
754+
.spawn()
755+
.expect("run with --format-with-invalid-attrs option failed");
756+
let output = child
757+
.wait_with_output()
758+
.expect("Failed to wait on rustfmt child");
759+
assert!(output.status.success());
760+
assert_temp_file_contents(FORMATTED_CONTENTS, temp_file);
761+
}
762+
763+
#[test]
764+
fn verify_default_stdin() {
765+
init_log();
766+
767+
let mut child = Command::new(rustfmt())
768+
.stdin(Stdio::piped())
769+
.stdout(Stdio::piped())
770+
.stderr(Stdio::piped())
771+
.spawn()
772+
.expect("run with check option failed");
773+
774+
{
775+
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
776+
stdin
777+
.write_all(CONTENTS)
778+
.expect("Failed to write to rustfmt --check");
779+
}
780+
let output = child
781+
.wait_with_output()
782+
.expect("Failed to wait on rustfmt child");
783+
assert!(!output.status.success());
784+
assert!(output.stdout.is_empty());
785+
assert_eq!(
786+
String::from_utf8(output.stderr).unwrap(),
787+
String::from("Input contained invalid rustfmt attributes\n")
788+
);
789+
}
790+
791+
#[test]
792+
fn verify_enabled_stdin() {
793+
init_log();
794+
795+
let mut child = Command::new(rustfmt())
796+
.arg("--format-with-invalid-attrs")
797+
.stdin(Stdio::piped())
798+
.stdout(Stdio::piped())
799+
.stderr(Stdio::piped())
800+
.spawn()
801+
.expect("run with check option failed");
802+
803+
{
804+
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
805+
stdin
806+
.write_all(CONTENTS)
807+
.expect("Failed to write to rustfmt --check");
808+
}
809+
let output = child
810+
.wait_with_output()
811+
.expect("Failed to wait on rustfmt child");
812+
assert!(output.status.success());
813+
assert_eq!(&output.stdout, &FORMATTED_CONTENTS);
814+
}
815+
}
683816
}

0 commit comments

Comments
 (0)