Skip to content

Commit

Permalink
Support Swift SPI declarations (#1277)
Browse files Browse the repository at this point in the history
Exclude at min_acl >= public with CLI to override.
  • Loading branch information
johnfairh committed Sep 18, 2021
1 parent be86033 commit 3835314
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 16 deletions.
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

0 comments on commit 3835314

Please sign in to comment.