analysis

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Feb 18, 2025 License: MIT Imports: 34 Imported by: 0

Documentation

Overview

scope resolution implementation for JS and TS files

Index

Constants

This section is empty.

Variables

View Source
var ScopeNodes = []string{
	"statement_block",
	"function_declaration",
	"function_expression",
	"for_statement",
	"for_in_statement",
	"for_of_statement",
}

Functions

func ChildWithFieldName

func ChildWithFieldName(node *sitter.Node, fieldName string) *sitter.Node

func ChildrenOfType

func ChildrenOfType(node *sitter.Node, nodeType string) []*sitter.Node

func ChildrenWithFieldName

func ChildrenWithFieldName(node *sitter.Node, fieldName string) []*sitter.Node

ChildrenWithFieldName returns all the children of a node with a specific field name. Tree-sitter can have multiple children with the same field name.

func FindMatchingChild

func FindMatchingChild(node *sitter.Node, predicate func(*sitter.Node) bool) *sitter.Node

FindMatchingChild iterates over all children of a node—both named and unnamed—and returns the first child that matches the predicate function.

func FirstChildOfType

func FirstChildOfType(node *sitter.Node, nodeType string) *sitter.Node

func GetExtFromLanguage

func GetExtFromLanguage(lang Language) string

func WalkTree

func WalkTree(node *sitter.Node, walker Walker)

Types

type Analyzer

type Analyzer struct {
	Language Language
	// WorkDir is the directory in which the analysis is being run.
	WorkDir string
	// ParseResult is the result of parsing a file with a tree-sitter parser,
	// along with some extra appendages (e.g: scope information).
	ParseResult *ParseResult

	// patternRules is a list of all rules that run after a query is run on the AST.
	// Usually, these are written in a DSL (which, for now, is the tree-sitter S-Expression query language)
	YmlRules []YmlRule
	// contains filtered or unexported fields
}

func FromFile

func FromFile(filePath string, baseRules []Rule) (*Analyzer, error)

func NewAnalyzer

func NewAnalyzer(file *ParseResult, rules []Rule) *Analyzer

func (*Analyzer) AddRule

func (ana *Analyzer) AddRule(rule Rule)

func (*Analyzer) Analyze

func (ana *Analyzer) Analyze() []*Issue

func (*Analyzer) OnEnterNode

func (ana *Analyzer) OnEnterNode(node *sitter.Node) bool

func (*Analyzer) OnLeaveNode

func (ana *Analyzer) OnLeaveNode(node *sitter.Node)

func (*Analyzer) Report

func (ana *Analyzer) Report(issue *Issue)

type Issue

type Issue struct {
	// The category of the issue
	Category config.Category
	// The severity of the issue
	Severity config.Severity
	// The message to display to the user
	Message string
	// The file path of the file that the issue was found in
	Filepath string
	// The range of the issue in the source code
	Range sitter.Range
	// (optional) The AST node that caused the issue
	Node *sitter.Node
	// Id is a unique ID for the issue.
	// Issue that have 'Id's can be explained using the `globstar desc` command.
	Id *string
}

func (*Issue) AsJson

func (i *Issue) AsJson() ([]byte, error)

func (*Issue) AsText

func (i *Issue) AsText() ([]byte, error)

type Language

type Language int
const (
	LangUnknown Language = iota
	LangPy
	LangJs  // vanilla JS and JSX
	LangTs  // TypeScript (not TSX)
	LangTsx // TypeScript with JSX extension
	LangJava
	LangRuby
	LangRust
	LangYaml
	LangCss
	LangDockerfile
	LangMarkdown
	LangSql
	LangKotlin
	LangOCaml
	LangLua
	LangBash
	LangCsharp
	LangElixir
	LangElm
	LangGo
	LangGroovy
	LangHcl
	LangHtml
	LangPhp
	LangScala
	LangSwift
)

func DecodeLanguage

func DecodeLanguage(language string) Language

DecodeLanguage converts a stringified language name to its corresponding Language enum

func LanguageFromFilePath

func LanguageFromFilePath(path string) Language

LanguageFromFilePath returns the Language of the file at the given path returns `LangUnkown` if the language is not recognized (e.g: `.txt` files).

func (Language) Grammar

func (lang Language) Grammar() *sitter.Language

tsGrammarForLang returns the tree-sitter grammar for the given language. May return `nil` when `lang` is `LangUnkown`.

type NodeFilter

type NodeFilter struct {
	// contains filtered or unexported fields
}

