nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: casey/just
Pull request #2152 (2024-06-13 20:19:26)

Load environment file from dotenv-path relative to working directory


Patch:
diff --git a/src/load_dotenv.rs b/src/load_dotenv.rs
index c867e973..29b31b24 100644
--- a/src/load_dotenv.rs
+++ b/src/load_dotenv.rs
@@ -24,8 +24,9 @@ pub(crate) fn load_dotenv(
   }
 
   if let Some(path) = dotenv_path {
+    let path = working_directory.join(path);
     if path.is_file() {
-      return load_from_file(&working_directory.join(path));
+      return load_from_file(&path);
     }
   }
 


Test patch:
diff --git a/tests/dotenv.rs b/tests/dotenv.rs
index 8e50467c..3c8df841 100644
--- a/tests/dotenv.rs
+++ b/tests/dotenv.rs
@@ -360,6 +360,7 @@ fn no_dotenv() {
     .stderr("echo DEFAULT\n")
     .run();
 }
+
 #[test]
 fn dotenv_env_var_override() {
   Test::new()
@@ -375,3 +376,21 @@ fn dotenv_env_var_override() {
     .stderr("echo $DOTENV_KEY\n")
     .run();
 }
+
+#[test]
+fn dotenv_path_usable_from_subdir() {
+  Test::new()
+    .justfile(
+      "
+        set dotenv-path := '.custom-env'
+
+        @echo:
+          echo $DOTENV_KEY
+      ",
+    )
+    .create_dir("sub")
+    .current_dir("sub")
+    .write(".custom-env", "DOTENV_KEY=dotenv-value")
+    .stdout("dotenv-value\n")
+    .run();
+}
diff --git a/tests/test.rs b/tests/test.rs
index e241d7e2..29350351 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -94,6 +94,11 @@ impl Test {
     self
   }
 
+  pub(crate) fn create_dir(self, path: impl AsRef<Path>) -> Self {
+    fs::create_dir_all(self.tempdir.path().join(path.as_ref())).unwrap();
+    self
+  }
+
   pub(crate) fn current_dir(mut self, path: impl AsRef<Path>) -> Self {
     path.as_ref().clone_into(&mut self.current_dir);
     self
Repository: casey/just
Pull request #2164 (2024-06-15 03:02:23)

List recipes by group in group justfile order with `just --list --unsorted`


Patch:
diff --git a/src/subcommand.rs b/src/subcommand.rs
index 9b9a2463..c001daa2 100644
--- a/src/subcommand.rs
+++ b/src/subcommand.rs
@@ -500,7 +500,17 @@ impl Subcommand {
       groups
     };
 
-    for (i, (group, recipes)) in groups.iter().enumerate() {
+    let mut ordered = module
+      .public_groups(config)
+      .into_iter()
+      .map(Some)
+      .collect::<Vec<Option<String>>>();
+
+    if groups.contains_key(&None) {
+      ordered.insert(0, None);
+    }
+
+    for (i, group) in ordered.into_iter().enumerate() {
       if i > 0 {
         println!();
       }
@@ -509,14 +519,14 @@ impl Subcommand {
 
       if !no_groups {
         print!("{list_prefix}");
-        if let Some(group_name) = group {
-          println!("[{group_name}]");
+        if let Some(group) = &group {
+          println!("[{group}]");
         } else {
           println!("(no group)");
         }
       }
 
-      for recipe in recipes {
+      for recipe in groups.get(&group).unwrap() {
         for (i, name) in iter::once(&recipe.name())
           .chain(aliases.get(recipe.name()).unwrap_or(&Vec::new()))
           .enumerate()


Test patch:
diff --git a/tests/groups.rs b/tests/groups.rs
index 0812852e..5dbf6274 100644
--- a/tests/groups.rs
+++ b/tests/groups.rs
@@ -96,6 +96,47 @@ fn list_with_groups_unsorted() {
     .run();
 }
 
+#[test]
+fn list_with_groups_unsorted_group_order() {
+  Test::new()
+    .justfile(
+      "
+        [group('y')]
+        [group('x')]
+        f:
+
+        [group('b')]
+        b:
+
+        [group('a')]
+        e:
+
+        c:
+      ",
+    )
+    .args(["--list", "--unsorted"])
+    .stdout(
+      "
+        Available recipes:
+            (no group)
+            c
+
+            [x]
+            f
+
+            [y]
+            f
+
+            [b]
+            b
+
+            [a]
+            e
+      ",
+    )
+    .run();
+}
+
 #[test]
 fn list_groups() {
   Test::new()
Repository: casey/just
Pull request #2167 (2024-06-15 05:46:38)

Add `datetime()` and `datetime_utc()` functions


Patch:
diff --git a/README.md b/README.md
index de792bad..f4d7363e 100644
--- a/README.md
+++ b/README.md
@@ -1642,6 +1642,16 @@ which will halt execution.
   characters. For example, `choose('64', HEX)` will generate a random
   64-character lowercase hex string.
 
+#### Datetime
+
+- `datetime(format)`<sup>master</sup> - Return local time with `format`.
+- `datetime_utc(format)`<sup>master</sup> - Return UTC time with `format`.
+
+The arguments to `datetime` and `datetime_utc` are `strftime`-style format
+strings, see the
+[`chrono` library docs](https://docs.rs/chrono/latest/chrono/format/strftime/index.html)
+for details.
+
 #### Semantic Versions
 
 - `semver_matches(version, requirement)`<sup>1.16.0</sup> - Check whether a
diff --git a/src/function.rs b/src/function.rs
index 92267622..dbce9349 100644
--- a/src/function.rs
+++ b/src/function.rs
@@ -47,6 +47,8 @@ pub(crate) fn get(name: &str) -> Option<Function> {
     "config_local_directory" => Nullary(|_| dir("local config", dirs::config_local_dir)),
     "data_directory" => Nullary(|_| dir("data", dirs::data_dir)),
     "data_local_directory" => Nullary(|_| dir("local data", dirs::data_local_dir)),
+    "datetime" => Unary(datetime),
+    "datetime_utc" => Unary(datetime_utc),
     "encode_uri_component" => Unary(encode_uri_component),
     "env" => UnaryOpt(env),
     "env_var" => Unary(env_var),
@@ -235,6 +237,14 @@ fn dir(name: &'static str, f: fn() -> Option<PathBuf>) -> FunctionResult {
   }
 }
 
+fn datetime(_context: Context, format: &str) -> FunctionResult {
+  Ok(chrono::Local::now().format(format).to_string())
+}
+
+fn datetime_utc(_context: Context, format: &str) -> FunctionResult {
+  Ok(chrono::Utc::now().format(format).to_string())
+}
+
 fn encode_uri_component(_context: Context, s: &str) -> FunctionResult {
   static PERCENT_ENCODE: percent_encoding::AsciiSet = percent_encoding::NON_ALPHANUMERIC
     .remove(b'-')


Test patch:
diff --git a/tests/datetime.rs b/tests/datetime.rs
new file mode 100644
index 00000000..5092cbe8
--- /dev/null
+++ b/tests/datetime.rs
@@ -0,0 +1,27 @@
+use super::*;
+
+#[test]
+fn datetime() {
+  Test::new()
+    .justfile(
+      "
+        x := datetime('%Y-%m-%d %z')
+      ",
+    )
+    .args(["--eval", "x"])
+    .stdout_regex(r"\d\d\d\d-\d\d-\d\d [+-]\d\d\d\d")
+    .run();
+}
+
+#[test]
+fn datetime_utc() {
+  Test::new()
+    .justfile(
+      "
+        x := datetime_utc('%Y-%m-%d %Z')
+      ",
+    )
+    .args(["--eval", "x"])
+    .stdout_regex(r"\d\d\d\d-\d\d-\d\d UTC")
+    .run();
+}
diff --git a/tests/lib.rs b/tests/lib.rs
index 42b3aff1..292482f0 100644
--- a/tests/lib.rs
+++ b/tests/lib.rs
@@ -47,6 +47,7 @@ mod completions;
 mod conditional;
 mod confirm;
 mod constants;
+mod datetime;
 mod delimiters;
 mod directories;
 mod dotenv;
Repository: casey/just
Pull request #2201 (2024-06-27 18:45:11)

Allow comments after `mod` statements


Patch:
diff --git a/src/parser.rs b/src/parser.rs
index 9ea372c9..9066e278 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -365,10 +365,11 @@ impl<'run, 'src> Parser<'run, 'src> {
             });
           }
           Some(Keyword::Mod)
-            if self.next_are(&[Identifier, Identifier, StringToken])
-              || self.next_are(&[Identifier, Identifier, Identifier, StringToken])
+            if self.next_are(&[Identifier, Identifier, Comment])
               || self.next_are(&[Identifier, Identifier, Eof])
               || self.next_are(&[Identifier, Identifier, Eol])
+              || self.next_are(&[Identifier, Identifier, Identifier, StringToken])
+              || self.next_are(&[Identifier, Identifier, StringToken])
               || self.next_are(&[Identifier, QuestionMark]) =>
           {
             self.presume_keyword(Keyword::Mod)?;


Test patch:
diff --git a/tests/modules.rs b/tests/modules.rs
index 43a12541..36928585 100644
--- a/tests/modules.rs
+++ b/tests/modules.rs
@@ -8,8 +8,6 @@ fn modules_are_unstable() {
         mod foo
       ",
     )
-    .arg("foo")
-    .arg("foo")
     .stderr(
       "error: Modules are currently unstable. \
       Invoke `just` with the `--unstable` flag to enable unstable features.\n",
@@ -781,3 +779,18 @@ fn colon_separated_path_components_are_not_used_as_arguments() {
     .status(1)
     .run();
 }
+
+#[test]
+fn comments_can_follow_modules() {
+  Test::new()
+    .write("foo.just", "foo:\n @echo FOO")
+    .justfile(
+      "
+        mod foo # this is foo
+      ",
+    )
+    .test_round_trip(false)
+    .args(["--unstable", "foo", "foo"])
+    .stdout("FOO\n")
+    .run();
+}
Repository: casey/just
Pull request #2210 (2024-06-30 01:08:46)

Allow unstable features with `--summary`


Patch:
diff --git a/src/config.rs b/src/config.rs
index 0e8f3d49..c32d62b3 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -744,6 +744,8 @@ impl Config {
       }
     };
 
+    let unstable = matches.get_flag(arg::UNSTABLE) || subcommand == Subcommand::Summary;
+
     Ok(Self {
       check: matches.get_flag(arg::CHECK),
       color: Self::color_from_matches(matches)?,
@@ -783,7 +785,7 @@ impl Config {
         .unwrap()
         .into(),
       unsorted: matches.get_flag(arg::UNSORTED),
-      unstable: matches.get_flag(arg::UNSTABLE),
+      unstable,
       verbosity: if matches.get_flag(arg::QUIET) {
         Verbosity::Quiet
       } else {
@@ -834,6 +836,7 @@ mod tests {
       $(shell_args: $shell_args:expr,)?
       $(subcommand: $subcommand:expr,)?
       $(unsorted: $unsorted:expr,)?
+      $(unstable: $unstable:expr,)?
       $(verbosity: $verbosity:expr,)?
     } => {
       #[test]
@@ -854,6 +857,7 @@ mod tests {
           $(shell_args: $shell_args,)?
           $(subcommand: $subcommand,)?
           $(unsorted: $unsorted,)?
+          $(unstable: $unstable,)?
           $(verbosity: $verbosity,)?
           ..testing::config(&[])
         };
@@ -1368,6 +1372,7 @@ mod tests {
     name: subcommand_summary,
     args: ["--summary"],
     subcommand: Subcommand::Summary,
+    unstable: true,
   }
 
   test! {


Test patch:
diff --git a/tests/summary.rs b/tests/summary.rs
index ee27ca0f..7b70baa8 100644
--- a/tests/summary.rs
+++ b/tests/summary.rs
@@ -71,3 +71,18 @@ fn submodule_recipes() {
     .stdout("bar foo::foo foo::bar::bar foo::bar::baz::baz foo::bar::baz::biz::biz\n")
     .run();
 }
+
+#[test]
+fn summary_implies_unstable() {
+  Test::new()
+    .write("foo.just", "foo:")
+    .justfile(
+      "
+        mod foo
+      ",
+    )
+    .test_round_trip(false)
+    .arg("--summary")
+    .stdout("foo::foo\n")
+    .run();
+}
Repository: casey/just
Pull request #2235 (2024-07-07 22:45:36)

Allow abbreviating functions ending in `_directory` to `_dir`


Patch:
diff --git a/README.md b/README.md
index c9f03a53..a992d62f 100644
--- a/README.md
+++ b/README.md
@@ -1349,6 +1349,11 @@ Done!
 `just` provides a few built-in functions that might be useful when writing
 recipes.
 
+All functions ending in `_directory` can be abbreviated to `_dir`. So
+`home_directory()` can also be written as `home_dir()`. In addition,
+`invocation_directory_native()` can be abbreviated to
+`invocation_dir_native()`.
+
 #### System Information
 
 - `arch()` — Instruction set architecture. Possible values are: `"aarch64"`,
diff --git a/src/function.rs b/src/function.rs
index f741b5e4..50bee73e 100644
--- a/src/function.rs
+++ b/src/function.rs
@@ -32,7 +32,15 @@ impl<'src: 'run, 'run> Context<'src, 'run> {
 }
 
 pub(crate) fn get(name: &str) -> Option<Function> {
-  let function = match name {
+  let name = if let Some(prefix) = name.strip_suffix("_dir") {
+    format!("{prefix}_directory")
+  } else if let Some(prefix) = name.strip_suffix("_dir_native") {
+    format!("{prefix}_directory_native")
+  } else {
+    name.into()
+  };
+
+  let function = match name.as_str() {
     "absolute_path" => Unary(absolute_path),
     "append" => Binary(append),
     "arch" => Nullary(arch),


Test patch:
diff --git a/tests/functions.rs b/tests/functions.rs
index 8fc81aca..4ebd87e0 100644
--- a/tests/functions.rs
+++ b/tests/functions.rs
@@ -1068,3 +1068,33 @@ fn unary_argument_count_mismamatch_error_message() {
     .status(EXIT_FAILURE)
     .run();
 }
+
+#[test]
+fn dir_abbreviations_are_accepted() {
+  Test::new()
+    .justfile(
+      "
+      abbreviated := justfile_dir()
+      unabbreviated := justfile_directory()
+
+      @foo:
+        # {{ assert(abbreviated == unabbreviated, 'fail') }}
+    ",
+    )
+    .run();
+}
+
+#[test]
+fn invocation_dir_native_abbreviation_is_accepted() {
+  Test::new()
+    .justfile(
+      "
+      abbreviated := invocation_directory_native()
+      unabbreviated := invocation_dir_native()
+
+      @foo:
+        # {{ assert(abbreviated == unabbreviated, 'fail') }}
+    ",
+    )
+    .run();
+}
Repository: casey/just
Pull request #2236 (2024-07-08 02:10:05)

Lexiclean search directory so `..` does not check the current directory
If the search directory was `..`, for example in the invocation `just ../foo`, we would wind up checking the justfile in the current directory since we did `INVOCATION_DIRECTORY/..`.ancestors(), which would first return `INVOCATION_DIRECTORY`.

Instead, lexiclean the result of joining th invocation directory with the search directory, so `..` is removed, and `ancestors()` doesn't return the invocation directory.


Patch:
diff --git a/src/search.rs b/src/search.rs
index 50854195..4d061e0f 100644
--- a/src/search.rs
+++ b/src/search.rs
@@ -4,6 +4,7 @@ const DEFAULT_JUSTFILE_NAME: &str = JUSTFILE_NAMES[0];
 pub(crate) const JUSTFILE_NAMES: [&str; 2] = ["justfile", ".justfile"];
 const PROJECT_ROOT_CHILDREN: &[&str] = &[".bzr", ".git", ".hg", ".svn", "_darcs"];
 
+#[derive(Debug)]
 pub(crate) struct Search {
   pub(crate) justfile: PathBuf,
   pub(crate) working_directory: PathBuf,
diff --git a/src/subcommand.rs b/src/subcommand.rs
index 6173bfdf..65c80a83 100644
--- a/src/subcommand.rs
+++ b/src/subcommand.rs
@@ -110,9 +110,10 @@ impl Subcommand {
     ) {
       let starting_path = match &config.search_config {
         SearchConfig::FromInvocationDirectory => config.invocation_directory.clone(),
-        SearchConfig::FromSearchDirectory { search_directory } => {
-          env::current_dir().unwrap().join(search_directory)
-        }
+        SearchConfig::FromSearchDirectory { search_directory } => config
+          .invocation_directory
+          .join(search_directory)
+          .lexiclean(),
         _ => unreachable!(),
       };
 


Test patch:
diff --git a/tests/search.rs b/tests/search.rs
index daac5bf5..b7ebe47f 100644
--- a/tests/search.rs
+++ b/tests/search.rs
@@ -143,6 +143,22 @@ fn single_upwards() {
   search_test(path, &["../"]);
 }
 
+#[test]
+fn double_upwards() {
+  let tmp = temptree! {
+    justfile: "default:\n\techo ok",
+    foo: {
+      bar: {
+        justfile: "default:\n\techo foo",
+      },
+    },
+  };
+
+  let path = tmp.path().join("foo/bar");
+
+  search_test(path, &["../default"]);
+}
+
 #[test]
 fn find_dot_justfile() {
   Test::new()
Repository: casey/just
Pull request #2244 (2024-07-14 02:16:16)

Print space before submodules in `--list` with groups


Patch:
diff --git a/justfile b/justfile
index 11f47dbf..fa126f6c 100755
--- a/justfile
+++ b/justfile
@@ -16,11 +16,13 @@ watch +args='test':
 test:
   cargo test
 
-ci: build-book
+ci: lint build-book
   cargo test --all
+
+lint:
   cargo clippy --all --all-targets -- --deny warnings
-  cargo fmt --all -- --check
   ./bin/forbid
+  cargo fmt --all -- --check
   cargo update --locked --package just
 
 fuzz:
diff --git a/src/subcommand.rs b/src/subcommand.rs
index 6c405b74..865b28ff 100644
--- a/src/subcommand.rs
+++ b/src/subcommand.rs
@@ -539,13 +539,13 @@ impl Subcommand {
       ordered.insert(0, None);
     }
 
+    let no_groups = groups.contains_key(&None) && groups.len() == 1;
+
     for (i, group) in ordered.into_iter().enumerate() {
       if i > 0 {
         println!();
       }
 
-      let no_groups = groups.contains_key(&None) && groups.len() == 1;
-
       if !no_groups {
         print!("{list_prefix}");
         if let Some(group) = &group {
@@ -605,7 +605,11 @@ impl Subcommand {
         Self::list_module(config, submodule, depth + 1);
       }
     } else {
-      for submodule in module.modules(config) {
+      for (i, submodule) in module.modules(config).into_iter().enumerate() {
+        if !no_groups && !groups.is_empty() && i == 0 {
+          println!();
+        }
+
         print!("{list_prefix}{} ...", submodule.name());
         format_doc(
           config,


Test patch:
diff --git a/tests/list.rs b/tests/list.rs
index 0ed5cba8..4b9d56dc 100644
--- a/tests/list.rs
+++ b/tests/list.rs
@@ -405,3 +405,49 @@ fn module_doc_aligned() {
     )
     .run();
 }
+
+#[test]
+fn space_before_submodules_following_groups() {
+  Test::new()
+    .write("foo.just", "")
+    .justfile(
+      "
+        mod foo
+
+        [group: 'baz']
+        bar:
+      ",
+    )
+    .test_round_trip(false)
+    .args(["--unstable", "--list"])
+    .stdout(
+      "
+        Available recipes:
+            [baz]
+            bar
+
+            foo ...
+      ",
+    )
+    .run();
+}
+
+#[test]
+fn no_space_before_submodules_not_following_groups() {
+  Test::new()
+    .write("foo.just", "")
+    .justfile(
+      "
+        mod foo
+      ",
+    )
+    .test_round_trip(false)
+    .args(["--unstable", "--list"])
+    .stdout(
+      "
+        Available recipes:
+            foo ...
+      ",
+    )
+    .run();
+}
Repository: casey/just
Pull request #2254 (2024-07-15 16:25:27)

Make empty mod doc attribute suppress doc comment


Patch:
diff --git a/src/analyzer.rs b/src/analyzer.rs
index b277b42f..1f1cece3 100644
--- a/src/analyzer.rs
+++ b/src/analyzer.rs
@@ -96,7 +96,7 @@ impl<'src> Analyzer<'src> {
             let mut doc_attr: Option<&str> = None;
             for attribute in attributes {
               if let Attribute::Doc(ref doc) = attribute {
-                doc_attr = doc.as_ref().map(|s| s.cooked.as_ref());
+                doc_attr = Some(doc.as_ref().map(|s| s.cooked.as_ref()).unwrap_or_default());
               } else {
                 return Err(name.token.error(InvalidAttribute {
                   item_kind: "Module",
diff --git a/src/subcommand.rs b/src/subcommand.rs
index 344667ea..3444a251 100644
--- a/src/subcommand.rs
+++ b/src/subcommand.rs
@@ -452,7 +452,7 @@ impl Subcommand {
       signature_widths: &BTreeMap<&str, usize>,
     ) {
       if let Some(doc) = doc {
-        if doc.lines().count() <= 1 {
+        if !doc.is_empty() && doc.lines().count() <= 1 {
           print!(
             "{:padding$}{} {}",
             "",


Test patch:
diff --git a/tests/modules.rs b/tests/modules.rs
index 14c3c010..97be8a11 100644
--- a/tests/modules.rs
+++ b/tests/modules.rs
@@ -787,3 +787,20 @@ fn bad_module_attribute_fails() {
     .status(EXIT_FAILURE)
     .run();
 }
+
+#[test]
+fn empty_doc_attribute_on_module() {
+  Test::new()
+    .write("foo.just", "")
+    .justfile(
+      r#"
+        # Suppressed comment
+        [doc]
+        mod foo
+      "#,
+    )
+    .test_round_trip(false)
+    .arg("--list")
+    .stdout("Available recipes:\n    foo ...\n")
+    .run();
+}
Repository: casey/just
Pull request #2268 (2024-07-21 23:39:15)

Remove (no group) before recipes not in a group
I think it's probably fine to just print them first with no header


Patch:
diff --git a/src/subcommand.rs b/src/subcommand.rs
index 0ab08a87..3f2c1ce9 100644
--- a/src/subcommand.rs
+++ b/src/subcommand.rs
@@ -575,11 +575,8 @@ impl Subcommand {
       }
 
       if !no_groups {
-        print!("{list_prefix}");
         if let Some(group) = &group {
-          println!("[{group}]");
-        } else {
-          println!("(no group)");
+          println!("{list_prefix}[{group}]");
         }
       }
 


Test patch:
diff --git a/tests/groups.rs b/tests/groups.rs
index 5dbf6274..8c41e98e 100644
--- a/tests/groups.rs
+++ b/tests/groups.rs
@@ -25,7 +25,6 @@ fn list_with_groups() {
     .stdout(
       "
         Available recipes:
-            (no group)
             c
 
             [alpha]
@@ -76,7 +75,6 @@ fn list_with_groups_unsorted() {
     .stdout(
       "
         Available recipes:
-            (no group)
             c
 
             [alpha]
@@ -118,7 +116,6 @@ fn list_with_groups_unsorted_group_order() {
     .stdout(
       "
         Available recipes:
-            (no group)
             c
 
             [x]
diff --git a/tests/list.rs b/tests/list.rs
index 1dce34fd..db7b669e 100644
--- a/tests/list.rs
+++ b/tests/list.rs
@@ -236,7 +236,6 @@ fn list_with_groups_in_modules() {
     .stdout(
       "
         Available recipes:
-            (no group)
             bar:
                 [BAZ]
                 baz
@@ -412,7 +411,6 @@ fn submodules_without_groups() {
     .stdout(
       "
         Available recipes:
-            (no group)
             foo ...
 
             [baz]
diff --git a/tests/modules.rs b/tests/modules.rs
index 7cc23866..50169f80 100644
--- a/tests/modules.rs
+++ b/tests/modules.rs
@@ -802,14 +802,13 @@ fn group_attribute_on_module() {
     .stdout(
       "
         Available recipes:
-            (no group)
             c
 
             [alpha]
             a
             foo ...
             zee ...
-            
+
             [beta]
             b
             bar ...
@@ -850,14 +849,13 @@ fn group_attribute_on_module_unsorted() {
     .stdout(
       "
         Available recipes:
-            (no group)
             c
 
             [alpha]
             a
             zee ...
             foo ...
-            
+
             [beta]
             b
             bar ...
@@ -898,7 +896,6 @@ fn group_attribute_on_module_list_submodule() {
     .stdout(
       "
         Available recipes:
-            (no group)
             c
 
             [alpha]
@@ -950,7 +947,6 @@ fn group_attribute_on_module_list_submodule_unsorted() {
     .stdout(
       "
         Available recipes:
-            (no group)
             c
 
             [alpha]
Repository: casey/just
Pull request #2353 (2024-09-07 15:16:08)

Fix bug in redefinition compile error reporting
This fixes a bug that caused the error reporting for redefinition compile errors to be reported "backwards" when the original and redefinition were differing types.

See #2350 for additional information.

casey: Nice! Can you add a test which demonstrates the fix?
marcaddeo: > Nice! Can you add a test which demonstrates the fix?

Test added 👍🏻 

Patch:
diff --git a/src/analyzer.rs b/src/analyzer.rs
index de023c41..8a6b8eed 100644
--- a/src/analyzer.rs
+++ b/src/analyzer.rs
@@ -52,10 +52,10 @@ impl<'src> Analyzer<'src> {
      -> CompileResult<'src> {
       if let Some((first_type, original)) = definitions.get(name.lexeme()) {
         if !(*first_type == second_type && duplicates_allowed) {
-          let (original, redefinition) = if name.line < original.line {
-            (name, *original)
+          let ((first_type, second_type), (original, redefinition)) = if name.line < original.line {
+            ((second_type, *first_type), (name, *original))
           } else {
-            (*original, name)
+            ((*first_type, second_type), (*original, name))
           };
 
           return Err(redefinition.token.error(Redefinition {
@@ -383,7 +383,7 @@ mod tests {
     line: 2,
     column: 6,
     width: 3,
-    kind: Redefinition { first_type: "alias", second_type: "recipe", name: "foo", first: 0 },
+    kind: Redefinition { first_type: "recipe", second_type: "alias", name: "foo", first: 0 },
   }
 
   analysis_error! {


Test patch:
diff --git a/tests/error_messages.rs b/tests/error_messages.rs
index 5b66ac3c..cc7515fb 100644
--- a/tests/error_messages.rs
+++ b/tests/error_messages.rs
@@ -109,3 +109,21 @@ fn file_paths_not_in_subdir_are_absolute() {
     )
     .run();
 }
+
+#[test]
+fn redefinition_errors_properly_swap_types() {
+  Test::new()
+    .write("foo.just", "foo:")
+    .justfile("foo:\n echo foo\n\nmod foo 'foo.just'")
+    .status(EXIT_FAILURE)
+    .stderr(
+      "
+error: Recipe `foo` defined on line 1 is redefined as a module on line 4
+ ——▶ justfile:4:5
+  │
+4 │ mod foo 'foo.just'
+  │     ^^^
+",
+    )
+    .run();
+}