Repository: rust-analyzer/rust-analyzer
Pull request #687 (2019-01-26 23:03:58)

fix verificatio on ci

matklad: bors r+
🤖
matklad: bors r-
bors[bot]: # Canceled

Patch:
diff --git a/.travis.yml b/.travis.yml
index bddf1bebbb..8d10a43f00 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -10,8 +10,6 @@ build: &rust_build
     script:
         - rustup component add rustfmt
         - rustup component add rust-src
-        - cargo gen-tests --verify
-        - cargo gen-syntax --verify
         - cargo test --no-run  # let's measure compile time separately
         - cargo test
     env:
diff --git a/crates/ra_hir/src/marks.rs b/crates/ra_hir/src/marks.rs
index d704c3adb8..338ed0516a 100644
--- a/crates/ra_hir/src/marks.rs
+++ b/crates/ra_hir/src/marks.rs
@@ -1,6 +1,4 @@
 test_utils::marks!(
     name_res_works_for_broken_modules
     item_map_enum_importing
-    type_var_cycles_resolve_completely
-    type_var_cycles_resolve_as_possible
 );
diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs
index 31ea457062..179ebddeec 100644
--- a/crates/ra_hir/src/ty.rs
+++ b/crates/ra_hir/src/ty.rs
@@ -29,8 +29,6 @@ use ra_arena::map::ArenaMap;
 use join_to_string::join;
 use rustc_hash::FxHashMap;
 
-use test_utils::tested_by;
-
 use crate::{
     Module, Function, Struct, StructField, Enum, EnumVariant, Path, Name, ImplBlock,
     FnSignature, FnScopes, ModuleDef, AdtDef,
@@ -864,15 +862,14 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
     }
 
     fn resolve_all(mut self) -> InferenceResult {
-        let mut tv_stack = Vec::new();
         let mut expr_types = mem::replace(&mut self.type_of_expr, ArenaMap::default());
         for ty in expr_types.values_mut() {
-            let resolved = self.resolve_ty_completely(&mut tv_stack, mem::replace(ty, Ty::Unknown));
+            let resolved = self.resolve_ty_completely(mem::replace(ty, Ty::Unknown));
             *ty = resolved;
         }
         let mut pat_types = mem::replace(&mut self.type_of_pat, ArenaMap::default());
         for ty in pat_types.values_mut() {
-            let resolved = self.resolve_ty_completely(&mut tv_stack, mem::replace(ty, Ty::Unknown));
+            let resolved = self.resolve_ty_completely(mem::replace(ty, Ty::Unknown));
             *ty = resolved;
         }
         InferenceResult {
@@ -1017,21 +1014,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
     /// by their known types. All types returned by the infer_* functions should
     /// be resolved as far as possible, i.e. contain no type variables with
     /// known type.
-    fn resolve_ty_as_possible(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty {
+    fn resolve_ty_as_possible(&mut self, ty: Ty) -> Ty {
         ty.fold(&mut |ty| match ty {
             Ty::Infer(tv) => {
                 let inner = tv.to_inner();
-                if tv_stack.contains(&inner) {
-                    tested_by!(type_var_cycles_resolve_as_possible);
-                    // recursive type
-                    return tv.fallback_value();
-                }
                 if let Some(known_ty) = self.var_unification_table.probe_value(inner).known() {
                     // known_ty may contain other variables that are known by now
-                    tv_stack.push(inner);
-                    let result = self.resolve_ty_as_possible(tv_stack, known_ty.clone());
-                    tv_stack.pop();
-                    result
+                    self.resolve_ty_as_possible(known_ty.clone())
                 } else {
                     ty
                 }
@@ -1060,21 +1049,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
 
     /// Resolves the type completely; type variables without known type are
     /// replaced by Ty::Unknown.
-    fn resolve_ty_completely(&mut self, tv_stack: &mut Vec<TypeVarId>, ty: Ty) -> Ty {
+    fn resolve_ty_completely(&mut self, ty: Ty) -> Ty {
         ty.fold(&mut |ty| match ty {
             Ty::Infer(tv) => {
                 let inner = tv.to_inner();
-                if tv_stack.contains(&inner) {
-                    tested_by!(type_var_cycles_resolve_completely);
-                    // recursive type
-                    return tv.fallback_value();
-                }
                 if let Some(known_ty) = self.var_unification_table.probe_value(inner).known() {
                     // known_ty may contain other variables that are known by now
-                    tv_stack.push(inner);
-                    let result = self.resolve_ty_completely(tv_stack, known_ty.clone());
-                    tv_stack.pop();
-                    result
+                    self.resolve_ty_completely(known_ty.clone())
                 } else {
                     tv.fallback_value()
                 }
@@ -1089,7 +1070,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
             let name = path.as_ident().cloned().unwrap_or_else(Name::self_param);
             if let Some(scope_entry) = self.scopes.resolve_local_name(expr, name) {
                 let ty = self.type_of_pat.get(scope_entry.pat())?;
-                let ty = self.resolve_ty_as_possible(&mut vec![], ty.clone());
+                let ty = self.resolve_ty_as_possible(ty.clone());
                 return Some(ty);
             };
         };
@@ -1258,7 +1239,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
         // use a new type variable if we got Ty::Unknown here
         let ty = self.insert_type_vars_shallow(ty);
         self.unify(&ty, expected);
-        let ty = self.resolve_ty_as_possible(&mut vec![], ty);
+        let ty = self.resolve_ty_as_possible(ty);
         self.write_pat_ty(pat, ty.clone());
         ty
     }
@@ -1557,7 +1538,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
         // use a new type variable if we got Ty::Unknown here
         let ty = self.insert_type_vars_shallow(ty);
         self.unify(&ty, &expected.ty);
-        let ty = self.resolve_ty_as_possible(&mut vec![], ty);
+        let ty = self.resolve_ty_as_possible(ty);
         self.write_expr_ty(tgt_expr, ty.clone());
         ty
     }
diff --git a/crates/ra_syntax/src/ast/generated.rs b/crates/ra_syntax/src/ast/generated.rs
index 4f8723ae7f..3ace6533c4 100644
--- a/crates/ra_syntax/src/ast/generated.rs
+++ b/crates/ra_syntax/src/ast/generated.rs
@@ -660,50 +660,6 @@ impl ToOwned for DynTraitType {
 
 impl DynTraitType {}
 
-// ElseBranch
-#[derive(Debug, PartialEq, Eq, Hash)]
-#[repr(transparent)]
-pub struct ElseBranch {
-    pub(crate) syntax: SyntaxNode,
-}
-unsafe impl TransparentNewType for ElseBranch {
-    type Repr = rowan::SyntaxNode<RaTypes>;
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum ElseBranchKind<'a> {
-    Block(&'a Block),
-    IfExpr(&'a IfExpr),
-}
-
-impl AstNode for ElseBranch {
-    fn cast(syntax: &SyntaxNode) -> Option<&Self> {
-        match syntax.kind() {
-            | BLOCK
-            | IF_EXPR => Some(ElseBranch::from_repr(syntax.into_repr())),
-            _ => None,
-        }
-    }
-    fn syntax(&self) -> &SyntaxNode { &self.syntax }
-}
-
-impl ToOwned for ElseBranch {
-    type Owned = TreeArc<ElseBranch>;
-    fn to_owned(&self) -> TreeArc<ElseBranch> { TreeArc::cast(self.syntax.to_owned()) }
-}
-
-impl ElseBranch {
-    pub fn kind(&self) -> ElseBranchKind {
-        match self.syntax.kind() {
-            BLOCK => ElseBranchKind::Block(Block::cast(&self.syntax).unwrap()),
-            IF_EXPR => ElseBranchKind::IfExpr(IfExpr::cast(&self.syntax).unwrap()),
-            _ => unreachable!(),
-        }
-    }
-}
-
-impl ElseBranch {}
-
 // EnumDef
 #[derive(Debug, PartialEq, Eq, Hash)]
 #[repr(transparent)]
diff --git a/crates/tools/src/bin/pre-commit.rs b/crates/tools/src/bin/pre-commit.rs
index bae3b26d3b..e00bd0d3da 100644
--- a/crates/tools/src/bin/pre-commit.rs
+++ b/crates/tools/src/bin/pre-commit.rs
@@ -1,10 +1,9 @@
-use std::{
-    process::{Command},
-};
+use std::process::Command;
 
-use tools::{Result, run_rustfmt, run, project_root};
 use failure::bail;
 
+use tools::{Result, run_rustfmt, run, project_root};
+
 fn main() -> tools::Result<()> {
     run_rustfmt(tools::Overwrite)?;
     update_staged()
diff --git a/crates/tools/src/lib.rs b/crates/tools/src/lib.rs
index d404db2144..311bcb4d8f 100644
--- a/crates/tools/src/lib.rs
+++ b/crates/tools/src/lib.rs
@@ -1,7 +1,8 @@
 use std::{
+    fs,
+    collections::HashMap,
     path::{Path, PathBuf},
     process::{Command, Stdio},
-    fs::copy,
     io::{Error, ErrorKind}
 };
 
@@ -13,6 +14,10 @@ pub use teraron::{Mode, Overwrite, Verify};
 pub type Result<T> = std::result::Result<T, failure::Error>;
 
 pub const GRAMMAR: &str = "crates/ra_syntax/src/grammar.ron";
+const GRAMMAR_DIR: &str = "crates/ra_syntax/src/grammar";
+const OK_INLINE_TESTS_DIR: &str = "crates/ra_syntax/tests/data/parser/inline/ok";
+const ERR_INLINE_TESTS_DIR: &str = "crates/ra_syntax/tests/data/parser/inline/err";
+
 pub const SYNTAX_KINDS: &str = "crates/ra_syntax/src/syntax_kinds/generated.rs.tera";
 pub const AST: &str = "crates/ra_syntax/src/ast/generated.rs.tera";
 const TOOLCHAIN: &str = "stable";
@@ -130,9 +135,9 @@ pub fn install_format_hook() -> Result<()> {
     if !result_path.exists() {
         run("cargo build --package tools --bin pre-commit", ".")?;
         if cfg!(windows) {
-            copy("./target/debug/pre-commit.exe", result_path)?;
+            fs::copy("./target/debug/pre-commit.exe", result_path)?;
         } else {
-            copy("./target/debug/pre-commit", result_path)?;
+            fs::copy("./target/debug/pre-commit", result_path)?;
         }
     } else {
         return Err(Error::new(ErrorKind::AlreadyExists, "Git hook already created").into());
@@ -156,3 +161,98 @@ pub fn run_fuzzer() -> Result<()> {
         "./crates/ra_syntax",
     )
 }
+
+pub fn gen_tests(mode: Mode) -> Result<()> {
+    let tests = tests_from_dir(&project_root().join(Path::new(GRAMMAR_DIR)))?;
+    fn install_tests(tests: &HashMap<String, Test>, into: &str, mode: Mode) -> Result<()> {
+        let tests_dir = project_root().join(into);
+        if !tests_dir.is_dir() {
+            fs::create_dir_all(&tests_dir)?;
+        }
+        // ok is never actually read, but it needs to be specified to create a Test in existing_tests
+        let existing = existing_tests(&tests_dir, true)?;
+        for t in existing.keys().filter(|&t| !tests.contains_key(t)) {
+            panic!("Test is deleted: {}", t);
+        }
+
+        let mut new_idx = existing.len() + 1;
+        for (name, test) in tests {
+            let path = match existing.get(name) {
+                Some((path, _test)) => path.clone(),
+                None => {
+                    let file_name = format!("{:04}_{}.rs", new_idx, name);
+                    new_idx += 1;
+                    tests_dir.join(file_name)
+                }
+            };
+            teraron::update(&path, &test.text, mode)?;
+        }
+        Ok(())
+    }
+    install_tests(&tests.ok, OK_INLINE_TESTS_DIR, mode)?;
+    install_tests(&tests.err, ERR_INLINE_TESTS_DIR, mode)
+}
+
+#[derive(Default, Debug)]
+struct Tests {
+    pub ok: HashMap<String, Test>,
+    pub err: HashMap<String, Test>,
+}
+
+fn tests_from_dir(dir: &Path) -> Result<Tests> {
+    let mut res = Tests::default();
+    for entry in ::walkdir::WalkDir::new(dir) {
+        let entry = entry.unwrap();
+        if !entry.file_type().is_file() {
+            continue;
+        }
+        if entry.path().extension().unwrap_or_default() != "rs" {
+            continue;
+        }
+        process_file(&mut res, entry.path())?;
+    }
+    let grammar_rs = dir.parent().unwrap().join("grammar.rs");
+    process_file(&mut res, &grammar_rs)?;
+    return Ok(res);
+    fn process_file(res: &mut Tests, path: &Path) -> Result<()> {
+        let text = fs::read_to_string(path)?;
+
+        for (_, test) in collect_tests(&text) {
+            if test.ok {
+                if let Some(old_test) = res.ok.insert(test.name.clone(), test) {
+                    bail!("Duplicate test: {}", old_test.name)
+                }
+            } else {
+                if let Some(old_test) = res.err.insert(test.name.clone(), test) {
+                    bail!("Duplicate test: {}", old_test.name)
+                }
+            }
+        }
+        Ok(())
+    }
+}
+
+fn existing_tests(dir: &Path, ok: bool) -> Result<HashMap<String, (PathBuf, Test)>> {
+    let mut res = HashMap::new();
+    for file in fs::read_dir(dir)? {
+        let file = file?;
+        let path = file.path();
+        if path.extension().unwrap_or_default() != "rs" {
+            continue;
+        }
+        let name = {
+            let file_name = path.file_name().unwrap().to_str().unwrap();
+            file_name[5..file_name.len() - 3].to_string()
+        };
+        let text = fs::read_to_string(&path)?;
+        let test = Test {
+            name: name.clone(),
+            text,
+            ok,
+        };
+        if let Some(old) = res.insert(name, (path, test)) {
+            println!("Duplicate test: {:?}", old);
+        }
+    }
+    Ok(res)
+}
diff --git a/crates/tools/src/main.rs b/crates/tools/src/main.rs
index d6eabce6c6..c3e293911b 100644
--- a/crates/tools/src/main.rs
+++ b/crates/tools/src/main.rs
@@ -1,30 +1,13 @@
-use std::{
-    collections::HashMap,
-    fs,
-    path::{Path, PathBuf},
-};
-
-use clap::{App, Arg, SubCommand};
-use failure::bail;
+use clap::{App, SubCommand};
 
 use tools::{
-    collect_tests, generate,install_format_hook, run, run_rustfmt,
-    Mode, Overwrite, Result, Test, Verify, project_root, run_fuzzer
+    generate, gen_tests, install_format_hook, run, run_rustfmt,
+    Overwrite, Result, run_fuzzer,
 };
 
-const GRAMMAR_DIR: &str = "crates/ra_syntax/src/grammar";
-const OK_INLINE_TESTS_DIR: &str = "crates/ra_syntax/tests/data/parser/inline/ok";
-const ERR_INLINE_TESTS_DIR: &str = "crates/ra_syntax/tests/data/parser/inline/err";
-
 fn main() -> Result<()> {
     let matches = App::new("tasks")
         .setting(clap::AppSettings::SubcommandRequiredElseHelp)
-        .arg(
-            Arg::with_name("verify")
-                .long("--verify")
-                .help("Verify that generated code is up-to-date")
-                .global(true),
-        )
         .subcommand(SubCommand::with_name("gen-syntax"))
         .subcommand(SubCommand::with_name("gen-tests"))
         .subcommand(SubCommand::with_name("install-code"))
@@ -32,19 +15,14 @@ fn main() -> Result<()> {
         .subcommand(SubCommand::with_name("format-hook"))
         .subcommand(SubCommand::with_name("fuzz-tests"))
         .get_matches();
-    let mode = if matches.is_present("verify") {
-        Verify
-    } else {
-        Overwrite
-    };
     match matches
         .subcommand_name()
         .expect("Subcommand must be specified")
     {
         "install-code" => install_code_extension()?,
-        "gen-tests" => gen_tests(mode)?,
+        "gen-tests" => gen_tests(Overwrite)?,
         "gen-syntax" => generate(Overwrite)?,
-        "format" => run_rustfmt(mode)?,
+        "format" => run_rustfmt(Overwrite)?,
         "format-hook" => install_format_hook()?,
         "fuzz-tests" => run_fuzzer()?,
         _ => unreachable!(),
@@ -52,101 +30,6 @@ fn main() -> Result<()> {
     Ok(())
 }
 
-fn gen_tests(mode: Mode) -> Result<()> {
-    let tests = tests_from_dir(Path::new(GRAMMAR_DIR))?;
-    fn install_tests(tests: &HashMap<String, Test>, into: &str, mode: Mode) -> Result<()> {
-        let tests_dir = project_root().join(into);
-        if !tests_dir.is_dir() {
-            fs::create_dir_all(&tests_dir)?;
-        }
-        // ok is never actually read, but it needs to be specified to create a Test in existing_tests
-        let existing = existing_tests(&tests_dir, true)?;
-        for t in existing.keys().filter(|&t| !tests.contains_key(t)) {
-            panic!("Test is deleted: {}", t);
-        }
-
-        let mut new_idx = existing.len() + 1;
-        for (name, test) in tests {
-            let path = match existing.get(name) {
-                Some((path, _test)) => path.clone(),
-                None => {
-                    let file_name = format!("{:04}_{}.rs", new_idx, name);
-                    new_idx += 1;
-                    tests_dir.join(file_name)
-                }
-            };
-            teraron::update(&path, &test.text, mode)?;
-        }
-        Ok(())
-    }
-    install_tests(&tests.ok, OK_INLINE_TESTS_DIR, mode)?;
-    install_tests(&tests.err, ERR_INLINE_TESTS_DIR, mode)
-}
-
-#[derive(Default, Debug)]
-struct Tests {
-    pub ok: HashMap<String, Test>,
-    pub err: HashMap<String, Test>,
-}
-
-fn tests_from_dir(dir: &Path) -> Result<Tests> {
-    let mut res = Tests::default();
-    for entry in ::walkdir::WalkDir::new(dir) {
-        let entry = entry.unwrap();
-        if !entry.file_type().is_file() {
-            continue;
-        }
-        if entry.path().extension().unwrap_or_default() != "rs" {
-            continue;
-        }
-        process_file(&mut res, entry.path())?;
-    }
-    let grammar_rs = dir.parent().unwrap().join("grammar.rs");
-    process_file(&mut res, &grammar_rs)?;
-    return Ok(res);
-    fn process_file(res: &mut Tests, path: &Path) -> Result<()> {
-        let text = fs::read_to_string(path)?;
-
-        for (_, test) in collect_tests(&text) {
-            if test.ok {
-                if let Some(old_test) = res.ok.insert(test.name.clone(), test) {
-                    bail!("Duplicate test: {}", old_test.name)
-                }
-            } else {
-                if let Some(old_test) = res.err.insert(test.name.clone(), test) {
-                    bail!("Duplicate test: {}", old_test.name)
-                }
-            }
-        }
-        Ok(())
-    }
-}
-
-fn existing_tests(dir: &Path, ok: bool) -> Result<HashMap<String, (PathBuf, Test)>> {
-    let mut res = HashMap::new();
-    for file in fs::read_dir(dir)? {
-        let file = file?;
-        let path = file.path();
-        if path.extension().unwrap_or_default() != "rs" {
-            continue;
-        }
-        let name = {
-            let file_name = path.file_name().unwrap().to_str().unwrap();
-            file_name[5..file_name.len() - 3].to_string()
-        };
-        let text = fs::read_to_string(&path)?;
-        let test = Test {
-            name: name.clone(),
-            text,
-            ok,
-        };
-        if let Some(old) = res.insert(name, (path, test)) {
-            println!("Duplicate test: {:?}", old);
-        }
-    }
-    Ok(res)
-}
-
 fn install_code_extension() -> Result<()> {
     run("cargo install --path crates/ra_lsp_server --force", ".")?;
     if cfg!(windows) {


Test patch:
diff --git a/crates/ra_hir/src/ty/snapshots/tests__recursive_vars.snap b/crates/ra_hir/src/ty/snapshots/tests__recursive_vars.snap
deleted file mode 100644
index c3227ff7ea..0000000000
--- a/crates/ra_hir/src/ty/snapshots/tests__recursive_vars.snap
+++ /dev/null
@@ -1,14 +0,0 @@
----
-created: "2019-01-26T22:42:22.329980185+00:00"
-creator: insta@0.5.2
-expression: "&result"
-source: crates/ra_hir/src/ty/tests.rs
----
-[11; 48) '{     ...&y]; }': ()
-[21; 22) 'y': &[unknown]
-[25; 32) 'unknown': &[unknown]
-[38; 45) '[y, &y]': [&&[unknown]]
-[39; 40) 'y': &[unknown]
-[42; 44) '&y': &&[unknown]
-[43; 44) 'y': &[unknown]
-
diff --git a/crates/ra_hir/src/ty/snapshots/tests__recursive_vars_2.snap b/crates/ra_hir/src/ty/snapshots/tests__recursive_vars_2.snap
deleted file mode 100644
index de124da5b1..0000000000
--- a/crates/ra_hir/src/ty/snapshots/tests__recursive_vars_2.snap
+++ /dev/null
@@ -1,21 +0,0 @@
----
-created: "2019-01-26T22:42:22.331805845+00:00"
-creator: insta@0.5.2
-expression: "&result"
-source: crates/ra_hir/src/ty/tests.rs
----
-[11; 80) '{     ...x)]; }': ()
-[21; 22) 'x': &&[unknown]
-[25; 32) 'unknown': &&[unknown]
-[42; 43) 'y': &&[unknown]
-[46; 53) 'unknown': &&[unknown]
-[59; 77) '[(x, y..., &x)]': [(&&[unknown], &&[unknown])]
-[60; 66) '(x, y)': (&&[unknown], &&[unknown])
-[61; 62) 'x': &&[unknown]
-[64; 65) 'y': &&[unknown]
-[68; 76) '(&y, &x)': (&&&[unknown], &&&[unknown])
-[69; 71) '&y': &&&[unknown]
-[70; 71) 'y': &&[unknown]
-[73; 75) '&x': &&&[unknown]
-[74; 75) 'x': &&[unknown]
-
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index f74d6f5ea3..e1165f6821 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -3,7 +3,6 @@ use std::fmt::Write;
 
 use ra_db::{SourceDatabase, salsa::Database};
 use ra_syntax::ast::{self, AstNode};
-use test_utils::covers;
 
 use crate::{
     source_binder,
@@ -563,37 +562,6 @@ fn quux() {
     );
 }
 
-#[test]
-fn recursive_vars() {
-    covers!(type_var_cycles_resolve_completely);
-    covers!(type_var_cycles_resolve_as_possible);
-    check_inference(
-        "recursive_vars",
-        r#"
-fn test() {
-    let y = unknown;
-    [y, &y];
-}
-"#,
-    );
-}
-
-#[test]
-fn recursive_vars_2() {
-    covers!(type_var_cycles_resolve_completely);
-    covers!(type_var_cycles_resolve_as_possible);
-    check_inference(
-        "recursive_vars_2",
-        r#"
-fn test() {
-    let x = unknown;
-    let y = unknown;
-    [(x, y), (&y, &x)];
-}
-"#,
-    );
-}
-
 fn infer(content: &str) -> String {
     let (db, _, file_id) = MockDatabase::with_single_file(content);
     let source_file = db.parse(file_id);
diff --git a/crates/tools/tests/cli.rs b/crates/tools/tests/cli.rs
index 2d238d9ea5..2ee4b5223a 100644
--- a/crates/tools/tests/cli.rs
+++ b/crates/tools/tests/cli.rs
@@ -1,14 +1,22 @@
-extern crate tools;
-
-use tools::{generate, run_rustfmt, Verify};
+use tools::{generate, gen_tests, run_rustfmt, Verify};
 
 #[test]
-fn verify_template_generation() {
+fn generated_grammar_is_fresh() {
     if let Err(error) = generate(Verify) {
         panic!("{}. Please update it by running `cargo gen-syntax`", error);
     }
 }
 
+#[test]
+fn generated_tests_are_fresh() {
+    if let Err(error) = gen_tests(Verify) {
+        panic!(
+            "{}. Please update tests by running `cargo gen-tests`",
+            error
+        );
+    }
+}
+
 #[test]
 fn check_code_formatting() {
     if let Err(error) = run_rustfmt(Verify) {