Skip to content

Commit

Permalink
Merge pull request #341 from madsmtm/icrate-deprecated
Browse files Browse the repository at this point in the history
Emit `#[deprecated]` attributes
  • Loading branch information
madsmtm authored Jan 15, 2023
2 parents 771d430 + b624f1b commit 143e77b
Show file tree
Hide file tree
Showing 11 changed files with 298 additions and 70 deletions.
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

0 comments on commit 143e77b

Please sign in to comment.