diff --git a/README.md b/README.md
index 8fdff68..1810aac 100644
--- a/README.md
+++ b/README.md
@@ -2007,12 +2007,15 @@ $ just --summary
test
```
-The `[private]` attributemaster may also be used to hide recipes without needing to change the name:
+The `[private]` attributemaster may also be used to hide recipes or aliases without needing to change the name:
```just
[private]
foo:
+[private]
+alias b := bar
+
bar:
```
diff --git a/src/alias.rs b/src/alias.rs
index f543abb..0c19fe5 100644
--- a/src/alias.rs
+++ b/src/alias.rs
@@ -3,6 +3,7 @@ use super::*;
/// An alias, e.g. `name := target`
#[derive(Debug, PartialEq, Clone, Serialize)]
pub(crate) struct Alias<'src, T = Rc>> {
+ pub(crate) attributes: BTreeSet,
pub(crate) name: Name<'src>,
#[serde(
bound(serialize = "T: Keyed<'src>"),
@@ -20,6 +21,7 @@ impl<'src> Alias<'src, Name<'src>> {
assert_eq!(self.target.lexeme(), target.name.lexeme());
Alias {
+ attributes: self.attributes,
name: self.name,
target,
}
@@ -28,7 +30,7 @@ impl<'src> Alias<'src, Name<'src>> {
impl Alias<'_> {
pub(crate) fn is_private(&self) -> bool {
- self.name.lexeme().starts_with('_')
+ self.name.lexeme().starts_with('_') || self.attributes.contains(&Attribute::Private)
}
}
diff --git a/src/analyzer.rs b/src/analyzer.rs
index 237b674..44efb43 100644
--- a/src/analyzer.rs
+++ b/src/analyzer.rs
@@ -2,6 +2,8 @@ use super::*;
use CompileErrorKind::*;
+const VALID_ALIAS_ATTRIBUTES: [Attribute; 1] = [Attribute::Private];
+
#[derive(Default)]
pub(crate) struct Analyzer<'src> {
recipes: Table<'src, UnresolvedRecipe<'src>>,
@@ -195,6 +197,15 @@ impl<'src> Analyzer<'src> {
}));
}
+ for attr in &alias.attributes {
+ if !VALID_ALIAS_ATTRIBUTES.contains(attr) {
+ return Err(alias.name.token().error(AliasInvalidAttribute {
+ alias: name,
+ attr: *attr,
+ }));
+ }
+ }
+
Ok(())
}
diff --git a/src/compile_error.rs b/src/compile_error.rs
index 4464c61..c7dcbdf 100644
--- a/src/compile_error.rs
+++ b/src/compile_error.rs
@@ -24,6 +24,14 @@ impl Display for CompileError<'_> {
use CompileErrorKind::*;
match &*self.kind {
+ AliasInvalidAttribute { alias, attr } => {
+ write!(
+ f,
+ "Alias {} has an invalid attribute `{}`",
+ alias,
+ attr.to_str()
+ )?;
+ }
AliasShadowsRecipe { alias, recipe_line } => {
write!(
f,
diff --git a/src/compile_error_kind.rs b/src/compile_error_kind.rs
index a6cc5f0..8c1fcdf 100644
--- a/src/compile_error_kind.rs
+++ b/src/compile_error_kind.rs
@@ -2,6 +2,10 @@ use super::*;
#[derive(Debug, PartialEq)]
pub(crate) enum CompileErrorKind<'src> {
+ AliasInvalidAttribute {
+ alias: &'src str,
+ attr: Attribute,
+ },
AliasShadowsRecipe {
alias: &'src str,
recipe_line: usize,
diff --git a/src/parser.rs b/src/parser.rs
index 50082ba..41ee820 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -325,7 +325,7 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
} else if self.next_is(Identifier) {
match Keyword::from_lexeme(next.lexeme()) {
Some(Keyword::Alias) if self.next_are(&[Identifier, Identifier, ColonEquals]) => {
- items.push(Item::Alias(self.parse_alias()?));
+ items.push(Item::Alias(self.parse_alias(BTreeSet::new())?));
}
Some(Keyword::Export) if self.next_are(&[Identifier, Identifier, ColonEquals]) => {
self.presume_keyword(Keyword::Export)?;
@@ -361,9 +361,17 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
BTreeSet::new(),
)?));
} else if let Some(attributes) = self.parse_attributes()? {
- let quiet = self.accepted(At)?;
- let doc = pop_doc_comment(&mut items, eol_since_last_comment);
- items.push(Item::Recipe(self.parse_recipe(doc, quiet, attributes)?));
+ let next_keyword = Keyword::from_lexeme(self.next()?.lexeme());
+ match next_keyword {
+ Some(Keyword::Alias) if self.next_are(&[Identifier, Identifier, ColonEquals]) => {
+ items.push(Item::Alias(self.parse_alias(attributes)?));
+ }
+ _ => {
+ let quiet = self.accepted(At)?;
+ let doc = pop_doc_comment(&mut items, eol_since_last_comment);
+ items.push(Item::Recipe(self.parse_recipe(doc, quiet, attributes)?));
+ }
+ }
} else {
return Err(self.unexpected_token()?);
}
@@ -383,13 +391,20 @@ impl<'tokens, 'src> Parser<'tokens, 'src> {
}
/// Parse an alias, e.g `alias name := target`
- fn parse_alias(&mut self) -> CompileResult<'src, Alias<'src, Name<'src>>> {
+ fn parse_alias(
+ &mut self,
+ attributes: BTreeSet,
+ ) -> CompileResult<'src, Alias<'src, Name<'src>>> {
self.presume_keyword(Keyword::Alias)?;
let name = self.parse_name()?;
self.presume_any(&[Equals, ColonEquals])?;
let target = self.parse_name()?;
self.expect_eol()?;
- Ok(Alias { name, target })
+ Ok(Alias {
+ attributes,
+ name,
+ target,
+ })
}
/// Parse an assignment, e.g. `foo := bar`
@@ -978,6 +993,12 @@ mod tests {
tree: (justfile (alias t test)),
}
+ test! {
+ name: alias_with_attributee,
+ text: "[private]\nalias t := test",
+ tree: (justfile (alias t test)),
+ }
+
test! {
name: aliases_multiple,
text: "alias t := test\nalias b := build",
@@ -996,6 +1017,18 @@ mod tests {
),
}
+ test! {
+ name: recipe_named_alias,
+ text: r#"
+ [private]
+ alias:
+ echo 'echoing alias'
+ "#,
+ tree: (justfile
+ (recipe alias (body ("echo 'echoing alias'")))
+ ),
+ }
+
test! {
name: export,
text: r#"export x := "hello""#,
diff --git a/tests/error_messages.rs b/tests/error_messages.rs
index 54b93d0..d5c2300 100644
--- a/tests/error_messages.rs
+++ b/tests/error_messages.rs
@@ -1,5 +1,17 @@
use super::*;
+test! {
+ name: invalid_alias_attribute,
+ justfile: "[private]\n[linux]\nalias t := test\n\ntest:\n",
+ stderr: "
+ error: Alias t has an invalid attribute `linux`
+ |
+ 3 | alias t := test
+ | ^
+ ",
+ status: EXIT_FAILURE,
+}
+
test! {
name: expected_keyword,
justfile: "foo := if '' == '' { '' } arlo { '' }",
diff --git a/tests/json.rs b/tests/json.rs
index 6f418cb..2927fb3 100644
--- a/tests/json.rs
+++ b/tests/json.rs
@@ -22,6 +22,7 @@ fn alias() {
"f": {
"name": "f",
"target": "foo",
+ "attributes": [],
}
},
"assignments": {},
@@ -299,6 +300,7 @@ fn duplicate_recipes() {
"first": "foo",
"aliases": {
"f": {
+ "attributes": [],
"name": "f",
"target": "foo",
}
diff --git a/tests/private.rs b/tests/private.rs
index 82e3d4f..13dcacf 100644
--- a/tests/private.rs
+++ b/tests/private.rs
@@ -1,7 +1,7 @@
use super::*;
#[test]
-fn attribute() {
+fn private_attribute_for_recipe() {
Test::new()
.justfile(
"
@@ -17,3 +17,24 @@ fn attribute() {
)
.run();
}
+
+#[test]
+fn private_attribute_for_alias() {
+ Test::new()
+ .justfile(
+ "
+ [private]
+ alias f := foo
+
+ foo:
+ ",
+ )
+ .args(&["--list"])
+ .stdout(
+ "
+ Available recipes:
+ foo
+ ",
+ )
+ .run();
+}