Documentation
¶
Overview ¶
Package sema owns thrift-ls's semantic analysis: the lint pipeline, the diagnostics it produces, and the fixes attached to them. All positions are parser coordinates; frontends translate to their own representations.
Index ¶
- Constants
- func BareName(name string) string
- func IncludeNameOf(file uri.URI) string
- func IsBasicType(t string) bool
- func ParseIdent(cur uri.URI, includes []*syntax.Include, identifier string) (include, ident string)
- func RefKindsFor(k DefinitionKind) []store.RefKind
- func TypeReferenceName(ft *syntax.FieldType) string
- type Action
- type ActionProvider
- type Analyzer
- type Config
- type DefinitionKind
- type Diagnostic
- type Edit
- type EnumImplicitValue
- type EnumMemberValue
- type File
- type FileAnalyzer
- type Fix
- type FixResult
- type Fixer
- type Graph
- type Hit
- type Index
- func (x *Index) FindInWorkspace(ctx context.Context, name string) (*Resolved, error)
- func (x *Index) References(ctx context.Context, file uri.URI, name string, kinds ...store.RefKind) ([]Hit, error)
- func (x *Index) ReferencesTo(ctx context.Context, def *Resolved, kinds ...store.RefKind) ([]Hit, error)
- func (x *Index) ReferencingFiles(file uri.URI) []uri.URI
- func (x *Index) ResolveService(ctx context.Context, from *store.ParsedFile, ident *syntax.Identifier) (*Resolved, error)
- func (x *Index) ResolveType(ctx context.Context, from *store.ParsedFile, ft *syntax.FieldType) (*Resolved, error)
- func (x *Index) ResolveValue(ctx context.Context, from *store.ParsedFile, v *syntax.ConstValue) (*Resolved, error)
- func (x *Index) UnderlyingType(ctx context.Context, from *store.ParsedFile, ft *syntax.FieldType) (*syntax.FieldType, *store.ParsedFile)
- func (x *Index) View() Graph
- type Pipeline
- func (p *Pipeline) CodeActions(ctx context.Context, view Graph, file uri.URI, span Span, report Report) []Action
- func (p *Pipeline) FixAll(ctx context.Context, view Store, targets []uri.URI, ...) (FixResult, error)
- func (p *Pipeline) FixesForFile(ctx context.Context, view Graph, file uri.URI, report Report) []Fix
- func (p *Pipeline) Run(ctx context.Context, view Graph, changed []uri.URI) (Report, error)
- func (p *Pipeline) WithAnalyzers(analyzers ...Analyzer) *Pipeline
- func (p *Pipeline) WithFixers(fs ...Fixer) *Pipeline
- func (p *Pipeline) WithProviders(ps ...ActionProvider) *Pipeline
- type Report
- type Resolved
- type Run
- type Severity
- type SkippedFix
- type Span
- type Store
Constants ¶
const ( CodeParseError = "parse-error" CodeIncludeCycle = "include-cycle" CodeFieldIDRange = "field-id-range" CodeFieldIDConflict = "field-id-conflict" CodeDuplicateDef = "duplicate-definition" CodeDuplicateEnumVal = "duplicate-enum-value" CodeDuplicateValue = "duplicate-value" CodeImplicitEnumValue = "implicit-enum-value" CodeUnusedInclude = "unused-include" CodeIncludeShadow = "include-shadow" CodeUndefinedType = "undefined-type" CodeUndefinedValue = "undefined-value" CodeValueTypeMismatch = "value-type-mismatch" CodeNonScalarMapKey = "non-scalar-map-key" CodeUnknownAnnotation = "unknown-annotation-type" )
Diagnostic codes carried on every diagnostic the pipeline reports. Fix providers and frontend filters match on these — never on the message text, which is free to change.
Variables ¶
This section is empty.
Functions ¶
func BareName ¶
BareName strips the include qualifier from a name: "base.User" becomes "User". References in files that include the definition file use the bare name, so qualified literals must match against it too.
func IncludeNameOf ¶
IncludeNameOf returns the include name of a file URI: the base name without extension. file:///base.thrift -> "base".
func IsBasicType ¶
IsBasicType reports whether t is a built-in base type.
func ParseIdent ¶
ParseIdent parses an identifier. identifier format:
- identifier
- include.identifier
it returns include, ident
func RefKindsFor ¶
func RefKindsFor(k DefinitionKind) []store.RefKind
RefKindsFor returns the reference slots a definition kind can appear in.
An exception is thrown (signatures) but never used as a field type; as an annotation type it is legal, since the compiler's get_type resolves any declared type. Enum values and consts live in value positions. Services are extends-only. Every other type can appear in field, signature, and annotation-type slots.
func TypeReferenceName ¶
TypeReferenceName returns the referenced type name of a FieldType, or "" for base types and containers.
Types ¶
type Action ¶
type Action struct {
Title string
Fix bool // true: quickfix for a diagnostic; false: refactor
File uri.URI
Edits []Edit
}
Action is an offered source edit: a quickfix for a diagnostic or a refactor.
type ActionProvider ¶
type ActionProvider interface {
Actions(ctx context.Context, f File, span Span, report Report) []Action
}
ActionProvider offers source edits for a selection, independent of any diagnostic: refactors. The report is available for providers whose actions double as quickfixes for diagnostics overlapping the selection.
type Analyzer ¶
Analyzer is a whole-run check: its findings may span files (include cycles are the current consumer), and it may need to see files that do not parse (Parse is the current consumer).
type Config ¶
type Config struct {
// Disabled names analyzers (by Name) to skip. nil runs all.
Disabled []string
// Severity overrides a diagnostic's severity by code.
Severity map[string]Severity
}
Config selects and tunes analyzers.
func ConfigFromLint ¶
ConfigFromLint builds a Config from the config layer's lint settings: the analyzer names to skip and the severity overrides by code, with severities named "error", "warning", "info", and "hint". Unknown names are ignored; the config sources validate them.
type DefinitionKind ¶
type DefinitionKind uint8
DefinitionKind identifies the kind of a resolved definition.
const ( DefinitionNone DefinitionKind = iota DefinitionStruct DefinitionUnion DefinitionException DefinitionEnum DefinitionTypedef DefinitionConst DefinitionEnumValue DefinitionService )
type Diagnostic ¶
type Diagnostic struct {
Code string // stable identity; never matched by message
Severity Severity
Message string
Span Span
Fixes []Fix
}
Diagnostic is one finding. Fixes are edits that resolve it, computed by the analyzer that reported the diagnostic.
type Edit ¶
Edit replaces Span with NewText in the file the diagnostic belongs to. An empty Span inserts.
type EnumImplicitValue ¶
type EnumImplicitValue struct {
Member *syntax.EnumValue
Value int64
Known bool // false when the preceding value is broken, so Value is unknowable
}
EnumImplicitValue is an enum member that lacks an explicit value, with the int constant the compiler auto-increments for it.
func EnumImplicitValues ¶
func EnumImplicitValues(enum *syntax.Enum) []EnumImplicitValue
EnumImplicitValues reports the members of an enum that carry no explicit value, together with the value the compiler would auto-increment: 0 for the first member, one greater than the preceding member's value otherwise. Members after an unparseable explicit constant report Known=false until the next parseable constant settles the chain.
type EnumMemberValue ¶ added in v0.2.2
EnumMemberValue is an enum member with the value the compiler resolves for it: explicit constants parse as written (base-0), implicit members auto-increment (0 for the first member, one greater than the preceding member's value otherwise). Known is false when the preceding value is broken, so Value is unknowable until the next parseable constant settles the chain.
func EnumMemberValues ¶ added in v0.2.2
func EnumMemberValues(enum *syntax.Enum) []EnumMemberValue
EnumMemberValues resolves every member of an enum to its on-wire value.
type File ¶
type File struct {
URI uri.URI
PF *store.ParsedFile
// contains filtered or unexported fields
}
File is one file's analysis inputs: its parsed tree plus the run's shared state.
func NewFile ¶ added in v0.2.2
NewFile builds the File a direct Analyzer, Fixer, or ActionProvider call needs: the parsed file plus a run holding view and a fresh Index. Pipeline runs build Files themselves; this is for unit tests and one-off invocations outside a run.
type FileAnalyzer ¶
type FileAnalyzer interface {
Name() string
AnalyzeFile(ctx context.Context, f File) ([]Diagnostic, error)
}
FileAnalyzer is the per-file shape most checks take: findings depend only on the file itself. The runner loops files, skips unparseable ones, and collects read errors; the analyzer never sees the loop.
type Fix ¶
Fix is a named set of edits resolving one diagnostic. The title is what the client shows in its quickfix menu.
func Apply ¶ added in v0.2.0
Apply returns content with the fixes applied. A fix is all-or-nothing: when any of its edits overlaps an accepted fix's edits — or its own — the whole fix is skipped. Insertions (empty spans) never conflict with each other; at the same offset they land in argument order, and just before a replacement starting there. applied and skipped partition fixes in argument order. An edit outside content is an error: offsets come from the same parse the fixes were computed on, so a out-of-range span is a fixer bug, not a condition to skip over.
type FixResult ¶ added in v0.2.0
type FixResult struct {
// Applied is the number of fixes applied across all passes.
Applied int
// FixedFiles lists the files whose content changed, sorted by URI.
FixedFiles []uri.URI
// Skipped lists the fixes that could not apply.
Skipped []SkippedFix
// Passes is the number of pipeline runs.
Passes int
// Remaining is the last pass's report: the diagnostics left after the
// final pass, fixable or not.
Remaining Report
}
FixResult reports one FixAll run.
type Fixer ¶
type Fixer interface {
Fix(ctx context.Context, f File, d Diagnostic) []Fix
}
Fixer computes fixes for diagnostics reported by other analyzers, on demand — for fixes too expensive to compute during analysis (a workspace search per unresolved type, for instance). The fixer self-filters on the diagnostic's code and returns nothing when it has none.
type Graph ¶ added in v0.2.0
type Graph interface {
Parse(ctx context.Context, file uri.URI) (*store.ParsedFile, error)
Dependents(file uri.URI) []uri.URI
Includers(file uri.URI) []uri.URI
KnownFiles() []uri.URI
Folder() uri.URI
WalkFiles(ctx context.Context, root uri.URI, fn func(uri.URI) error) error
Resolver() *store.Resolver
}
Graph is the read surface analysis needs from the document store: parse files and walk the include graph. Analyzers, fixers, and providers see only this; the store's write and concurrency surface (Update, Evict, generations) stays with the session owner, except for the batch fixer below.
type Hit ¶
type Hit struct {
File uri.URI
Span Span // parser coordinates; the frontend maps them
Text string // as written: "User", "shared.User", "shared.thrift.User"
// Kind is the grammar slot the reference sits in, so callers can tell
// type hits from value hits (e.g. for highlight kinds).
Kind store.RefKind
}
Hit is one reference occurrence of a name, with the qualifying text preserved so a rename can rewrite includes correctly.
type Index ¶
type Index struct {
// contains filtered or unexported fields
}
Index answers cross-file semantic queries over one view: definition resolution and reference search. It composes per-file store.FileIndexes over the include graph.
An Index is cheap — construct one per request with NewIndex. Resolutions are memoized per (file, name), so a request resolving the same name in the same file repeatedly (references, diagnostics) resolves it once.
func (*Index) FindInWorkspace ¶
FindInWorkspace returns the definition of name in any known file of the workspace, falling back to a directory walk when the workspace has not been indexed yet (e.g. a quick-fix on the first didOpen).
func (*Index) References ¶
func (x *Index) References(ctx context.Context, file uri.URI, name string, kinds ...store.RefKind) ([]Hit, error)
References returns every occurrence of name in file and in every file that transitively includes it, restricted to the given reference kinds. The definition site is not included (no self-referencing hit). Hits are matched by bare name only; use ReferencesTo for resolution-matched results.
func (*Index) ReferencesTo ¶
func (x *Index) ReferencesTo(ctx context.Context, def *Resolved, kinds ...store.RefKind) ([]Hit, error)
ReferencesTo returns every reference to def: in def.File and every file that transitively includes it. Hits are name- and resolution-matched, so same-named definitions elsewhere are not reported.
For an enum def, value references qualified with the enum name ("Color.RED", "shared.Color.RED") are matched too, provided the qualifier resolves to this very enum; the hit covers only the enum segment, so a rename rewrites the qualifier while keeping the member name.
func (*Index) ReferencingFiles ¶
ReferencingFiles returns every file that directly includes file, in graph order.
func (*Index) ResolveService ¶
func (x *Index) ResolveService(ctx context.Context, from *store.ParsedFile, ident *syntax.Identifier) (*Resolved, error)
ResolveService resolves a service name or extends reference, or returns nil when unresolved.
func (*Index) ResolveType ¶
func (x *Index) ResolveType(ctx context.Context, from *store.ParsedFile, ft *syntax.FieldType) (*Resolved, error)
ResolveType resolves a type reference to its definition, or returns nil when unresolved (base types, unresolvable name). Files backing the resolution may fail to read; those count as unresolved.
func (*Index) ResolveValue ¶
func (x *Index) ResolveValue(ctx context.Context, from *store.ParsedFile, v *syntax.ConstValue) (*Resolved, error)
ResolveValue resolves a const-value identifier to its definition (an enum value or a const), or returns nil when unresolved.
func (*Index) UnderlyingType ¶
func (x *Index) UnderlyingType(ctx context.Context, from *store.ParsedFile, ft *syntax.FieldType) (*syntax.FieldType, *store.ParsedFile)
UnderlyingType follows typedef chains — across includes — until it reaches a type that is not itself a typedef, returning that type and the parsed file whose scope it resolves in. Every consumer that classifies a type by kind (value matching, completion, checks) resolves through here instead of comparing surface names, so "typedef map<string,string> M" classifies as a map everywhere.
An identifier that resolves to no definition returns nil — the type has no classifiable kind, and existence is another check's job. A cyclic or pathological chain stops at maxTypedefDepth steps.
type Pipeline ¶
type Pipeline struct {
// contains filtered or unexported fields
}
Pipeline runs analyzers over changed files. It is a value: safe to share, no state beyond the analyzers, fixers, providers, and config.
func (*Pipeline) CodeActions ¶
func (p *Pipeline) CodeActions(ctx context.Context, view Graph, file uri.URI, span Span, report Report) []Action
CodeActions returns the actions for span in file: quickfixes from the report's diagnostics overlapping span (inline fixes first, then the fixers), then the action providers' refactors.
func (*Pipeline) FixAll ¶ added in v0.2.0
func (p *Pipeline) FixAll(ctx context.Context, view Store, targets []uri.URI, persist func(context.Context, uri.URI, []byte) error) (FixResult, error)
FixAll runs the pipeline over targets and applies every applicable fix, re-running until a pass applies no fix or maxFixPasses passes have run. Only targets are analyzed and fixed; resolution and fixers read the whole view, so fixing one file of a workspace resolves against all of it and never touches the other files.
persist receives each changed file's new content: it must make the content durable (write to disk, update the editor overlay) and visible to view's file source before returning. FixAll drives view.Update itself, so persist must not.
func (*Pipeline) FixesForFile ¶ added in v0.2.0
FixesForFile returns every fix the pipeline offers for file: the inline fixes of the report's diagnostics for it, then the fixers' output for each diagnostic. Fixes are edits in parser coordinates on the file's current content.
func (*Pipeline) Run ¶
Run analyzes changed over view: one run, one shared Index across all changed files and all analyzers.
func (*Pipeline) WithAnalyzers ¶ added in v0.2.0
WithAnalyzers returns a copy of the pipeline with analyzers appended.
func (*Pipeline) WithFixers ¶
WithFixers returns a copy of the pipeline with the fixers added.
func (*Pipeline) WithProviders ¶
func (p *Pipeline) WithProviders(ps ...ActionProvider) *Pipeline
WithProviders returns a copy of the pipeline with the action providers added.
type Report ¶
type Report map[uri.URI][]Diagnostic
Report is the result of one analysis pass, keyed by file.
type Resolved ¶
type Resolved struct {
File uri.URI
Parsed *store.ParsedFile
// Name is the definition's identifier node, whose range is the jump
// target.
Name *syntax.Identifier
// Node is the definition itself: *syntax.Struct, *syntax.Enum, etc.
// For an enum value, Node is the *syntax.Identifier (same as Name).
Node syntax.Node
Kind DefinitionKind
}
Resolved is a resolved definition: the target file, the parsed document, the definition identifier (jump target), and its kind.
func DefFromNode ¶
func DefFromNode(pf *store.ParsedFile, n syntax.Node) *Resolved
DefFromNode builds a Resolved from any top-level definition node. Use when the concrete type and Kind are not known statically (e.g. FindInWorkspace).
type Run ¶
type Run struct {
// contains filtered or unexported fields
}
Run is one analysis pass. Analyzers add findings to it.
func (*Run) Add ¶
func (r *Run) Add(file uri.URI, ds ...Diagnostic)
Add appends findings for file, applying the run's severity overrides. The findings are copied: the caller keeps ownership of its slice.
type SkippedFix ¶ added in v0.2.0
SkippedFix is a fix FixAll could not apply.
type Span ¶
Span is a half-open file region in parser coordinates (1-based line and rune column, byte offset). The byte offsets are authoritative; the LSP frontend maps them through the file mapper to UTF-16.
func LineSpan ¶ added in v0.2.2
LineSpan returns the span of the whole source line containing pos, the trailing newline included. Fixers use this to delete line-held statements (unused includes) whole.
func SpanOf ¶
func SpanOf(pf *store.ParsedFile, node syntax.Node) Span
SpanOf returns the source span of a node in the parsed file.
type Store ¶ added in v0.2.0
type Store interface {
Graph
Update(ctx context.Context, changes ...*store.FileChange) store.ChangeResult
}
Store is the read-write surface batch fixing needs: analyze through Graph, land each pass through Update.