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

Support for Swift SPI declarations #1277

Merged
merged 2 commits into from Sep 18, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Expand Up @@ -2,7 +2,11 @@

##### Breaking

* None.
* Support Swift SPI groups. Swift declarations marked `@_spi` are no longer
included in docs when `--min-acl` is set to `public` or `open`. Use
`--include-spi-declarations` to include docs for these declarations.
[John Fairhurst](https://github.com/johnfairh)
[#1263](https://github.com/realm/jazzy/issues/1263)

##### Enhancements

Expand Down
3 changes: 3 additions & 0 deletions README.md
Expand Up @@ -318,6 +318,9 @@ In Swift mode, Jazzy by default documents only `public` and `open` declarations.
include declarations with a lower access level, set the `--min-acl` flag to `internal`,
`fileprivate`, or `private`.

By default, Jazzy does not document declarations marked `@_spi` when `--min-acl` is
set to `public` or `open`. Set the `--include-spi-declarations` flag to include them.

In Objective-C mode, Jazzy documents all declarations found in the `--umbrella-header`
header file and any other header files included by it.

Expand Down
6 changes: 6 additions & 0 deletions lib/jazzy/config.rb
Expand Up @@ -476,6 +476,12 @@ def hide_objc?
'have children.',
default: false

config_attr :include_spi_declarations,
command_line: '--[no-]include-spi-declarations',
description: 'Include Swift declarations marked `@_spi` even if '\
'--min-acl is set to `public` or `open`.',
default: false

# rubocop:enable Layout/ArgumentAlignment

def initialize
Expand Down
43 changes: 36 additions & 7 deletions lib/jazzy/sourcekitten.rb
Expand Up @@ -277,12 +277,18 @@ def self.make_default_doc_info(declaration)
declaration.children = []
end

def self.attribute?(doc, attr_name)
doc['key.attributes']&.find do |attribute|
attribute['key.attribute'] == "source.decl.attribute.#{attr_name}"
end
end

def self.availability_attribute?(doc)
return false unless doc['key.attributes']
attribute?(doc, 'available')
end

!doc['key.attributes'].select do |attribute|
attribute.values.first == 'source.decl.attribute.available'
end.empty?
def self.spi_attribute?(doc)
attribute?(doc, '_spi')
end

def self.should_document?(doc)
Expand All @@ -302,10 +308,31 @@ def self.should_document?(doc)
return false
end

# Document enum elements, since we can't tell their ACL.
# Only document @_spi declarations in some scenarios
return false unless should_document_spi?(doc)

# Don't document declarations excluded by the min_acl setting
if type.swift_extension?
should_document_swift_extension?(doc)
else
should_document_acl?(type, doc)
end
end

# Check visibility: SPI
def self.should_document_spi?(doc)
spi_ok = @min_acl < SourceDeclaration::AccessControlLevel.public ||
Config.instance.include_spi_declarations ||
(!spi_attribute?(doc) && !doc['key.symgraph_spi'])

@stats.add_spi_skipped unless spi_ok
spi_ok
end

# Check visibility: access control
def self.should_document_acl?(type, doc)
# Include all enum elements for now, can't tell their ACL.
return true if type.swift_enum_element?
# Document extensions if they might have parts covered by the ACL.
return should_document_swift_extension?(doc) if type.swift_extension?

acl_ok = SourceDeclaration::AccessControlLevel.from_doc(doc) >= @min_acl
unless acl_ok
Expand All @@ -315,6 +342,8 @@ def self.should_document?(doc)
acl_ok
end

# Document extensions if they add protocol conformances, or have any
# member that needs to be documented.
def self.should_document_swift_extension?(doc)
doc['key.inheritedtypes'] ||
Array(doc['key.substructure']).any? do |subdoc|
Expand Down
13 changes: 11 additions & 2 deletions lib/jazzy/stats.rb
Expand Up @@ -5,7 +5,7 @@ module Jazzy
class Stats
include Config::Mixin

attr_reader :documented, :acl_skipped, :undocumented_decls
attr_reader :documented, :acl_skipped, :spi_skipped, :undocumented_decls

def add_documented
@documented += 1
Expand All @@ -15,6 +15,10 @@ def add_acl_skipped
@acl_skipped += 1
end

def add_spi_skipped
@spi_skipped += 1
end

def add_undocumented(decl)
@undocumented_decls << decl
end
Expand All @@ -32,7 +36,7 @@ def undocumented
end

def initialize
@documented = @acl_skipped = 0
@documented = @acl_skipped = @spi_skipped = 0
@undocumented_decls = []
end

Expand All @@ -54,6 +58,11 @@ def report
"#{symbol_or_symbols(acl_skipped)} " \
'(use `--min-acl` to specify a different minimum ACL)'
end

if spi_skipped > 0
puts "skipped #{spi_skipped} SPI #{symbol_or_symbols(spi_skipped)} " \
'(use `--include-spi-declarations` to include these)'
end
end

def doc_coverage
Expand Down
3 changes: 2 additions & 1 deletion lib/jazzy/symbol_graph/sym_node.rb
Expand Up @@ -113,7 +113,7 @@ def inherits_clause
end

def full_declaration
symbol.availability
symbol.attributes
.append(symbol.declaration + inherits_clause + where_clause)
.join("\n")
end
Expand Down Expand Up @@ -147,6 +147,7 @@ def to_sourcekit
unless children.empty?
hash['key.substructure'] = children_to_sourcekit
end
hash['key.symgraph_spi'] = true if symbol.spi

hash
end
Expand Down
19 changes: 15 additions & 4 deletions lib/jazzy/symbol_graph/symbol.rb
Expand Up @@ -10,10 +10,11 @@ class Symbol
attr_accessor :declaration
attr_accessor :kind
attr_accessor :acl
attr_accessor :spi
attr_accessor :location # can be nil, keys :filename :line :character
attr_accessor :constraints # array, can be empty
attr_accessor :doc_comments # can be nil
attr_accessor :availability # array, can be empty
attr_accessor :attributes # array, can be empty
attr_accessor :generic_type_params # set, can be empty
attr_accessor :parameter_names # array, can be nil

Expand All @@ -31,14 +32,15 @@ def initialize(hash)
init_func_signature(func_signature)
end
init_acl(hash[:accessLevel])
self.spi = hash[:spi]
if location = hash[:location]
init_location(location)
end
init_constraints(hash, raw_decl)
if comments_hash = hash[:docComment]
init_doc_comments(comments_hash)
end
init_availability(hash[:availability] || [])
init_attributes(hash[:availability] || [])
init_generic_type_params(hash)
end

Expand Down Expand Up @@ -159,8 +161,8 @@ def init_doc_comments(comments_hash)
# Availability
# Re-encode this as Swift. Should really teach Jazzy about these,
# could maybe then do something smarter here.
def init_availability(avail_hash_list)
self.availability = avail_hash_list.map do |avail|
def availability_attributes(avail_hash_list)
avail_hash_list.map do |avail|
str = '@available('
if avail[:isUnconditionallyDeprecated]
str += '*, deprecated'
Expand Down Expand Up @@ -190,6 +192,15 @@ def decode_version(hash)
str
end

def spi_attributes
spi ? ['@_spi(Unknown)'] : []
end

def init_attributes(avail_hash_list)
self.attributes =
availability_attributes(avail_hash_list) + spi_attributes
end

# Sort order
include Comparable

Expand Down
2 changes: 1 addition & 1 deletion spec/integration_specs
Submodule integration_specs updated 236 files