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) []cache.RefKind
- func TypeReferenceName(ft *syntax.FieldType) string
- type Action
- type ActionProvider
- type AddIncludeFixer
- type Analyzer
- type Config
- type CycleCheck
- type DefinitionKind
- type Diagnostic
- type DuplicateCheck
- type Edit
- type EnumImplicitValue
- type EnumValueCheck
- type EnumValuesProvider
- type FieldIDCheck
- type FieldQualifierProvider
- type File
- type FileAnalyzer
- type Fix
- type Fixer
- type Hit
- type Include
- type IncludeShadowCheck
- 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 ...cache.RefKind) ([]Hit, error)
- func (x *Index) ReferencesTo(ctx context.Context, def *Resolved, kinds ...cache.RefKind) ([]Hit, error)
- func (x *Index) ReferencingFiles(file uri.URI) []uri.URI
- func (x *Index) ResolveService(ctx context.Context, from *cache.ParsedFile, ident *syntax.Identifier) (*Resolved, error)
- func (x *Index) ResolveType(ctx context.Context, from *cache.ParsedFile, ft *syntax.FieldType) (*Resolved, error)
- func (x *Index) ResolveValue(ctx context.Context, from *cache.ParsedFile, v *syntax.ConstValue) (*Resolved, error)
- func (x *Index) UnderlyingType(ctx context.Context, from *cache.ParsedFile, ft *syntax.FieldType) (*syntax.FieldType, *cache.ParsedFile)
- func (x *Index) View() *cache.View
- type ParseCheck
- type Pipeline
- func (p *Pipeline) CodeActions(ctx context.Context, view *cache.View, file uri.URI, span Span, report Report) []Action
- func (p *Pipeline) Run(ctx context.Context, view *cache.View, changed []uri.URI) (Report, error)
- func (p *Pipeline) WithFixers(fs ...Fixer) *Pipeline
- func (p *Pipeline) WithProviders(ps ...ActionProvider) *Pipeline
- type Report
- type Resolved
- type Run
- type SemanticAnalysis
- type Severity
- type Span
- type UnusedIncludeCheck
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) []cache.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 AddIncludeFixer ¶
type AddIncludeFixer struct{}
AddIncludeFixer offers the include that makes an undefined type resolve: the definition is searched across the workspace, so the fix works project-wide. It fixes only "undefined-type" diagnostics.
func (AddIncludeFixer) Fix ¶
func (f AddIncludeFixer) Fix(ctx context.Context, file File, d Diagnostic) []Fix
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 CycleCheck ¶
type CycleCheck struct{}
CycleCheck reports include edges that close a cycle: the include X -> Y is reported when Y transitively includes X back. Cycles of any length are caught, including self-includes.
func (*CycleCheck) Name ¶
func (c *CycleCheck) Name() string
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 DuplicateCheck ¶
type DuplicateCheck struct{}
DuplicateCheck reports the duplicates the compiler rejects: a name defined twice in one scope — structs, unions, exceptions, enums, members, fields, function arguments, service functions, typedefs, constants — enum members whose resolved value collides, and map keys or set values that repeat in a constant. Duplicates are ambiguous on the wire and in generated code, so they are errors.
func (*DuplicateCheck) AnalyzeFile ¶
func (c *DuplicateCheck) AnalyzeFile(ctx context.Context, f File) ([]Diagnostic, error)
func (*DuplicateCheck) Name ¶
func (c *DuplicateCheck) Name() string
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 EnumValueCheck ¶
type EnumValueCheck struct{}
func (*EnumValueCheck) AnalyzeFile ¶
func (c *EnumValueCheck) AnalyzeFile(ctx context.Context, f File) ([]Diagnostic, error)
func (*EnumValueCheck) Name ¶
func (c *EnumValueCheck) Name() string
EnumValueCheck warns on enum members that lack an explicit value.
The compiler auto-increments implicit members: 0 for the first member, one greater than the preceding member's value otherwise. Their on-wire value therefore follows their position; inserting, removing, or reordering members silently changes serialized data.
type EnumValuesProvider ¶
type EnumValuesProvider struct{}
EnumValuesProvider offers the rewrite that appends an explicit value to every member of the enum containing the selection, mirroring the auto-incremented constants the compiler would assign. When a diagnostic overlaps the selection, the same action is also offered as its quickfix.
type FieldIDCheck ¶
type FieldIDCheck struct{}
func (*FieldIDCheck) AnalyzeFile ¶
func (c *FieldIDCheck) AnalyzeFile(ctx context.Context, f File) ([]Diagnostic, error)
func (*FieldIDCheck) Name ¶
func (c *FieldIDCheck) Name() string
FieldIDCheck checks struct, union, exception, function parameter, and throws field ids: they must be unique positive integers in [1, 32767].
type FieldQualifierProvider ¶
type FieldQualifierProvider struct{}
FieldQualifierProvider offers the "Make field required" and "Make field optional" actions for the fields whose declaration line the selection covers. Every covered field yields one action per qualifier it does not already carry: an unqualified field gets both, a required field gets "Make field optional", and vice versa. Union fields never offer "Make field required": unions have no required members.
type File ¶
type File struct {
URI uri.URI
PF *cache.ParsedFile
// contains filtered or unexported fields
}
File is one file's analysis inputs: its parsed tree plus the run's shared state.
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.
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 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 cache.RefKind
}
Hit is one reference occurrence of a name, with the qualifying text preserved so a rename can rewrite includes correctly.
type IncludeShadowCheck ¶
type IncludeShadowCheck struct{}
IncludeShadowCheck warns when one include path matches files under more than one include path. The nearest include path wins; the warning names the rest.
func (*IncludeShadowCheck) AnalyzeFile ¶
func (c *IncludeShadowCheck) AnalyzeFile(ctx context.Context, f File) ([]Diagnostic, error)
func (*IncludeShadowCheck) Name ¶
func (c *IncludeShadowCheck) Name() string
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 cache.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 ...cache.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 ...cache.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 *cache.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 *cache.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 *cache.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 *cache.ParsedFile, ft *syntax.FieldType) (*syntax.FieldType, *cache.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 ParseCheck ¶
type ParseCheck struct{}
ParseCheck reports the lexer's and parser's errors and warnings for every changed file. It is a whole-run analyzer because it must see files whose AST is too broken for the other analyzers — they skip those, this one reports them.
func (*ParseCheck) Name ¶
func (p *ParseCheck) Name() string
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 DefaultPipeline ¶
DefaultPipeline composes the built-in analyzers with their fixers and action providers.
func (*Pipeline) CodeActions ¶
func (p *Pipeline) CodeActions(ctx context.Context, view *cache.View, 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) Run ¶
Run analyzes changed over view: one run, one shared Index across all changed files and all analyzers.
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 *cache.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 *cache.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.
type SemanticAnalysis ¶
type SemanticAnalysis struct{}
func (*SemanticAnalysis) AnalyzeFile ¶
func (s *SemanticAnalysis) AnalyzeFile(ctx context.Context, f File) ([]Diagnostic, error)
func (*SemanticAnalysis) Name ¶
func (s *SemanticAnalysis) Name() string
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.
type UnusedIncludeCheck ¶
type UnusedIncludeCheck struct{}
UnusedIncludeCheck reports an include no reference in the file resolves into. Unused includes bloat the compile and make the dependency graph look worse than it is.
func (*UnusedIncludeCheck) AnalyzeFile ¶
func (c *UnusedIncludeCheck) AnalyzeFile(ctx context.Context, f File) ([]Diagnostic, error)
func (*UnusedIncludeCheck) Name ¶
func (c *UnusedIncludeCheck) Name() string