Extracting the inferred type with SwiftSyntax

Given the following data structure:

public struct Test {
  var trueKey: Bool = true
  var falseKey = false
}

if we go through the abstract syntax tree var trueKey: Bool = true returns, among other things the TypeAnnotationSyntax as follow:

├─bindingSpecifier: keyword(SwiftSyntax.Keyword.var)
╰─bindings: PatternBindingListSyntax
  ╰─[0]: PatternBindingSyntax
    ├─pattern: IdentifierPatternSyntax
    │ ╰─identifier: identifier("trueKey")
    ├─typeAnnotation: TypeAnnotationSyntax
    │ ├─colon: colon
    │ ╰─type: IdentifierTypeSyntax
    │   ╰─name: identifier("Bool")
    ╰─initializer: InitializerClauseSyntax
      ├─equal: equal
      ╰─value: BooleanLiteralExprSyntax
        ╰─literal: keyword(SwiftSyntax.Keyword.true)

whereas var falseKey = false does not include the TypeAnnotationSyntax type as it's inferred and returns:

VariableDeclSyntax
├─bindingSpecifier: keyword(SwiftSyntax.Keyword.var)
╰─bindings: PatternBindingListSyntax
  ╰─[0]: PatternBindingSyntax
    ├─pattern: IdentifierPatternSyntax
    │ ╰─identifier: identifier("falseKey")
    ╰─initializer: InitializerClauseSyntax
      ├─equal: equal
      ╰─value: BooleanLiteralExprSyntax
        ╰─literal: keyword(SwiftSyntax.Keyword.false)

Is there a reason for that? Moreover is there a way to extract the type in a generic way?

SwiftSyntax only represents the actual syntax present in the file, so this isn't possible. Types aren't inferred until later on during semantic analysis.

You can try to make a best guess when the value being assigned is a literal, but code like this will always be a problem:

var x = someFunction()
4 Likes

You can eventually pass the type as parameter to the macro (not ideal, but that what i got) and eventually enforce that only when the syntax tree doesn't have it with a diagnostic

Yeah no I am aware of the alternatives, it just seemed weird given that the AST has an initializer.value type that can lead to extracting the type - and I can do that manually for more straightforward types (like bool, string and Int) -, but as @allevato explained this is done at a later stage.

Thanks to both on the feedback :)

I wrote an advanced macro that will generate an initialiser even parse all standard inferred data types

What’s the reason for such a macro? Isn’t what it generates is what the compiler generates aside from default values.

@young And it’s public by default. I work a lot on SDKs. And their data models tend to be public so I have to explicitly write the initialiser every time as by default it’s internal. So that’s a lot of unnecessary lines of code.

2 Likes