analysis

package
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: Apr 14, 2025 License: MIT Imports: 35 Imported by: 0

Documentation

Overview

scope resolution implementation for JS and TS files

Index

Constants

This section is empty.

Variables

View Source
var ErrUnsupportedLanguage = fmt.Errorf("unsupported language")

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

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

Functions

func ChildWithFieldName added in v0.4.0

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

func ChildrenOfType added in v0.4.0

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

func ChildrenWithFieldName added in v0.4.0

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 ContainsSkipcq added in v0.7.0

func ContainsSkipcq(skipLines []*SkipComment, issue *Issue) bool

func FindMatchingChild added in v0.4.0

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 added in v0.4.0

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

func GetEscapedCommentIdentifierFromPath added in v0.3.0

func GetEscapedCommentIdentifierFromPath(path string) string

func GetExtFromLanguage added in v0.4.0

func GetExtFromLanguage(lang Language) string

func IssueAsTextFromJson added in v0.3.0

func IssueAsTextFromJson(jsonData []byte) ([]byte, error)

func Preorder

func Preorder(pass *Pass, fn func(*sitter.Node))

func ReportIssues

func ReportIssues(issues []*Issue, format string) ([]byte, error)

func RunAnalyzerTests added in v0.3.0

func RunAnalyzerTests(testDir string, analyzers []*Analyzer) (string, string, bool, error)

func WalkTree added in v0.4.0

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

Types

type Analyzer

type Analyzer struct {
	Name        string
	Description string
	Category    Category
	Severity    Severity
	Language    Language
	Requires    []*Analyzer
	Run         func(*Pass) (any, error)
	ResultType  reflect.Type
}

type Category

type Category string
const (
	CategoryStyle       Category = "style"
	CategoryBugRisk     Category = "bug-risk"
	CategoryAntipattern Category = "antipattern"
	CategoryPerformance Category = "performance"
	CategorySecurity    Category = "security"
)

func (Category) IsValid

func (c Category) IsValid() bool

type Issue

type Issue struct {
	// The category of the issue
	Category Category
	// The severity of the issue
	Severity Severity
	// The message to display to the user
	Message string
	// The file path of the file that the issue was found in
	Filepath string
	// (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 IssueFromJson added in v0.3.0

func IssueFromJson(jsonData []byte) (*Issue, error)

func RunAnalyzers

func RunAnalyzers(path string, analyzers []*Analyzer, fileFilter func(string) bool) ([]*Issue, error)

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 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 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)

type Pass

type Pass struct {
	Analyzer    *Analyzer
	FileContext *ParseResult
	Files       []*ParseResult
	ResultOf    map[*Analyzer]any
	Report      func(*Pass, *sitter.Node, string)
	// TODO (opt): the cache should ideally not be stored in-memory
	ResultCache map[*Analyzer]map[*ParseResult]any
}

type Reference added in v0.4.0

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 checkers like "unused-variable", but is safe to use for checkers like "unused-import"

type Scope added in v0.4.0

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 added in v0.4.0

func NewScope(upper *Scope) *Scope

func (*Scope) Lookup added in v0.4.0

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

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

type ScopeBuilder added in v0.4.0

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 checkers, just not any that require scope resolution.

type ScopeTree added in v0.4.0

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 added in v0.4.0

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

BuildScopeTree constructs a scope tree from the AST for a program

func MakeScopeTree added in v0.4.0

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

func (*ScopeTree) GetScope added in v0.4.0

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

GetScope finds the nearest surrounding scope of an AST node

type Severity

type Severity string
const (
	SeverityCritical Severity = "critical"
	SeverityError    Severity = "error"
	SeverityWarning  Severity = "warning"
	SeverityInfo     Severity = "info"
)

func (Severity) IsValid

func (s Severity) IsValid() bool

type SkipComment added in v0.7.0

type SkipComment struct {
	// the line number for the skipcq comment
	CommentLine int
	// the entire text of the skipcq comment
	CommentText string
	// (optional) name of the checker for targetted skip
	CheckerIds []string
}

for caching the skipcq comments

func GatherSkipInfo added in v0.7.0

func GatherSkipInfo(fileContext *ParseResult) []*SkipComment

cache all the skipcq comments from an ast

type TsScopeBuilder added in v0.4.0

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

func (*TsScopeBuilder) CollectVariables added in v0.4.0

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

func (*TsScopeBuilder) DeclaresVariable added in v0.4.0

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

func (*TsScopeBuilder) GetLanguage added in v0.4.0

func (j *TsScopeBuilder) GetLanguage() Language

func (*TsScopeBuilder) NodeCreatesScope added in v0.4.0

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

func (*TsScopeBuilder) OnNodeEnter added in v0.4.0

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

func (*TsScopeBuilder) OnNodeExit added in v0.4.0

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

type UnresolvedRef added in v0.4.0

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

type VarKind added in v0.4.0

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

type Variable added in v0.4.0

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 Walker added in v0.4.0

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.

Jump to

Keyboard shortcuts

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