thriftcheck

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Sep 19, 2023 License: Apache-2.0 Imports: 13 Imported by: 2

README

ThriftCheck

ThriftCheck is a linter for Thrift IDL files. It provides a general Thrift linting framework, a set of checks, and a command line tool (thriftcheck).

thriftcheck

thriftcheck is a configuration-driven tool for linting Thrift IDL files from the command line:

usage: thriftcheck [options] [path ...]
  -I, --include value
    	include path (can be specified multiple times)
  -c, --config string
    	configuration file path (default ".thriftcheck.toml")
  --errors-only
    	only report errors (not warnings)
  -h, --help
    	show command help
  -l, --list
    	list all available checks with their status and exit
  --stdin-filename string
    	filename used when piping from stdin (default "stdin")
  -v, --verbose
    	enable verbose (debugging) output
  --version
    	print the version and exit

You can pass a list of filenames or directory paths. Directories will be expanded recursively to include all nested .thrift files.

You also can lint from standard input by passing - as the sole filename. Use --stdin-name to customize the filename used in output messages.

$ thriftlint --stdin-name filename.thrift - < filename.thrift

Messages are reported to standard output using a familiar parseable format:

file.thrift:1:1: error: "py" namespace must match "^idl\\." (namespace.pattern)
file.thrift:3:1: error: unable to find include path for "bar.thrift" (include.path)

If you only want errors (and not warnings) to be reported, you can use the --errors-only command line option.

thriftcheck's exit code indicates whether it reported any warnings (1) or errors (2). Otherwise, exit code 0 is returned.

Configuration

Many checks are configurable via the configuration file. This file is named .thriftcheck.toml and is loaded from the current directory by default, but you can use the --config command line option to use a different file. If you prefer, you can use a JSON- or YAML-formatted file instead by using a .json or .yaml file extension, respectively. The examples shown below use the default TOML syntax.

example.toml is an example configuration file that you can use as a starting point.

Checks

The full list of available checks can printed using the --list command line option. By default, all checks are enabled.

You can enable or disable checks using the configuration file's top-level enabled and disabled lists. The list of disabled checks is subtracted from the full list first, and then the resulting list is filtered by the list of enabled checks. Either list can be empty (the default).

constant.ref

This check reports an error if a referenced constant or enum value cannot be found in either the current scope or in an included file (using dot notation).

enum.size

This check warns or errors if an enumeration's element size grows beyond a limit.

[enum.size]
warning = 500
error = 1000
field.doc.missing

This check warns if a field is missing a documentation comment.

field.id.missing

This check reports an error if a field's ID is missing (using the legacy implicit/auto-assigning syntax).

field.id.negative

This check reports an error if a field's ID is explicitly negative.

field.id.zero

This check reports an error if a field's ID is explicitly zero, which is generally unsupported by the Apache Thrift compiler. This is distinct from the field.id.negative check given the existence of the --allow-neg-keys Apache Thrift compiler option.

field.optional

This check warns if a field isn't declared as "optional", which is considered a best practice.

field.requiredness

This check warns if a field isn't explicitly declared as "required" or "optional".

include.path

This check ensures that each include'd file can be located in the set of given include paths.

Relative paths are resolved relative to the current working directory. The list of includes specified in the configuration file is used by default, but if any paths are specified on the command line using the -I option, they will be used instead.

includes = [
    'shared',
]
include.restricted

This check restricts some files from being imported by other files using a map of patterns: the key is a file name pattern that matches the including filename and the value is a regular expression that matches the included filename. When both match, the include is flagged as "restricted" and an error is reported.

[checks.include]
[[checks.include.restricted]]
"*" = "(huge|massive).thrift"
int.64bit

This check warns when an integer constant exceeds the 32-bit number range. Some languages (e.g. JavaScript) don't support 64-bit integers.

map.key.type

This check ensures that only primitive types are used for map<> keys.

names.reserved

This checks allows you to extend the default list of reserved keywords with additional disallowed names.

[checks.names]
reserved = [
    "template",
]
namespace.patterns