NodeFilter is a filter that can be applied to a PatternRule to restrict the the nodes that the rule is applied to. The rule is only applied to nodes that have a parent matching (or not matching) the query.

type ParseResult

type ParseResult struct {
	// Ast is the root node of the tree-sitter parse-tree
	// representing this file
	Ast *sitter.Node
	// Source is the raw source code of the file
	Source []byte
	// FilePath is the path to the file that was parsed
	FilePath string
	// Language is the tree-sitter language used to parse the file
	TsLanguage *sitter.Language
	// Language is the language of the file
	Language Language
	// ScopeTree represents the scope hierarchy of the file.
	// Can be nil if scope support for this language has not been implemented yet.
	ScopeTree *ScopeTree
}

ParseResult is the result of parsing a file.

func Parse

func Parse(filePath string, source []byte, language Language, grammar *sitter.Language) (*ParseResult, error)

func ParseFile

func ParseFile(filePath string) (*ParseResult, error)

ParseFile parses the file at the given path using the appropriate tree-sitter grammar.

type PathFilter

type PathFilter struct {
	ExcludeGlobs []glob.Glob
	IncludeGlobs []glob.Glob
}

PathFilter is a glob that can be applied to a PatternRule to restrict the files that the rule is applied to.

type PatternRuleFile

type PatternRuleFile struct {
	Language string          `yaml:"language"`
	Code     string          `yaml:"name"`
	Message  string          `yaml:"message"`
	Category config.Category `yaml:"category"`
	Severity config.Severity `yaml:"severity"`
	// Pattern is a single pattern in the form of:
	// pattern: (some_pattern)
	// in the YAML file
	Pattern string `yaml:"pattern,omitempty"`
	// Patterns are ultiple patterns in the form of:
	// pattern:  (something)
	// in the YAML file
	Patterns    []string     `yaml:"patterns,omitempty"`
	Description string       `yaml:"description,omitempty"`
	Filters     []filterYAML `yaml:"filters,omitempty"`
	Exclude     []string     `yaml:"exclude,omitempty"`
	Include     []string     `yaml:"include,omitempty"`
}

type Reference

type Reference struct {
	// IsWriteRef determines if this reference is a write reference.
	// For write refs, only the expression being assigned is stored.
	// i.e: for `a = 3`, this list will store the `3` node, not the assignment node
	IsWriteRef bool
	// Variable stores the variable being referenced
	Variable *Variable
	// Node stores the node that references the variable
	Node *sitter.Node
}

Reference represents a variable reference inside a source file Cross-file references like those in Golang and C++ (macros/extern) are NOT supported, so this shouldn't be used for rules like "unused-variable", but is safe to use for rules like "unused-import"

type Rule

type Rule interface {
	NodeType() string
	GetLanguage() Language
	OnEnter() *VisitFn
	OnLeave() *VisitFn
}

func CreateRule

func CreateRule(nodeType string, language Language, onEnter, onLeave *VisitFn) Rule

type Scope

type Scope struct {
	// AstNode is the AST node that introduces this scope into the scope tree
	AstNode *sitter.Node
	// Variables is a map of variable name to an object representing it
	Variables map[string]*Variable
	// Upper is the parent scope of this scope
	Upper *Scope
	// Children is a list of scopes that are children of this scope
	Children []*Scope
}

func NewScope

func NewScope(upper *Scope) *Scope

func (*Scope) Lookup

func (s *Scope) Lookup(name string) *Variable

Lookup searches for a variable in the current scope and its parents

type ScopeBuilder

type ScopeBuilder interface {
	GetLanguage() Language
	// NodeCreatesScope returns true if the node introduces a new scope
	// into the scope tree
	NodeCreatesScope(node *sitter.Node) bool
	// DeclaresVariable determines if we can extract new variables out of this AST node
	DeclaresVariable(node *sitter.Node) bool
	// CollectVariables extracts variables from the node and adds them to the scope
	CollectVariables(node *sitter.Node) []*Variable
	// OnNodeEnter is called when the scope builder enters a node
	// for the first time, and hasn't scanned its children decls just yet
	// can be used to handle language specific scoping rules, if any
	// If `node` is smth like a block statement, `currentScope` corresponds
	// to the scope introduced by the block statement.
	OnNodeEnter(node *sitter.Node, currentScope *Scope)
	// OnNodeExit is called when the scope builder exits a node
	// can be used to handle language specific scoping rules, if any
	// If `node` is smth like a block statement, `currentScope` corresponds
	// to the scope introduced by the block statement.
	OnNodeExit(node *sitter.Node, currentScope *Scope)
}

