Skip to content

Commit

Permalink
Parse availability and add #[deprecated] attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Jan 14, 2023
1 parent 819c18e commit 5b94785
Show file tree
Hide file tree
Showing 9 changed files with 282 additions and 57 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 @@ -193,11 +193,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
Loading

0 comments on commit 5b94785

Please sign in to comment.