-
Notifications
You must be signed in to change notification settings - Fork 413
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add `--swift-build-tool symbolgraph` to use the Swift 5.3 symbolgraph feature to generate docs from `.swiftmodule` files. Add to test specs and docs. The importer converts the symbol graph to approximately SourceKitten output and provides that as input to the existing code. Only intentional change to existing code is a modification of the "do we blame the user for this declaration not being documented" question, to deal with symbol graph output that is sometimes missing source locations. The modification turned out to fix #498 and showed a missed 'undocumented' declaration in a test suite.
- Loading branch information
Showing
14 changed files
with
987 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
require 'set' | ||
require 'jazzy/symbol_graph/graph' | ||
require 'jazzy/symbol_graph/constraint' | ||
require 'jazzy/symbol_graph/symbol' | ||
require 'jazzy/symbol_graph/relationship' | ||
require 'jazzy/symbol_graph/sym_node' | ||
require 'jazzy/symbol_graph/ext_node' | ||
|
||
# This is the top-level symbolgraph driver that deals with | ||
# figuring out arguments, running the tool, and loading the | ||
# results. | ||
|
||
module Jazzy | ||
module SymbolGraph | ||
# Run `swift symbolgraph-extract` with configured args, | ||
# parse the results, and return as JSON in SourceKit[ten] | ||
# format. | ||
def self.build(config) | ||
Dir.mktmpdir do |tmp_dir| | ||
args = arguments(config, tmp_dir) | ||
|
||
Executable.execute_command('swift', | ||
args.unshift('symbolgraph-extract'), | ||
true) # raise on error | ||
|
||
Dir[tmp_dir + '/*.symbols.json'].map do |filename| | ||
# The @ part is for extensions in our module (before the @) | ||
# of types in another module (after the @). | ||
filename =~ /(.*?)(@(.*?))?\.symbols/ | ||
module_name = Regexp.last_match[3] || Regexp.last_match[1] | ||
{ | ||
filename => | ||
Graph.new(File.read(filename), module_name).to_sourcekit, | ||
} | ||
end.to_json | ||
end | ||
end | ||
|
||
# Figure out the args to pass to symbolgraph-extract | ||
# rubocop:disable Metrics/CyclomaticComplexity | ||
def self.arguments(config, output_path) | ||
if config.module_name.empty? | ||
raise 'error: `--swift-build-tool symbolgraph` requires `--module`.' | ||
end | ||
|
||
user_args = config.build_tool_arguments.join | ||
|
||
if user_args =~ /--(?:module-name|minimum-access-level|output-dir)/ | ||
raise 'error: `--build-tool-arguments` for '\ | ||
"`--swift-build-tool symbolgraph` can't use `--module`, "\ | ||
'`--minimum-access-level`, or `--output-dir`.' | ||
end | ||
|
||
# Default set | ||
args = [ | ||
"--module-name=#{config.module_name}", | ||
'--minimum-access-level=private', | ||
"--output-dir=#{output_path}", | ||
'--skip-synthesized-members', | ||
] | ||
|
||
# Things user can override | ||
args.append("--sdk=#{sdk(config)}") unless user_args =~ /--sdk/ | ||
args.append("--target=#{target}") unless user_args =~ /--target/ | ||
args.append("-F=#{config.source_directory}") unless user_args =~ /-F(?!s)/ | ||
args.append("-I=#{config.source_directory}") unless user_args =~ /-I/ | ||
|
||
args + config.build_tool_arguments | ||
end | ||
# rubocop:enable Metrics/CyclomaticComplexity | ||
|
||
# Get the SDK path. On !darwin this just isn't needed. | ||
def self.sdk(config) | ||
`xcrun --show-sdk-path --sdk #{config.sdk}`.chomp | ||
end | ||
|
||
# Guess a default LLVM target. Feels like the tool should figure this | ||
# out from sdk + the binary somehow? | ||
def self.target | ||
`swift -version` =~ /Target: (.*?)$/ | ||
Regexp.last_match[1] || 'x86_64-apple-macosx10.15' | ||
end | ||
|
||
# This is a last-ditch fallback for when symbolgraph doesn't | ||
# provide a name - at least conforming external types to local | ||
# protocols. | ||
def self.demangle(usr) | ||
args = %w[demangle -simplified -compact].append(usr.sub(/^s:/, 's')) | ||
output, = Executable.execute_command('swift', args, true) | ||
return output.chomp | ||
rescue | ||
usr | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
module Jazzy | ||
module SymbolGraph | ||
# Constraint is a tidied-up JSON object, used by both Symbol and | ||
# Relationship, and key to reconstructing extensions. | ||
class Constraint | ||
attr_accessor :kind | ||
attr_accessor :lhs | ||
attr_accessor :rhs | ||
|
||
private | ||
|
||
def initialize(kind, lhs, rhs) | ||
self.kind = kind # "==" or ":" | ||
self.lhs = lhs | ||
self.rhs = rhs | ||
end | ||
|
||
public | ||
|
||
KIND_MAP = { | ||
'conformance' => ':', | ||
'superclass' => ':', | ||
'sameType' => '==', | ||
}.freeze | ||
|
||
# Init from a JSON hash | ||
def self.new_hash(hash) | ||
kind = KIND_MAP[hash[:kind]] | ||
raise "Unknown constraint kind '#{kind}'" unless kind | ||
lhs = hash[:lhs].sub(/^Self\./, '') | ||
rhs = hash[:rhs].sub(/^Self\./, '') | ||
new(kind, lhs, rhs) | ||
end | ||
|
||
# Init from a Swift declaration fragment eg. 'A : B' | ||
def self.new_declaration(decl) | ||
decl =~ /^(.*?)\s*([:<=]+)\s*(.*)$/ | ||
new(Regexp.last_match[2], | ||
Regexp.last_match[1], | ||
Regexp.last_match[3]) | ||
end | ||
|
||
def to_swift | ||
"#{lhs} #{kind} #{rhs}" | ||
end | ||
|
||
# The first component of types in the constraint | ||
def type_names | ||
Set.new([lhs, rhs].map { |n| n.sub(/\..*$/, '') }) | ||
end | ||
|
||
def self.new_list(hash_list) | ||
hash_list.map { |h| Constraint.new_hash(h) }.sort.uniq | ||
end | ||
|
||
# Swift protocols and reqs have an implementation/hidden conformance | ||
# to their own protocol: we don't want to think about this in docs. | ||
def self.new_list_for_symbol(hash_list, path_components) | ||
hash_list.map do |hash| | ||
if hash[:lhs] == 'Self' && | ||
hash[:kind] == 'conformance' && | ||
path_components.include?(hash[:rhs]) | ||
next nil | ||
end | ||
Constraint.new_hash(hash) | ||
end.compact | ||
end | ||
|
||
# Workaround Swift 5.3 bug with missing constraint rels | ||
def self.new_list_from_declaration(decl) | ||
decl.split(/\s*,\s*/).map { |cons| Constraint.new_declaration(cons) } | ||
end | ||
|
||
# Sort order - by Swift text | ||
include Comparable | ||
|
||
def <=>(other) | ||
to_swift <=> other.to_swift | ||
end | ||
|
||
alias eql? == | ||
|
||
def hash | ||
to_swift.hash | ||
end | ||
end | ||
end | ||
end | ||
|
||
class Array | ||
def to_where_clause | ||
empty? ? '' : ' where ' + map(&:to_swift).join(', ') | ||
end | ||
end |
Oops, something went wrong.