This check ensures that a namespace's name matches a regular expression pattern. The pattern can be configured one a per-language basis.

[[namespace.patterns]]
py = "^idl\\."
set.value.type

This check ensures that only primitive types are used for set<> values.

Custom Checks

You can also implement your own checks using the thriftcheck package's public interfaces. Checks are functions which receive a *thriftheck.C followed by a variable number of ast.Node-compliant argument types.

check := thriftcheck.NewCheck("enum.name", func(c *thriftcheck.C, e *ast.Enum, ei *ast.EnumItem) {
	for _, r := range ei.Name {
		if !unicode.IsUpper(r) && unicode.IsLetter(r) {
			c.Errorf(f, "item name %q (in %q) can only contain uppercase letters", ei.Name, e.Name)
			return
		}
	}
})

You can pass any list of checks to thriftcheck.NewLinter. You will probably want to build a custom version of the thriftcheck tool that is aware of your additional checks.

nolint Directives

You can disable one or more checks on a per-node basis using nolint directives. nolint directives apply to the current node and all of its descendents. The directives's value can be empty, in which case linting is entirely disabled, or it can be set to a comma-separated list of checks to disable.

nolint directives can be written as Thrift annotations:

enum State {
	STOPPED = 1
	RUNNING = 2
	PASSED = 3
	FAILED = 4
} (nolint = "enum.size")

... and as @nolint lines in documentation blocks:

/**
 * States
 *
 * @nolint(enum.size)
 */
enum State {
	STOPPED = 1
	RUNNING = 2
	PASSED = 3
	FAILED = 4
}

The annotation syntax is preferred, but the documentation block syntax is useful for those few cases where the target node doesn't support Thrift annotations (such as const declarations).

Editor Support

  • Vim, using ALE

pre-commit

pre-commit support is provided. Simply add the following to your .pre-commit-config.yaml configuration file:

- repo: https://github.com/pinterest/thriftcheck
  rev: 1.0.0 # git revision or tag
  hooks:
    - id: thriftcheck
      name: thriftcheck

License

This software is released under the terms of the Apache 2.0 License.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Doc

func Doc(node ast.Node) string

Doc returns an ast.Node's Doc string.

func Parse

func Parse(r io.Reader) (*ast.Program, *idl.Info, error)

Parse parses Thrift document content.

func ParseFile

func ParseFile(filename string, dirs []string) (*ast.Program, *idl.Info, error)

ParseFile parses a Thrift file. The filename must appear in one of the given directories.

func Resolve

func Resolve(name string, program *ast.Program, dirs []string) (ast.Node, error)

Resolve resolves a named reference to its target node.

The target can either be in the current program's scope or it can refer to an included file using dot notation. Included files must exist in one of the given search directories.

func ResolveConstant

func ResolveConstant(ref ast.ConstantReference, program *ast.Program, dirs []string) (ast.Node, error)

ResolveConstant resolves an ast.ConstantReference to its target node.

The following name formats are supported:

  • "Constant" (ast.Constant)
  • "Enum.Value" (ast.EnumItem)
  • "include.Constant" (ast.Constant)
  • "include.Enum.Value" (ast.EnumItem)

func ResolveType

func ResolveType(ref ast.TypeReference, program *ast.Program, dirs []string) (ast.Node, error)

ResolveType calls Resolve and goes one step further by attempting to resolve the target node's own type. This is useful when the reference points to an ast.Typedef or ast.Constant, for example, and the caller is primarily intererested in the target's ast.Type.

Types

type C

type C struct {
	Filename string
	Dirs     []string
	Program  *ast.Program
	Check    string
	Messages Messages
	// contains filtered or unexported fields
}

C is a type passed to all check functions to provide context.

func (*C) Errorf

func (c *C) Errorf(node ast.Node, message string, args ...interface{})

Errorf records a new message for the given node with Error severity.

func (*C) Logf

func (c *C) Logf(message string, args ...interface{})

Logf prints a formatted message to the verbose output logger.

