Skip to content

Commit

Permalink
Implement @deprecated in WIT (#1687)
Browse files Browse the repository at this point in the history
This is intended to correspond with WebAssembly/component-model#377.

Closes #1683
  • Loading branch information
alexcrichton authored Jul 23, 2024
1 parent 62444c3 commit 368184c
Show file tree
Hide file tree
Showing 19 changed files with 248 additions and 29 deletions.
21 changes: 19 additions & 2 deletions crates/wit-component/src/printing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -903,19 +903,36 @@ impl WitPrinter {
fn print_stability(&mut self, stability: &Stability) {
match stability {
Stability::Unknown => {}
Stability::Stable { since, feature } => {
Stability::Stable {
since,
feature,
deprecated,
} => {
self.output.push_str("@since(version = ");
self.output.push_str(&since.to_string());
if let Some(feature) = feature {
self.output.push_str(", feature = ");
self.output.push_str(feature);
}
self.output.push_str(")\n");
if let Some(version) = deprecated {
self.output.push_str("@deprecated(version = ");
self.output.push_str(&version.to_string());
self.output.push_str(")\n");
}
}
Stability::Unstable { feature } => {
Stability::Unstable {
feature,
deprecated,
} => {
self.output.push_str("@unstable(feature = ");
self.output.push_str(feature);
self.output.push_str(")\n");
if let Some(version) = deprecated {
self.output.push_str("@deprecated(version = ");
self.output.push_str(&version.to_string());
self.output.push_str(")\n");
}
}
}
}
Expand Down
33 changes: 25 additions & 8 deletions crates/wit-parser/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1550,6 +1550,10 @@ enum Attribute<'a> {
span: Span,
feature: Id<'a>,
},
Deprecated {
span: Span,
version: Version,
},
}

impl<'a> Attribute<'a> {
Expand All @@ -1559,35 +1563,46 @@ impl<'a> Attribute<'a> {
let id = parse_id(tokens)?;
let attr = match id.name {
"since" => {
tokens.eat(Token::LeftParen)?;
tokens.expect(Token::LeftParen)?;
eat_id(tokens, "version")?;
tokens.eat(Token::Equals)?;
tokens.expect(Token::Equals)?;
let (_span, version) = parse_version(tokens)?;
let feature = if tokens.eat(Token::Comma)? {
eat_id(tokens, "feature")?;
tokens.eat(Token::Equals)?;
tokens.expect(Token::Equals)?;
Some(parse_id(tokens)?)
} else {
None
};
tokens.eat(Token::RightParen)?;
tokens.expect(Token::RightParen)?;
Attribute::Since {
span: id.span,
version,
feature,
}
}
"unstable" => {
tokens.eat(Token::LeftParen)?;
tokens.expect(Token::LeftParen)?;
eat_id(tokens, "feature")?;
tokens.eat(Token::Equals)?;
tokens.expect(Token::Equals)?;
let feature = parse_id(tokens)?;
tokens.eat(Token::RightParen)?;
tokens.expect(Token::RightParen)?;
Attribute::Unstable {
span: id.span,
feature,
}
}
"deprecated" => {
tokens.expect(Token::LeftParen)?;
eat_id(tokens, "version")?;
tokens.expect(Token::Equals)?;
let (_span, version) = parse_version(tokens)?;
tokens.expect(Token::RightParen)?;
Attribute::Deprecated {
span: id.span,
version,
}
}
other => {
bail!(Error::new(id.span, format!("unknown attribute `{other}`"),))
}
Expand All @@ -1599,7 +1614,9 @@ impl<'a> Attribute<'a> {

fn span(&self) -> Span {
match self {
Attribute::Since { span, .. } | Attribute::Unstable { span, .. } => *span,
Attribute::Since { span, .. }
| Attribute::Unstable { span, .. }
| Attribute::Deprecated { span, .. } => *span,
}
}
}
Expand Down
37 changes: 36 additions & 1 deletion crates/wit-parser/src/ast/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1376,19 +1376,54 @@ impl<'a> Resolver<'a> {
fn stability(&mut self, attrs: &[ast::Attribute<'_>]) -> Result<Stability> {
match attrs {
[] => Ok(Stability::Unknown),

[ast::Attribute::Since {
version, feature, ..
}] => Ok(Stability::Stable {
since: version.clone(),
feature: feature.as_ref().map(|s| s.name.to_string()),
deprecated: None,
}),

[ast::Attribute::Since {
version, feature, ..
}, ast::Attribute::Deprecated {
version: deprecated,
..
}]
| [ast::Attribute::Deprecated {
version: deprecated,
..
}, ast::Attribute::Since {
version, feature, ..
}] => Ok(Stability::Stable {
since: version.clone(),
feature: feature.as_ref().map(|s| s.name.to_string()),
deprecated: Some(deprecated.clone()),
}),

[ast::Attribute::Unstable { feature, .. }] => Ok(Stability::Unstable {
feature: feature.name.to_string(),
deprecated: None,
}),

[ast::Attribute::Unstable { feature, .. }, ast::Attribute::Deprecated { version, .. }]
| [ast::Attribute::Deprecated { version, .. }, ast::Attribute::Unstable { feature, .. }] => {
Ok(Stability::Unstable {
feature: feature.name.to_string(),
deprecated: Some(version.clone()),
})
}
[ast::Attribute::Deprecated { span, .. }] => {
bail!(Error::new(
*span,
"must pair @deprecated with either @since or @unstable",
))
}
[_, b, ..] => {
bail!(Error::new(
b.span(),
"only one stability attribute is allowed per-item",
"unsupported combination of attributes",
))
}
}
Expand Down
24 changes: 23 additions & 1 deletion crates/wit-parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -845,13 +845,35 @@ pub enum Stability {
since: Version,
#[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
feature: Option<String>,
#[cfg_attr(
feature = "serde",
serde(
skip_serializing_if = "Option::is_none",
default,
serialize_with = "serialize_optional_version",
deserialize_with = "deserialize_optional_version"
)
)]
deprecated: Option<Version>,
},

/// `@unstable(feature = foo)`
///
/// This item is explicitly tagged `@unstable`. A feature name is listed and
/// this item is excluded by default in `Resolve` unless explicitly enabled.
Unstable { feature: String },
Unstable {
feature: String,
#[cfg_attr(
feature = "serde",
serde(
skip_serializing_if = "Option::is_none",
default,
serialize_with = "serialize_optional_version",
deserialize_with = "deserialize_optional_version"
)
)]
deprecated: Option<Version>,
},

/// This item does not have either `@since` or `@unstable`.
Unknown,
Expand Down
4 changes: 3 additions & 1 deletion crates/wit-parser/src/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1411,7 +1411,9 @@ impl Resolve {
fn include_stability(&self, stability: &Stability) -> bool {
match stability {
Stability::Stable { .. } | Stability::Unknown => true,
Stability::Unstable { feature } => self.features.contains(feature) || self.all_features,
Stability::Unstable { feature, .. } => {
self.features.contains(feature) || self.all_features
}
}
}
}
Expand Down
23 changes: 23 additions & 0 deletions crates/wit-parser/src/serde_.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,3 +122,26 @@ where
let version: String = String::deserialize(deserializer)?;
version.parse().map_err(|e| D::Error::custom(e))
}

pub fn serialize_optional_version<S>(
version: &Option<Version>,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
version
.as_ref()
.map(|s| s.to_string())
.serialize(serializer)
}

pub fn deserialize_optional_version<'de, D>(deserializer: D) -> Result<Option<Version>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
match <Option<String>>::deserialize(deserializer)? {
Some(version) => Ok(Some(version.parse().map_err(|e| D::Error::custom(e))?)),
None => Ok(None),
}
}
4 changes: 4 additions & 0 deletions crates/wit-parser/tests/ui/parse-fail/bad-deprecated1.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package a:b;

@deprecated
interface foo {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
expected '(', found keyword `interface`
--> tests/ui/parse-fail/bad-deprecated1.wit:4:1
|
4 | interface foo {}
| ^
4 changes: 4 additions & 0 deletions crates/wit-parser/tests/ui/parse-fail/bad-deprecated2.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package a:b;

@deprecated()
interface foo {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
expected an identifier or string, found ')'
--> tests/ui/parse-fail/bad-deprecated2.wit:3:13
|
3 | @deprecated()
| ^
4 changes: 4 additions & 0 deletions crates/wit-parser/tests/ui/parse-fail/bad-deprecated3.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package a:b;

@deprecated(since = 1.2.3)
interface foo {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
expected `version`, found `since`
--> tests/ui/parse-fail/bad-deprecated3.wit:3:13
|
3 | @deprecated(since = 1.2.3)
| ^----
5 changes: 5 additions & 0 deletions crates/wit-parser/tests/ui/parse-fail/bad-deprecated4.wit
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package a:b;

@deprecated(since = 1.2.3)
@deprecated(since = 1.2.3)
interface foo {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
expected `version`, found `since`
--> tests/ui/parse-fail/bad-deprecated4.wit:3:13
|
3 | @deprecated(since = 1.2.3)
| ^----
4 changes: 2 additions & 2 deletions crates/wit-parser/tests/ui/parse-fail/bad-since1.wit.result
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
expected an identifier or string, found keyword `interface`
expected '(', found keyword `interface`
--> tests/ui/parse-fail/bad-since1.wit:4:1
|
4 | interface foo1 {}
| ^--------
| ^
18 changes: 18 additions & 0 deletions crates/wit-parser/tests/ui/since-and-unstable.wit
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,21 @@ world in-a-world {
constructor();
}
}

interface deprecated1 {
@since(version = 1.0.0)
@deprecated(version = 1.0.1)
type t1 = u32;

@deprecated(version = 1.0.1)
@since(version = 1.0.0)
type t2 = u32;

@unstable(feature = foo)
@deprecated(version = 1.0.1)
type t3 = u32;

@deprecated(version = 1.0.1)
@unstable(feature = foo)
type t4 = u32;
}
Loading

0 comments on commit 368184c

Please sign in to comment.