ScopeBuilder is an interface that has to be implemented once for every supported language. Languages that don't implement a `ScopeBuilder` can still have lints, just not any that require scope resolution.

type ScopeTree

type ScopeTree struct {
	Language Language
	// ScopeOfNode maps every scope-having node to its corresponding scope.
	// E.g: a block statement is mapped to the scope it introduces.
	ScopeOfNode map[*sitter.Node]*Scope
	// Root is the top-level scope in the program,
	// usually associated with the `program` or `module` node
	Root *Scope
}

func BuildScopeTree

func BuildScopeTree(builder ScopeBuilder, ast *sitter.Node, source []byte) *ScopeTree

BuildScopeTree constructs a scope tree from the AST for a program

func MakeScopeTree

func MakeScopeTree(lang Language, ast *sitter.Node, source []byte) *ScopeTree

func (*ScopeTree) GetScope

func (st *ScopeTree) GetScope(node *sitter.Node) *Scope

GetScope finds the nearest surrounding scope of an AST node

type TsScopeBuilder

type TsScopeBuilder struct {
	// contains filtered or unexported fields
}

func (*TsScopeBuilder) CollectVariables

func (ts *TsScopeBuilder) CollectVariables(node *sitter.Node) []*Variable

func (*TsScopeBuilder) DeclaresVariable

func (ts *TsScopeBuilder) DeclaresVariable(node *sitter.Node) bool

func (*TsScopeBuilder) GetLanguage

func (j *TsScopeBuilder) GetLanguage() Language

func (*TsScopeBuilder) NodeCreatesScope

func (ts *TsScopeBuilder) NodeCreatesScope(node *sitter.Node) bool

func (*TsScopeBuilder) OnNodeEnter

func (ts *TsScopeBuilder) OnNodeEnter(node *sitter.Node, scope *Scope)

func (*TsScopeBuilder) OnNodeExit

func (ts *TsScopeBuilder) OnNodeExit(node *sitter.Node, scope *Scope)

type UnresolvedRef

type UnresolvedRef struct {
	// contains filtered or unexported fields
}

type VarKind

type VarKind int32
const (
	VarKindError VarKind = iota
	VarKindImport
	VarKindFunction
	VarKindVariable
	VarKindParameter
)

type Variable

type Variable struct {
	Kind VarKind
	// Stores the name of the variable
	Name string
	// DeclNode is the AST node that declares this variable
	DeclNode *sitter.Node
	// Refs is a list of references to this variable throughout the file
	Refs []*Reference
}

type VisitFn

type VisitFn func(rule Rule, analyzer *Analyzer, node *sitter.Node)

type Walker

type Walker interface {
	// OnEnterNode is called when the walker enters a node.
	// The boolean return value indicates whether the walker should
	// continue walking the sub-tree of this node.
	OnEnterNode(node *sitter.Node) bool
	// OnLeaveNode is called when the walker leaves a node.
	// This is called after all the children of the node have been visited and explored.
	OnLeaveNode(node *sitter.Node)
}

Walker is an interface that dictates what to do when entering and leaving each node during the pre-order traversal of a tree. To traverse post-order, use the `OnLeaveNode` callback.

type YmlRule

type YmlRule interface {
	Name() string
	Patterns() []*sitter.Query
	Language() Language
	Category() config.Category
	Severity() config.Severity
	OnMatch(
		ana *Analyzer,
		matchedQuery *sitter.Query,
		matchedNode *sitter.Node,
		captures []sitter.QueryCapture,
	)
	PathFilter() *PathFilter
	NodeFilters() []NodeFilter
}

A YmlRule is a rule that matches a tree-sitter query pattern and reports an issue when the pattern is found. Unlike regular issues, PatternRules are not associated with a specific node type, rather they are invoked for *every* node that matches the pattern.

func CreatePatternRule

func CreatePatternRule(
	patterns []*sitter.Query,
	language Language,
	issueMessage string,
	issueId string,
	pathFilter *PathFilter,
) YmlRule

func ReadFromBytes

func ReadFromBytes(fileContent []byte) (YmlRule, error)

ReadFromBytes reads a pattern rule definition from bytes array

func ReadFromFile

func ReadFromFile(filePath string) (YmlRule, error)

ReadFromFile reads a pattern rule definition from a YAML config file.

Jump to

Keyboard shortcuts

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