func (*C) Resolve

func (c *C) Resolve(name string) ast.Node

Resolve resolves a name.

func (*C) ResolveConstant

func (c *C) ResolveConstant(ref ast.ConstantReference) ast.Node

ResolveConstant resolves a constant reference to its target.

func (*C) ResolveType

func (c *C) ResolveType(ref ast.TypeReference) ast.Node

ResolveType resolves a type reference to its target type.

func (*C) Warningf

func (c *C) Warningf(node ast.Node, message string, args ...interface{})

Warningf records a new message for the given node with Warning severity.

type Check

type Check struct {
	Name string
	// contains filtered or unexported fields
}

Check is a named check function.

func NewCheck

func NewCheck(name string, fn interface{}) *Check

NewCheck creates a new Check.

func (*Check) Call

func (c *Check) Call(ctx *C, nodes ...ast.Node) bool

Call the check function if its arguments end with the current node in the hierarchy and all other variable arguments are its strictly ordered parents.

The first argument is always a *C instance.

The nodes are ordered from the current node through it ancestors.

hierarchy = {*ast.EnumItem, *ast.Enum, *ast.Program}

The following functions would match:

f(*C, *ast.Program, *ast.Enum, *ast.EnumItem)
f(*C, *ast.Enum, *ast.EnumItem)
f(*C, *ast.EnumItem)

But these would not:

f(*C, *ast.Program)
f(*C, *ast.Enum)
f(*C, *ast.EnumItem, *ast.Enum)
f(*C, *ast.Program, *ast.EnumItem)

Function arguments can also use the generic ast.Node interface type:

f(*C, ast.Node)
f(*C, *ast.Program, ast.Node)
f(*C, parent, node ast.Node)

type Checks

type Checks []*Check

Checks is a list of checks.

func (Checks) SortedNames

func (c Checks) SortedNames() []string

SortedNames returns a sorted list of the checks' names.

func (Checks) String

func (c Checks) String() string

func (Checks) With

func (c Checks) With(prefixes []string) Checks

With returns a copy with only those checks whose names match the given prefixes.

func (Checks) Without

func (c Checks) Without(prefixes []string) Checks

Without returns a copy without those checks whose names match the given prefixes.

type Linter

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

Linter is a configured Thrift linter.

func NewLinter

func NewLinter(checks Checks, options ...Option) *Linter

NewLinter creates a new Linter configured with the given checks and options.

func (*Linter) Lint

func (l *Linter) Lint(r io.Reader, filename string) (Messages, error)

Lint lints a single input file.

func (*Linter) LintFiles

func (l *Linter) LintFiles(filenames []string) (Messages, error)

LintFiles lints multiple files. Each is opened, parsed, and linted in order, and the aggregate result is returned.

type Message

type Message struct {
	Filename string
	Pos      ast.Position
	Node     ast.Node
	Check    string
	Severity Severity
	Message  string
}

Message is a message produced by a Check.

func (Message) String

func (m Message) String() string

type Messages

type Messages []Message

Messages is a list of messages.

type Option

type Option func(*Linter)

Option represents a Linter option.

func WithIncludes

func WithIncludes(includes []string) Option

WithIncludes is an Option that adds Thrift include paths to the linter.

func WithLogger

func WithLogger(logger *log.Logger) Option

WithLogger is an Option that sets the log.Logger object used by the linter.

type Severity

type Severity int

Severity represents the severity level of a message.

const (
	// Warning indicates a warning.
	Warning Severity = iota
	// Error indicates an error.
	Error
)

func (Severity) String

func (s Severity) String() string

type VisitorFunc

type VisitorFunc func(ast.Walker, ast.Node) VisitorFunc

VisitorFunc adapts a function to the ast.Visitor interface. This differs from ast.VisitorFunc in that is supports an ast.Visitor-compativle return value.

func (VisitorFunc) Visit

func (f VisitorFunc) Visit(w ast.Walker, n ast.Node) ast.Visitor

Visit the given node and its descendants.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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