scanner

package
v0.8.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 19, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Rendered for darwin/amd64

Overview

Package scanner drives the first phase of the code-generation pipeline.

It invokes xcrun clang with -ast-dump=json on a framework's umbrella header, unmarshals the resulting Clang JSON AST, and walks it to produce a [meta.FrameworkMeta] value that captures the framework's complete public API: classes, protocols, enums, structs, free functions, extern constants, block types, and typedefs.

Extraction is restricted to declarations whose source file belongs to the named framework's own headers (filter.go), so re-exported types from other frameworks are skipped and attributed to their true owner during the load phase.

Key entry points:

  • DumpAST — invokes xcrun clang and returns the parsed AST root node.
  • Extract — walks an AST value and returns a populated [meta.FrameworkMeta].

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CLibraryHeader

func CLibraryHeader(sdkPath, name string) string

CLibraryHeader returns the umbrella header path for a known Apple C library. If the library definition specifies a custom Header path, that is used; otherwise the default {Name}/{Name}.h convention applies.

func CLibraryHeaderDir

func CLibraryHeaderDir(sdkPath, name string) string

CLibraryHeaderDir returns the filter path for a known Apple C library. This is the path prefix used to decide whether an AST node belongs to the library. It may be a directory (e.g. "bsm/") or an exact file (e.g. "sandbox.h") depending on how the library's headers are laid out.

func CLibraryHeaderRelative

func CLibraryHeaderRelative(name string) string

CLibraryHeaderRelative returns the umbrella header include path for a known Apple C library, relative to {SDK}/usr/include/ and slash-separated — the form used in a generated "#include <…>" directive (e.g. "compression.h", "os/log.h", "bsm/libbsm.h"). Defaults to the {Name}/{Name}.h convention when the library definition has no custom Header.

func ClangVersion

func ClangVersion() (string, error)

ClangVersion returns the version line of the clang binary that DumpAST invokes (e.g. "Apple clang version 21.0.0 (clang-2100.3.9.2)"). Recorded in scanned metadata because clang releases have changed AST output in ways that affect extraction (Clang 21 stopped emitting availability versions).

func DetectSubFrameworkNames

func DetectSubFrameworkNames(bundlePath string) []string

DetectSubFrameworkNames returns the names of sub-frameworks inside bundlePath/Frameworks/ that have a valid umbrella header. Returns nil when the framework is not an umbrella.

func Extract

func Extract(root *ASTNode, sdkPath, frameworkName, sdkVersion, arch string) *macosplatformmetadata.FrameworkMeta

Extract walks the Clang AST root node and produces a FrameworkMeta containing only declarations that originate from the named framework's headers.

func FrameworkBundlePath

func FrameworkBundlePath(sdkPath, name string) string

FrameworkBundlePath returns the path to a framework's .framework bundle. It handles both top-level frameworks and "Parent/Child" sub-frameworks.

func FrameworkHeader

func FrameworkHeader(sdkPath, framework string) string

FrameworkHeader returns the path to the umbrella header for a framework. framework may be "Name" (top-level) or "Parent/Child" (sub-framework). Falls back to the C library header path for known Apple C libraries.

func FrameworkHeaderDir

func FrameworkHeaderDir(sdkPath, framework string) string

FrameworkHeaderDir returns the directory containing all headers for a framework. framework may be "Name" (top-level) or "Parent/Child" (sub-framework). Falls back to the C library header directory for known Apple C libraries.

func IsCLibrary

func IsCLibrary(sdkPath, name string) bool

IsCLibrary reports whether name is a known Apple C library whose umbrella header exists on the filesystem.

func IsCLibraryName

func IsCLibraryName(name string) bool

IsCLibraryName reports whether name is registered as a known Apple C library, without performing any filesystem check. Use this when an SDK path is not available (e.g. when constructing metadata output paths during a scan).

func IsSubFramework

func IsSubFramework(name string) bool

IsSubFramework reports whether name uses "Parent/Child" sub-framework notation.

func IsSwiftOnly

func IsSwiftOnly(bundlePath string) bool

IsSwiftOnly reports whether the framework at bundlePath has no ObjC surface and is implemented entirely in Swift (indicated by a .swiftmodule directory).

func ListCLibraries

func ListCLibraries(sdkPath string) ([]string, error)

ListCLibraries returns the sorted names of known Apple C libraries present in sdkPath. If sdkPath is empty it is auto-detected via xcrun.

func ListFrameworks

func ListFrameworks(sdkPath string) ([]string, error)

ListFrameworks returns the sorted names of all frameworks available in sdkPath that have a valid umbrella header (Framework.framework/Headers/Framework.h). If sdkPath is empty it is auto-detected via xcrun. Sub-frameworks nested inside umbrella frameworks (e.g. Carbon/HIToolbox) are included using "Parent/Child" notation so the scanner can locate them.

func LoadCLibrariesFile

func LoadCLibrariesFile(path string) (bool, error)

LoadCLibrariesFile replaces the active C library registry with the contents of a JSON config file (map of library name → CLibraryDef). Adding a new Apple C library is then a data change plus re-scan, not a Go change. Returns false without error when the file does not exist.

func LoadScanConfigFile

func LoadScanConfigFile(path string) (bool, error)

LoadScanConfigFile replaces the active per-framework scan configuration with the contents of a JSON config file (map of framework name → ScanConfig). Returns false without error when the file does not exist.

func SDKPath

func SDKPath() (string, error)

SDKPath returns the path to the active macOS SDK via xcrun.

func SDKVersion

func SDKVersion() (string, error)

SDKVersion returns the macOS SDK version string (e.g. "26.5").

func SubFrameworkParts

func SubFrameworkParts(name string) (parent, child string)

SubFrameworkParts splits a "Parent/Child" name into parent and child.

func XcodeVersion

func XcodeVersion() (string, error)

XcodeVersion returns the active Xcode version and build, joined on one line (e.g. "Xcode 26.0 Build version 17A321"). Recorded in scanned metadata as toolchain provenance.

Types

type ASTNode

type ASTNode struct {
	ID          string    `json:"id"`
	Kind        string    `json:"kind"`
	Loc         *Location `json:"loc"`
	Range       *SrcRange `json:"range"`
	Name        string    `json:"name"`
	MangledName string    `json:"mangledName"`
	Type        *ASTType  `json:"type"`
	ReturnType  *ASTType  `json:"returnType"`
	Inner       []ASTNode `json:"inner"`

	// PreviousDecl is the hex ID string of the previous declaration of this
	// name in the same translation unit. When set, this node is a redeclaration
	// (e.g. @class Foo; repeated after the canonical @interface Foo...@end, or
	// an empty @class Foo; that precedes the canonical definition). Used in
	// extractClass to distinguish canonical definitions from foreign-class
	// forward declarations.
	PreviousDecl string `json:"previousDecl"`

	// ObjCInterfaceDecl / ObjCCategoryDecl
	Super     *ASTRef  `json:"super"`
	Protocols []ASTRef `json:"protocols"`
	// ObjCCategoryDecl: the class this category extends
	Interface *ASTRef `json:"interface"`

	// ObjCMethodDecl — Clang uses "instance": true for instance methods,
	// absence of the field (or false) means class method.
	IsInstance bool `json:"instance"`
	IsVariadic bool `json:"variadic"`
	IsImplicit bool `json:"implicit"`

	// ObjCPropertyDecl
	Getter *ASTRef `json:"getter"`
	Setter *ASTRef `json:"setter"`
	// property attribute flags: "readonly", "copy", "weak", "assign", etc.
	PropertyAttributes []string `json:"propertyAttributes"`

	// EnumDecl
	FixedUnderlyingType *ASTType `json:"fixedUnderlyingType"`

	// RecordDecl (struct/union)
	TagUsed            string `json:"tagUsed"`            // "struct" or "union"
	CompleteDefinition bool   `json:"completeDefinition"` // true for full record decl, false/missing for forward decl

	// VarDecl
	StorageClass string `json:"storageClass"` // "extern"

	// IntegerLiteral / FloatingLiteral / ConstantExpr
	// Clang emits numeric literals as JSON numbers; use RawMessage to handle both
	// number and string variants uniformly.
	Value RawValue `json:"value"`

	// Availability
	Availability []ASTNode `json:"availability"` // AvailabilityAttr nodes

	// AvailabilityAttr fields
	Platform   string `json:"platform"` // "macos", "ios"
	Introduced string `json:"introduced"`
	Deprecated string `json:"deprecated"`
	Obsoleted  string `json:"obsoleted"`
	Message    string `json:"message"`

	// ObjCTypeParamDecl — generic type params on a class
	// appears as inner nodes of ObjCInterfaceDecl
	Bound *ASTType `json:"bound"` // upper bound (usually "id")

	// Nullability qualifier attached to a type
	NullabilityQual string `json:"nullabilityQual"` // "nullable", "nonnull", "unspecified"

	// SwiftNameAttr carries the NS_SWIFT_NAME value; appears as an inner node.
	SwiftName string `json:"swiftName,omitempty"`
}

ASTNode is a node in the Clang JSON AST produced by clang -ast-dump=json. Only fields we actually use are decoded; unknown fields are silently ignored.

func DumpAST

func DumpAST(sdkPath, framework, arch string) (*ASTNode, error)

DumpAST invokes xcrun clang -ast-dump=json on the framework's umbrella header and returns the parsed top-level ASTNode. The arch parameter should be "arm64" or "x86_64".

type ASTRef

type ASTRef struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

type ASTType

type ASTType struct {
	QualType          string `json:"qualType"` // e.g. "NSArray<ObjectType> *", "NSUInteger"
	TypeAliasDeclID   string `json:"typeAliasDeclId"`
	DesugaredQualType string `json:"desugaredQualType"` // after typedef expansion
}

type CLibraryDef

type CLibraryDef struct {
	// LinkLib is the dylib name passed to -l (e.g. "EndpointSecurity" → -lEndpointSecurity).
	LinkLib string `json:"link_lib"`
	// Header is the umbrella header path relative to {SDK}/usr/include/.
	// Empty means use the default {Name}/{Name}.h convention.
	Header string `json:"header,omitempty"`
	// HeaderDir is the filter path relative to {SDK}/usr/include/ used to decide
	// whether an AST node belongs to this library. Empty means derive from Header.
	// Use a directory path (e.g. "bsm/") to accept all headers under that directory,
	// or a file path (e.g. "sandbox.h") to match only that exact file.
	HeaderDir string `json:"header_dir,omitempty"`
}

CLibraryDef describes a known Apple C library under {SDK}/usr/include/.

type IncludedFromRef

type IncludedFromRef struct {
	File string `json:"file"`
}

IncludedFromRef holds the file that directly includes the file containing a source location. Clang emits this when the declaration file is not the translation unit root.

type Location

type Location struct {
	FilePath string `json:"file"`
	Line     int    `json:"line"`
	Col      int    `json:"col"`
	// When the loc is a macro expansion the actual file is in ExpansionLoc
	ExpansionLoc *Location `json:"expansionLoc"`
	// Spelling location (pre-macro-expansion)
	SpellingLoc *Location `json:"spellingLoc"`
	// IncludedFrom is the file that directly includes the file this loc is in.
	// Present when loc.file is absent (Clang cursor optimisation) or when Clang
	// wants to indicate the include chain.
	IncludedFrom *IncludedFromRef `json:"includedFrom"`
}

func (*Location) IncludedFromFile

func (l *Location) IncludedFromFile() string

IncludedFromFile returns the path of the file that directly includes the file this location is in, or "" if none was recorded. Used by the framework filter to determine true framework ownership when loc.file is absent.

func (*Location) ResolvedFile

func (l *Location) ResolvedFile() string

ResolvedFile returns the most useful file path from this location, unwrapping macro expansions to find the real header file. For macro-expanded declarations (NS_ENUM, NS_OPTIONS, etc.) the expansion location is the call site in the SDK header, while the spelling location is the macro definition inside CoreFoundation. We want the call site.

func (*Location) ResolvedLine

func (l *Location) ResolvedLine() int

ResolvedLine returns the source line number, preferring spellingLoc over expansionLoc.

type RawValue

type RawValue string

RawValue holds an AST literal value which Clang may emit as a JSON number or as a JSON string. It normalises both to a plain string.

func (RawValue) MarshalJSON

func (r RawValue) MarshalJSON() ([]byte, error)

MarshalJSON writes the value as a JSON string.

func (RawValue) String

func (r RawValue) String() string

String returns the value as a plain Go string.

func (*RawValue) UnmarshalJSON

func (r *RawValue) UnmarshalJSON(data []byte) error

type ScanConfig

type ScanConfig struct {
	// ExtraIncludeDirs lists additional directories (relative to the SDK root,
	// slash-separated) whose headers count as belonging to this framework.
	ExtraIncludeDirs []string `json:"extra_include_dirs,omitempty"`
}

ScanConfig holds per-framework scan customisations.

type SrcRange

type SrcRange struct {
	Begin Location `json:"begin"`
	End   Location `json:"end"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL