From 38353144c7afa5d8f108b65bc4cd7c9454a4aa72 Mon Sep 17 00:00:00 2001 From: John Fairhurst Date: Sat, 18 Sep 2021 10:35:05 +0100 Subject: [PATCH] Support Swift SPI declarations (#1277) Exclude at min_acl >= public with CLI to override. --- CHANGELOG.md | 6 ++++- README.md | 3 +++ lib/jazzy/config.rb | 6 +++++ lib/jazzy/sourcekitten.rb | 43 +++++++++++++++++++++++++----- lib/jazzy/stats.rb | 13 +++++++-- lib/jazzy/symbol_graph/sym_node.rb | 3 ++- lib/jazzy/symbol_graph/symbol.rb | 19 ++++++++++--- spec/integration_specs | 2 +- 8 files changed, 79 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a83ea91f..6c137a564 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index 47f227bf4..a076c3eaa 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/lib/jazzy/config.rb b/lib/jazzy/config.rb index 79c330871..d40ca07cb 100644 --- a/lib/jazzy/config.rb +++ b/lib/jazzy/config.rb @@ -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 diff --git a/lib/jazzy/sourcekitten.rb b/lib/jazzy/sourcekitten.rb index 6797f128d..fa6d037c8 100644 --- a/lib/jazzy/sourcekitten.rb +++ b/lib/jazzy/sourcekitten.rb @@ -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) @@ -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 @@ -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| diff --git a/lib/jazzy/stats.rb b/lib/jazzy/stats.rb index a8cecc3a6..e8ed572ec 100644 --- a/lib/jazzy/stats.rb +++ b/lib/jazzy/stats.rb @@ -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 @@ -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 @@ -32,7 +36,7 @@ def undocumented end def initialize - @documented = @acl_skipped = 0 + @documented = @acl_skipped = @spi_skipped = 0 @undocumented_decls = [] end @@ -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 diff --git a/lib/jazzy/symbol_graph/sym_node.rb b/lib/jazzy/symbol_graph/sym_node.rb index 65bc4dd05..65200b6c7 100644 --- a/lib/jazzy/symbol_graph/sym_node.rb +++ b/lib/jazzy/symbol_graph/sym_node.rb @@ -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 @@ -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 diff --git a/lib/jazzy/symbol_graph/symbol.rb b/lib/jazzy/symbol_graph/symbol.rb index b536a6d13..a2bb66c7d 100644 --- a/lib/jazzy/symbol_graph/symbol.rb +++ b/lib/jazzy/symbol_graph/symbol.rb @@ -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 @@ -31,6 +32,7 @@ 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 @@ -38,7 +40,7 @@ def initialize(hash) 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 @@ -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' @@ -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 diff --git a/spec/integration_specs b/spec/integration_specs index 10337f7d7..4891788fb 160000 --- a/spec/integration_specs +++ b/spec/integration_specs @@ -1 +1 @@ -Subproject commit 10337f7d7fa21e7eac0d368840859a0332f0d853 +Subproject commit 4891788fbcabfd4967202b9a2151b0cca3c050b6