Skip to content

Commit 8b7e82b

Browse files
bors[bot]matklad
andauthored
Merge #7918
7918: Generalize file ensuring infrastructure r=matklad a=matklad bors r+ 🤖 Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
2 parents 071dde1 + d2bb226 commit 8b7e82b

File tree

16 files changed

+158
-144
lines changed

16 files changed

+158
-144
lines changed

crates/rust-analyzer/src/config.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -859,12 +859,12 @@ fn manual(fields: &[(&'static str, &'static str, &[&str], &str)]) -> String {
859859
mod tests {
860860
use std::fs;
861861

862-
use test_utils::project_dir;
862+
use test_utils::{ensure_file_contents, project_root};
863863

864864
use super::*;
865865

866866
#[test]
867-
fn schema_in_sync_with_package_json() {
867+
fn generate_package_json_config() {
868868
let s = Config::json_schema();
869869
let schema = format!("{:#}", s);
870870
let mut schema = schema
@@ -877,27 +877,26 @@ mod tests {
877877
.to_string();
878878
schema.push_str(",\n");
879879

880-
let package_json_path = project_dir().join("editors/code/package.json");
880+
let package_json_path = project_root().join("editors/code/package.json");
881881
let mut package_json = fs::read_to_string(&package_json_path).unwrap();
882882

883883
let start_marker = " \"$generated-start\": false,\n";
884884
let end_marker = " \"$generated-end\": false\n";
885885

886886
let start = package_json.find(start_marker).unwrap() + start_marker.len();
887887
let end = package_json.find(end_marker).unwrap();
888+
888889
let p = remove_ws(&package_json[start..end]);
889890
let s = remove_ws(&schema);
890-
891891
if !p.contains(&s) {
892892
package_json.replace_range(start..end, &schema);
893-
fs::write(&package_json_path, &mut package_json).unwrap();
894-
panic!("new config, updating package.json")
893+
ensure_file_contents(&package_json_path, &package_json)
895894
}
896895
}
897896

898897
#[test]
899-
fn schema_in_sync_with_docs() {
900-
let docs_path = project_dir().join("docs/user/generated_config.adoc");
898+
fn generate_config_documentation() {
899+
let docs_path = project_root().join("docs/user/generated_config.adoc");
901900
let current = fs::read_to_string(&docs_path).unwrap();
902901
let expected = ConfigData::manual();
903902

crates/syntax/src/tests.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::{
77
use ast::NameOwner;
88
use expect_test::expect_file;
99
use rayon::prelude::*;
10-
use test_utils::{bench, bench_fixture, project_dir, skip_slow_tests};
10+
use test_utils::{bench, bench_fixture, project_root, skip_slow_tests};
1111

1212
use crate::{ast, fuzz, tokenize, AstNode, SourceFile, SyntaxError, TextRange, TextSize, Token};
1313

@@ -153,7 +153,7 @@ fn reparse_fuzz_tests() {
153153
/// Test that Rust-analyzer can parse and validate the rust-analyzer
154154
#[test]
155155
fn self_hosting_parsing() {
156-
let dir = project_dir().join("crates");
156+
let dir = project_root().join("crates");
157157
let files = walkdir::WalkDir::new(dir)
158158
.into_iter()
159159
.filter_entry(|entry| {
@@ -193,7 +193,7 @@ fn self_hosting_parsing() {
193193
}
194194

195195
fn test_data_dir() -> PathBuf {
196-
project_dir().join("crates/syntax/test_data")
196+
project_root().join("crates/syntax/test_data")
197197
}
198198

199199
fn assert_errors_are_present(errors: &[SyntaxError], path: &Path) {

crates/test_utils/src/bench_fixture.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::fs;
44

55
use stdx::format_to;
66

7-
use crate::project_dir;
7+
use crate::project_root;
88

99
pub fn big_struct() -> String {
1010
let n = 1_000;
@@ -32,11 +32,11 @@ struct S{} {{
3232
}
3333

3434
pub fn glorious_old_parser() -> String {
35-
let path = project_dir().join("bench_data/glorious_old_parser");
35+
let path = project_root().join("bench_data/glorious_old_parser");
3636
fs::read_to_string(&path).unwrap()
3737
}
3838

3939
pub fn numerous_macro_rules() -> String {
40-
let path = project_dir().join("bench_data/numerous_macro_rules");
40+
let path = project_root().join("bench_data/numerous_macro_rules");
4141
fs::read_to_string(&path).unwrap()
4242
}

crates/test_utils/src/lib.rs

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ mod fixture;
1414
use std::{
1515
convert::{TryFrom, TryInto},
1616
env, fs,
17-
path::PathBuf,
17+
path::{Path, PathBuf},
1818
};
1919

2020
use profile::StopWatch;
21-
use stdx::lines_with_ends;
21+
use stdx::{is_ci, lines_with_ends};
2222
use text_size::{TextRange, TextSize};
2323

2424
pub use dissimilar::diff as __diff;
@@ -288,14 +288,14 @@ pub fn skip_slow_tests() -> bool {
288288
if should_skip {
289289
eprintln!("ignoring slow test")
290290
} else {
291-
let path = project_dir().join("./target/.slow_tests_cookie");
291+
let path = project_root().join("./target/.slow_tests_cookie");
292292
fs::write(&path, ".").unwrap();
293293
}
294294
should_skip
295295
}
296296

297297
/// Returns the path to the root directory of `rust-analyzer` project.
298-
pub fn project_dir() -> PathBuf {
298+
pub fn project_root() -> PathBuf {
299299
let dir = env!("CARGO_MANIFEST_DIR");
300300
PathBuf::from(dir).parent().unwrap().parent().unwrap().to_owned()
301301
}
@@ -353,3 +353,39 @@ pub fn bench(label: &'static str) -> impl Drop {
353353

354354
Bencher { sw: StopWatch::start(), label }
355355
}
356+
357+
/// Checks that the `file` has the specified `contents`. If that is not the
358+
/// case, updates the file and then fails the test.
359+
pub fn ensure_file_contents(file: &Path, contents: &str) {
360+
if let Err(()) = try_ensure_file_contents(file, contents) {
361+
panic!("Some files were not up-to-date");
362+
}
363+
}
364+
365+
/// Checks that the `file` has the specified `contents`. If that is not the
366+
/// case, updates the file and return an Error.
367+
pub fn try_ensure_file_contents(file: &Path, contents: &str) -> Result<(), ()> {
368+
match std::fs::read_to_string(file) {
369+
Ok(old_contents) if normalize_newlines(&old_contents) == normalize_newlines(contents) => {
370+
return Ok(())
371+
}
372+
_ => (),
373+
}
374+
let display_path = file.strip_prefix(&project_root()).unwrap_or(file);
375+
eprintln!(
376+
"\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n",
377+
display_path.display()
378+
);
379+
if is_ci() {
380+
eprintln!(" NOTE: run `cargo test` locally and commit the updated files\n");
381+
}
382+
if let Some(parent) = file.parent() {
383+
let _ = std::fs::create_dir_all(parent);
384+
}
385+
std::fs::write(file, contents).unwrap();
386+
Err(())
387+
}
388+
389+
fn normalize_newlines(s: &str) -> String {
390+
s.replace("\r\n", "\n")
391+
}

docs/dev/architecture.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,9 +308,8 @@ This sections talks about the things which are everywhere and nowhere in particu
308308
### Code generation
309309

310310
Some of the components of this repository are generated through automatic processes.
311-
`cargo xtask codegen` runs all generation tasks.
311+
Generated code is updated automatically on `cargo test`.
312312
Generated code is generally committed to the git repository.
313-
There are tests to check that the generated code is fresh.
314313

315314
In particular, we generate:
316315

xtask/src/codegen.rs

Lines changed: 38 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,68 +7,66 @@
77
88
mod gen_syntax;
99
mod gen_parser_tests;
10+
mod gen_lint_completions;
1011
mod gen_assists_docs;
1112
mod gen_feature_docs;
12-
mod gen_lint_completions;
1313
mod gen_diagnostic_docs;
1414

1515
use std::{
1616
fmt, mem,
1717
path::{Path, PathBuf},
1818
};
19-
use xshell::{cmd, pushenv, read_file, write_file};
19+
use xshell::{cmd, pushenv};
2020

21-
use crate::{ensure_rustfmt, flags, project_root, Result};
21+
use crate::{ensure_rustfmt, project_root, Result};
2222

2323
pub(crate) use self::{
24-
gen_assists_docs::{generate_assists_docs, generate_assists_tests},
25-
gen_diagnostic_docs::generate_diagnostic_docs,
26-
gen_feature_docs::generate_feature_docs,
27-
gen_lint_completions::generate_lint_completions,
28-
gen_parser_tests::generate_parser_tests,
29-
gen_syntax::generate_syntax,
24+
gen_assists_docs::generate_assists_tests, gen_lint_completions::generate_lint_completions,
25+
gen_parser_tests::generate_parser_tests, gen_syntax::generate_syntax,
3026
};
3127

32-
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
33-
pub(crate) enum Mode {
34-
Overwrite,
35-
Verify,
28+
pub(crate) fn docs() -> Result<()> {
29+
// We don't commit docs to the repo, so we can just overwrite them.
30+
gen_assists_docs::generate_assists_docs()?;
31+
gen_feature_docs::generate_feature_docs()?;
32+
gen_diagnostic_docs::generate_diagnostic_docs()?;
33+
Ok(())
3634
}
3735

38-
impl flags::Codegen {
39-
pub(crate) fn run(self) -> Result<()> {
40-
if self.features {
41-
generate_lint_completions(Mode::Overwrite)?;
42-
}
43-
generate_syntax(Mode::Overwrite)?;
44-
generate_parser_tests(Mode::Overwrite)?;
45-
generate_assists_tests(Mode::Overwrite)?;
46-
generate_assists_docs(Mode::Overwrite)?;
47-
generate_feature_docs(Mode::Overwrite)?;
48-
generate_diagnostic_docs(Mode::Overwrite)?;
49-
Ok(())
50-
}
36+
#[allow(unused)]
37+
fn used() {
38+
generate_parser_tests();
39+
generate_assists_tests();
40+
generate_syntax();
41+
generate_lint_completions();
5142
}
5243

53-
/// A helper to update file on disk if it has changed.
54-
/// With verify = false,
55-
fn update(path: &Path, contents: &str, mode: Mode) -> Result<()> {
56-
match read_file(path) {
57-
Ok(old_contents) if normalize(&old_contents) == normalize(contents) => {
58-
return Ok(());
44+
/// Checks that the `file` has the specified `contents`. If that is not the
45+
/// case, updates the file and then fails the test.
46+
pub(crate) fn ensure_file_contents(file: &Path, contents: &str) -> Result<()> {
47+
match std::fs::read_to_string(file) {
48+
Ok(old_contents) if normalize_newlines(&old_contents) == normalize_newlines(contents) => {
49+
return Ok(())
5950
}
6051
_ => (),
6152
}
62-
if mode == Mode::Verify {
63-
anyhow::bail!("`{}` is not up-to-date", path.display());
53+
let display_path = file.strip_prefix(&project_root()).unwrap_or(file);
54+
eprintln!(
55+
"\n\x1b[31;1merror\x1b[0m: {} was not up-to-date, updating\n",
56+
display_path.display()
57+
);
58+
if std::env::var("CI").is_ok() {
59+
eprintln!(" NOTE: run `cargo test` locally and commit the updated files\n");
6460
}
65-
eprintln!("updating {}", path.display());
66-
write_file(path, contents)?;
67-
return Ok(());
68-
69-
fn normalize(s: &str) -> String {
70-
s.replace("\r\n", "\n")
61+
if let Some(parent) = file.parent() {
62+
let _ = std::fs::create_dir_all(parent);
7163
}
64+
std::fs::write(file, contents).unwrap();
65+
anyhow::bail!("some file were not up to date")
66+
}
67+
68+
fn normalize_newlines(s: &str) -> String {
69+
s.replace("\r\n", "\n")
7270
}
7371

7472
const PREAMBLE: &str = "Generated file, do not edit by hand, see `xtask/src/codegen`";

xtask/src/codegen/gen_assists_docs.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,25 @@
22
33
use std::{fmt, path::Path};
44

5+
use xshell::write_file;
6+
57
use crate::{
6-
codegen::{self, extract_comment_blocks_with_empty_lines, reformat, Location, Mode, PREAMBLE},
8+
codegen::{self, extract_comment_blocks_with_empty_lines, reformat, Location, PREAMBLE},
79
project_root, rust_files_in, Result,
810
};
911

10-
pub(crate) fn generate_assists_tests(mode: Mode) -> Result<()> {
12+
pub(crate) fn generate_assists_tests() -> Result<()> {
1113
let assists = Assist::collect()?;
12-
generate_tests(&assists, mode)
14+
generate_tests(&assists)
1315
}
1416

15-
pub(crate) fn generate_assists_docs(mode: Mode) -> Result<()> {
17+
pub(crate) fn generate_assists_docs() -> Result<()> {
1618
let assists = Assist::collect()?;
1719
let contents = assists.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n");
1820
let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim());
1921
let dst = project_root().join("docs/user/generated_assists.adoc");
20-
codegen::update(&dst, &contents, mode)
22+
write_file(dst, &contents)?;
23+
Ok(())
2124
}
2225

2326
#[derive(Debug)]
@@ -111,7 +114,7 @@ impl fmt::Display for Assist {
111114
}
112115
}
113116

114-
fn generate_tests(assists: &[Assist], mode: Mode) -> Result<()> {
117+
fn generate_tests(assists: &[Assist]) -> Result<()> {
115118
let mut buf = String::from("use super::check_doc_test;\n");
116119

117120
for assist in assists.iter() {
@@ -135,7 +138,10 @@ r#####"
135138
buf.push_str(&test)
136139
}
137140
let buf = reformat(&buf)?;
138-
codegen::update(&project_root().join("crates/ide_assists/src/tests/generated.rs"), &buf, mode)
141+
codegen::ensure_file_contents(
142+
&project_root().join("crates/ide_assists/src/tests/generated.rs"),
143+
&buf,
144+
)
139145
}
140146

141147
fn hide_hash_comments(text: &str) -> String {

xtask/src/codegen/gen_diagnostic_docs.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,20 @@
22
33
use std::{fmt, path::PathBuf};
44

5+
use xshell::write_file;
6+
57
use crate::{
6-
codegen::{self, extract_comment_blocks_with_empty_lines, Location, Mode, PREAMBLE},
8+
codegen::{extract_comment_blocks_with_empty_lines, Location, PREAMBLE},
79
project_root, rust_files, Result,
810
};
911

10-
pub(crate) fn generate_diagnostic_docs(mode: Mode) -> Result<()> {
12+
pub(crate) fn generate_diagnostic_docs() -> Result<()> {
1113
let diagnostics = Diagnostic::collect()?;
1214
let contents =
1315
diagnostics.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n");
1416
let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim());
1517
let dst = project_root().join("docs/user/generated_diagnostic.adoc");
16-
codegen::update(&dst, &contents, mode)?;
18+
write_file(&dst, &contents)?;
1719
Ok(())
1820
}
1921

xtask/src/codegen/gen_feature_docs.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,19 @@
22
33
use std::{fmt, path::PathBuf};
44

5+
use xshell::write_file;
6+
57
use crate::{
6-
codegen::{self, extract_comment_blocks_with_empty_lines, Location, Mode, PREAMBLE},
8+
codegen::{extract_comment_blocks_with_empty_lines, Location, PREAMBLE},
79
project_root, rust_files, Result,
810
};
911

10-
pub(crate) fn generate_feature_docs(mode: Mode) -> Result<()> {
12+
pub(crate) fn generate_feature_docs() -> Result<()> {
1113
let features = Feature::collect()?;
1214
let contents = features.into_iter().map(|it| it.to_string()).collect::<Vec<_>>().join("\n\n");
1315
let contents = format!("//{}\n{}\n", PREAMBLE, contents.trim());
1416
let dst = project_root().join("docs/user/generated_features.adoc");
15-
codegen::update(&dst, &contents, mode)?;
17+
write_file(&dst, &contents)?;
1618
Ok(())
1719
}
1820

0 commit comments

Comments
 (0)