Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Emit #[deprecated] attributes #341

Merged
merged 3 commits into from
Jan 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 138 additions & 5 deletions crates/header-translator/src/availability.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,146 @@
use clang::PlatformAvailability;
//! <https://docs.swift.org/swift-book/ReferenceManual/Attributes.html#ID583>
use std::fmt;

use clang::{Entity, PlatformAvailability, Version};

use crate::context::Context;

#[derive(Debug, Clone, PartialEq, Default)]
struct Unavailable {
ios: bool,
macos: bool,
maccatalyst: bool,
watchos: bool,
tvos: bool,
}

#[derive(Debug, Clone, PartialEq, Default)]
struct Versions {
ios: Option<Version>,
macos: Option<Version>,
maccatalyst: Option<Version>,
watchos: Option<Version>,
tvos: Option<Version>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct Availability {
#[allow(dead_code)]
inner: Vec<()>,
unavailable: Unavailable,
introduced: Versions,
deprecated: Versions,
message: Option<String>,
_swift: Option<PlatformAvailability>,
}

impl Availability {
pub fn parse(_availability: Vec<PlatformAvailability>) -> Self {
Self { inner: Vec::new() }
pub fn parse(entity: &Entity<'_>, _context: &Context<'_>) -> Self {
let availabilities = entity
.get_platform_availability()
.expect("platform availability");

let mut unavailable = Unavailable::default();
let mut introduced = Versions::default();
let mut deprecated = Versions::default();
let mut message = None;
let mut _swift = None;

for availability in availabilities {
let mut set = |availability: PlatformAvailability,
unavailable: &mut bool,
introduced: &mut Option<Version>,
deprecated: &mut Option<Version>| {
*unavailable = availability.unavailable;
*introduced = availability.introduced;
*deprecated = availability.deprecated;

// TODO: Unsure how we would handle these if they exist
if availability.obsoleted.is_some() {
error!("availability attribute containd `obsoleted`");
}

if let Some(m) = availability.message {
if let Some(message) = message.as_deref() {
if m != message {
error!(m, message, "message avalability attributes were not equal");
}
}
message = Some(m);
}
};

// TODO: Ensure that a specific platform only appears once
match &*availability.platform {
"ios" => set(
availability,
&mut unavailable.ios,
&mut introduced.ios,
&mut deprecated.ios,
),
"macos" => set(
availability,
&mut unavailable.macos,
&mut introduced.macos,
&mut deprecated.macos,
),
"maccatalyst" => set(
availability,
&mut unavailable.maccatalyst,
&mut introduced.maccatalyst,
&mut deprecated.maccatalyst,
),
"watchos" => set(
availability,
&mut unavailable.watchos,
&mut introduced.watchos,
&mut deprecated.watchos,
),
"tvos" => set(
availability,
&mut unavailable.tvos,
&mut introduced.tvos,
&mut deprecated.tvos,
),
"swift" => {
_swift = Some(availability);
}
platform => error!(?platform, "unknown availability platform"),
}
}

Self {
unavailable,
introduced,
deprecated,
message,
_swift,
}
}
}

impl fmt::Display for Availability {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.deprecated {
Versions {
ios: None,
macos: None,
maccatalyst: None,
watchos: None,
tvos: None,
} => {
// Not deprecated
}
Versions { .. } => {
// Deprecated
// TODO: Use version data to output a more detailed message
if let Some(message) = &self.message {
writeln!(f, "#[deprecated = {message:?}]")?;
} else {
writeln!(f, "#[deprecated]")?;
}
}
}
// TODO: Emit `cfg` attributes based on `self.unavailable`
// TODO: Emit availability checks based on `self.introduced`
Ok(())
}
}
12 changes: 10 additions & 2 deletions crates/header-translator/src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -231,11 +231,19 @@ impl<'a> Cache<'a> {
// Fix up a few typedef + enum declarations
let mut iter = mem::take(&mut file.stmts).into_iter().peekable();
while let Some(stmt) = iter.next() {
if let Stmt::AliasDecl { id, ty, kind: None } = &stmt {
if let Stmt::AliasDecl {
id,
availability: _,
ty,
kind: None,
} = &stmt
{
if let Some(Stmt::EnumDecl {
id: enum_id,
availability: _,
ty: enum_ty,
..
kind: _,
variants: _,
}) = iter.peek_mut()
{
if enum_ty.is_typedef_to(&id.name) {
Expand Down
1 change: 1 addition & 0 deletions crates/header-translator/src/library.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ impl fmt::Display for Library {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "{FILE_PRELUDE}")?;
writeln!(f, "#![allow(unused_imports)]")?;
writeln!(f, "#![allow(deprecated)]")?;

for name in self.files.keys() {
writeln!(f, "#[path = \"{name}.rs\"]")?;
Expand Down
14 changes: 4 additions & 10 deletions crates/header-translator/src/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,11 +337,7 @@ impl<'tu> PartialMethod<'tu> {
return None;
}

let availability = Availability::parse(
entity
.get_platform_availability()
.expect("method availability"),
);
let availability = Availability::parse(&entity, context);

let modifiers = MethodModifiers::parse(&entity, context);

Expand Down Expand Up @@ -476,11 +472,7 @@ impl PartialProperty<'_> {
return (None, None);
}

let availability = Availability::parse(
entity
.get_platform_availability()
.expect("method availability"),
);
let availability = Availability::parse(&entity, context);

let modifiers = MethodModifiers::parse(&entity, context);

Expand Down Expand Up @@ -570,6 +562,8 @@ impl fmt::Display for Method {
// Attributes
//

write!(f, "{}", self.availability)?;

if self.is_optional_protocol {
writeln!(f, " #[optional]")?;
}
Expand Down
2 changes: 1 addition & 1 deletion crates/header-translator/src/rust_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1784,7 +1784,7 @@ fn parse_unexposed_tokens(s: &str) -> String {
if let Some(TokenTree::Ident(ident)) = iter.peek() {
let ident = ident.to_string();
match &*ident {
"NS_RETURNS_INNER_POINTER" | "NS_REFINED_FOR_SWIFT" => {
"NS_RETURNS_INNER_POINTER" | "NS_REFINED_FOR_SWIFT" | "NS_SWIFT_UI_ACTOR" => {
iter.next();
}
"API_AVAILABLE"
Expand Down
Loading