How to list all structs implementing a specific Protocol in a Swift module?

Hi I am looking for a way to find all declarations of structs that implement a specific protocol in a swift module.

I have been searching for existing tooling and found the following tools but none of them seem to be a good fit for what I'm trying to accomplish:

  • SwiftSyntax: and libSyntax are libraries to parse and manipulate swift source files.
    The problem: is that these libraries do no semantic processing and thus it is hard to get type information for example for types that get extended across multiple files using Swift Extensions.

  • swiftc -dump-ast: parses swift source files, creates an Abstract Syntax Tree from these files, type checks it, annotates it with type information, and dumps the typed AST in human readable format.
    The problem: is that this ast-dump format is not suitable for parsing. (See what @jrose and @Vinicius_Vendramini wrote in Improving the AST dump format)

  • SourceKit: provides "a way to interface with the compiler (via a C API) and primarily do semantic things that editors need, like provide code-completion"
    The problem: is that I cannot seem to find how to use it to lookup types that conform to a certain protocol.

@akyrtzi once wrote about SwiftSyntax (in 2018) that

Currently there is only syntactic support, there is no way to do semantic queries via using SwiftSyntax.

I can imagine though that a lot of things have changed and evolved since then.

I am looking for something similar to what you see in (docC generated) swift documentation under the title Conforming Types for protocol reference pages.

What would be the best (and/or easiest) way to extract this type information from a swift code base? Are there some SourceKit requests that I can use for this? Or should I use some other tooling?

1 Like

You might be better off with SourceKit requests, but IndexStoreDB is another project to check out:

2 Likes

Sourcery can also do this for you.

1 Like

docC is getting info from running the toolchain's swift-symbolgraph-extract, which outputs a JSON file with documentation/semantic information for the APIs of a swift module.

2 Likes

Is there documentation somewhere about how to use swift-symbolgraph-extract? The --help seems to print the help for swift-frontend.

Admittedly not a great answer but this set of tests shows how it can be invoked.
I would also encourage anyone to file requests in bugs.swift.org for improvements like having a dedicated help message.

Also \cc @QuietMisdreavus

Thanks @akyrtzi, symbol-graph-extract is indeed more or less the kind of tool I was looking for.

@George indeed for the moment the documentation is a bit scarce but there is a nice library made by @owenv that uses swift-symbolgraph-extract. The JSON that symbol-graph-extract dumps is pretty self explaining. It outputs a graph of all symbols of a compiled library/executable using two lists:

  • a list of symbols which are the nodes in the graph and represent all the swift symbols from your source code.
    • they represent for example: a class, a struct, an instance property, a type property, etc.
  • a list of relationships that describe the edges between these symbols in the graph
    • examples are: memberOf, conformsTo, inheritsFrom, etc.

A comprehensive scheme of the exact values you can expect in this JSON can be found in @owenv's SymbolGraphFormat.
That project is now almost one year old though. So I don't know if the format changed much since then. But I can tell you that the Names.navigator property in his code should be an optional to make it parse the current output from Swift 5.5's symbol-graph-extract dump.

@owenv How did you figure out the format? Did you reverse engineer it just by looking at the source code of SymbolGraphGen or did you have some other source of information?

I don't know how to get to the help command of the swift-frontend but using the Swift 5.5 toolchain and executing swift symbolgraph-extract -help gives me:

OVERVIEW: Swift Symbol Graph Extractor

USAGE: swift-frontend

OPTIONS:
  -Fsystem <value>      Add directory to system framework search path
  -F <value>            Add directory to framework search path
  -help                 Display available options
  -I <value>            Add directory to the import search path
  -L <value>            Add directory to library link search path
  -minimum-access-level <level>
                        Include symbols with this access level or more
  -module-cache-path <value>
                        Specifies the Clang module cache path
  -module-name <value>  Name of the module to build
  -output-dir <dir>     Symbol Graph JSON Output Directory (Required)
  -pretty-print         Pretty-print the output JSON
  -resource-dir </usr/lib/swift>
                        The directory that holds the compiler resource files
  -sdk <sdk>            Compile against <sdk>
  -skip-inherited-docs  Skip emitting doc comments for members inherited through classes or default implementations
  -skip-synthesized-members
                        Skip members inherited through classes or default implementations
  -swift-version <vers> Interpret input according to a specific Swift language version number
  -target <triple>      Generate code for the given target <triple>, such as x86_64-apple-macos10.9
  -v                    Show commands to run and use verbose output
  -Xcc <arg>            Pass <arg> to the C/C++/Objective-C compiler

Which has some options that look to me like they are very specific for the Symbol Graph Extractor.
Especially the pretty-print and skip-inherited-docs are useful tools to expand/reduce the size of the JSON dump.

I never really worked on this beyond a rough prototype, so it's pretty broken & out-of-date now. I wouldn't recommend trying to use it in a serious project but some small parts may still be useful.

Yeah, if you look through that directory you can find the serialization code used to generate the nodes, mixins and edges that appear in the output. It can also be helpful to dump a symbol graph for a large system framework to see how all the pieces fit together.


Also, in addition to using swift-symbolgraph-extract directly, you can also pass -emit-symbol-graph/-emit-symbol-graph-dir to the driver when building a module, which is easier in some cases (Add optional -emit-symbol-graph output when emitting modules by bitjammer · Pull Request #35110 · apple/swift · GitHub)

2 Likes

Of course we can always fall back to reading C++ code to get all the details. But your Codable structs with comments helped me out in understanding the structure a bit faster :wink:. So thanks for that :pray: