diff --git a/.tsk.toml b/.tsk.toml new file mode 100644 index 0000000..aaac8f2 --- /dev/null +++ b/.tsk.toml @@ -0,0 +1,2 @@ +[encapsulation] +allow-generated-construction = true diff --git a/AGENTS.md b/AGENTS.md index e6f00e3..0023c48 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -53,6 +53,9 @@ do not convey their meaning. Do not use naked returns. - Keep every comment to at most two lines. - Document every public symbol. +- `bit lint-tsk` runs the `tsk` linters; `tsk config` lists them. Keep an + intentional finding with a `//nolint: ` comment at the end of + its line or on its own line above the statement. # Optional values @@ -66,8 +69,6 @@ - Keep nil checks on required arguments; those values are not optional. - Read values with `Get()` or `Default()` and handle the absent case. Never call `MustGet()` outside tests; the linter rejects it. -- `bit lint-optional` reports nil used as an absent value. Keep an intentional - nil with an `//optionalnil:allow ` comment on or above its line. - Tag optional JSON fields with `omitzero`, not `omitempty`. - `Option` decodes JSON with `json.Unmarshal`, ignoring decoder settings such as `DisallowUnknownFields`. A type held in an `Option` must enforce strict diff --git a/BUILD.bit b/BUILD.bit index 77555c3..ce98eb3 100644 --- a/BUILD.bit +++ b/BUILD.bit @@ -69,18 +69,6 @@ lint-go[go-module] = go.lint { dir = go-module depends_on = [sample-proto] } -lint-encapsulation[go-module] = exec.test { - command = "encapsulation-linter -allow-generated-construction ./..." - dir = go-module - inputs = ["#{go-module}/**/*.go", "#{go-module}/go.mod", "#{go-module}/go.sum", "bin/.encapsulation-linter-0.2.0.pkg"] - depends_on = [build-go] -} -lint-optional[go-module] = exec.test { - command = "optionalnil ./..." - dir = go-module - inputs = ["#{go-module}/**/*.go", "#{go-module}/go.mod", "#{go-module}/go.sum", "go.mod", "go.sum", "cmd/optionalnil/*.go", "scripts/optionalnil", "scripts/spectre-ingress"] - depends_on = [build-go] -} lint-proto = exec.test { command = "buf lint" inputs = ["buf.yaml", "internal/sample/proto/*.proto", "bin/.buf-1.73.0.pkg"] @@ -105,6 +93,12 @@ lint-scripts = exec { command = "shellcheck scripts/*" inputs = ["scripts/*", "bin/.shellcheck-0.11.0.pkg"] } +lint-tsk[go-module] = exec.test { + command = "tsk" + dir = go-module + inputs = ["#{go-module}/**/*.go", "#{go-module}/go.mod", "#{go-module}/go.sum", ".tsk.toml", "bin/.tsktsk-0.2.0.pkg"] + depends_on = [build-go] +} explicit sample-bin = go.exe { package = "./cmd/spectre-sample" @@ -149,7 +143,7 @@ target publish-container(registry : string, image-tag : string, version : string target test = [test-go, test-comment-length, test-launcher, test-response-comparison-integration, test-release-version, test-container-publisher] # Lint everything -target lint = [lint-go, lint-encapsulation, lint-optional, lint-proto, lint-actions, lint-action-pins, lint-dockerfile, lint-release, lint-scripts, fmt-check-go] +target lint = [lint-go, lint-tsk, lint-proto, lint-actions, lint-action-pins, lint-dockerfile, lint-release, lint-scripts, fmt-check-go] # Format everything target fmt = [fmt-go, fmt-action-pins] diff --git a/bin/.docker-cli@29.4.pkg b/bin/.docker-cli-29.8.2.pkg similarity index 100% rename from bin/.docker-cli@29.4.pkg rename to bin/.docker-cli-29.8.2.pkg diff --git a/bin/.encapsulation-linter-0.2.0.pkg b/bin/.tsktsk-0.2.0.pkg similarity index 100% rename from bin/.encapsulation-linter-0.2.0.pkg rename to bin/.tsktsk-0.2.0.pkg diff --git a/bin/docker b/bin/docker index fa67897..5e82634 120000 --- a/bin/docker +++ b/bin/docker @@ -1 +1 @@ -.docker-cli@29.4.pkg \ No newline at end of file +.docker-cli-29.8.2.pkg \ No newline at end of file diff --git a/bin/encapsulation-linter b/bin/encapsulation-linter deleted file mode 120000 index b421c33..0000000 --- a/bin/encapsulation-linter +++ /dev/null @@ -1 +0,0 @@ -.encapsulation-linter-0.2.0.pkg \ No newline at end of file diff --git a/bin/tsk b/bin/tsk new file mode 120000 index 0000000..bbd39cd --- /dev/null +++ b/bin/tsk @@ -0,0 +1 @@ +.tsktsk-0.2.0.pkg \ No newline at end of file diff --git a/cmd/optionalnil/main.go b/cmd/optionalnil/main.go deleted file mode 100644 index c72b394..0000000 --- a/cmd/optionalnil/main.go +++ /dev/null @@ -1,8 +0,0 @@ -// Command optionalnil reports nil used as an absent value instead of Option. -package main - -import "golang.org/x/tools/go/analysis/singlechecker" - -func main() { - singlechecker.Main(newOptionalNil()) -} diff --git a/cmd/optionalnil/optionalnil.go b/cmd/optionalnil/optionalnil.go deleted file mode 100644 index 03f9a58..0000000 --- a/cmd/optionalnil/optionalnil.go +++ /dev/null @@ -1,460 +0,0 @@ -package main - -import ( - "go/ast" - "go/constant" - "go/token" - "go/types" - "strings" - - . "github.com/alecthomas/types/optional" - "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/analysis/passes/inspect" - "golang.org/x/tools/go/ast/edge" - "golang.org/x/tools/go/ast/inspector" - "golang.org/x/tools/go/types/typeutil" -) - -// allowDirective suppresses a report on its own line or the line below. -const allowDirective = "//optionalnil:allow" - -// newOptionalNil returns an analyzer that reports nil used as an absent value in -// code belonging to the analysed package's module. -func newOptionalNil() *analysis.Analyzer { - return &analysis.Analyzer{ - Name: "optionalnil", - Doc: "report nil used as an absent value instead of Option\n\n" + - "Use " + allowDirective + " to keep an intentional nil.", - Requires: []*analysis.Analyzer{inspect.Analyzer}, - FactTypes: []analysis.Fact{new(generatedPackage)}, - Run: run, - } -} - -// generatedPackage marks a package whose files are all generated. Its API follows -// the generator's conventions, so nil uses of it are not reported. -type generatedPackage struct{} - -func (*generatedPackage) AFact() {} - -func (*generatedPackage) String() string { return "generatedPackage" } - -func run(pass *analysis.Pass) (any, error) { - analyse(pass) - return nil, nil //nolint:nilnil // The analyzer only reports diagnostics. -} - -func analyse(pass *analysis.Pass) { - if pass.Module == nil || !inModule(pass.Pkg.Path(), pass.Module.Path) { - return // Neither dependencies nor test mains are first-party. - } - if allGenerated(pass.Files) { - pass.ExportPackageFact(&generatedPackage{}) - return - } - root := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Root() - c := newChecker(pass, root) - for cur := range root.Preorder((*ast.Ident)(nil)) { - if pass.TypesInfo.Types[cur.Node().(*ast.Ident)].IsNil() { - c.checkNil(cur) - } - } -} - -type checker struct { - pass *analysis.Pass - module string - errorType *types.Interface - allowed map[string]map[int]bool - tracked map[types.Object]bool -} - -func newChecker(pass *analysis.Pass, root inspector.Cursor) *checker { - c := &checker{ - pass: pass, - module: pass.Module.Path, - errorType: types.Universe.Lookup("error").Type().Underlying().(*types.Interface), - allowed: allowedLines(pass), - tracked: map[types.Object]bool{}, - } - c.trackLocals(root) - return c -} - -// checkNil reports a nil literal whose destination is a first-party slot for a -// value that may be absent. -func (c *checker) checkNil(cur inspector.Cursor) { - for c.keepsNil(cur.Parent().Node()) { - cur = cur.Parent() - } - kind, index := cur.ParentEdge() - parent := cur.Parent() - switch kind { //nolint:exhaustive // Only edges that give the nil a destination matter. - case edge.ReturnStmt_Results: - c.checkReturn(parent, index) - case edge.AssignStmt_Rhs: - assign := parent.Node().(*ast.AssignStmt) - if len(assign.Lhs) != len(assign.Rhs) { - break - } - if element, ok := assign.Lhs[index].(*ast.IndexExpr); ok { - c.checkMapValue(cur, c.pass.TypesInfo.TypeOf(element.X)) - } else { - c.checkStore(cur, c.referencedVar(assign.Lhs[index])) - } - case edge.ValueSpec_Values: - spec := parent.Node().(*ast.ValueSpec) - if len(spec.Names) == len(spec.Values) { - c.checkStore(cur, c.referencedVar(spec.Names[index])) - } - case edge.KeyValueExpr_Value: - if parent.ParentEdgeKind() != edge.CompositeLit_Elts { - break - } - literalType := c.pass.TypesInfo.TypeOf(parent.Parent().Node().(*ast.CompositeLit)) - switch literalType.Underlying().(type) { - case *types.Struct: - c.checkStore(cur, c.referencedVar(parent.Node().(*ast.KeyValueExpr).Key)) - case *types.Map: - c.checkMapValue(cur, literalType) - } - case edge.CompositeLit_Elts: - if structType, ok := c.pass.TypesInfo.TypeOf(parent.Node().(*ast.CompositeLit)).Underlying().(*types.Struct); ok { - c.checkStore(cur, Some(structType.Field(index))) - } - case edge.CallExpr_Args: - c.checkArgument(cur, parent.Node().(*ast.CallExpr), index) - case edge.BinaryExpr_X, edge.BinaryExpr_Y: - c.checkComparison(parent, kind) - } -} - -func (c *checker) checkReturn(ret inspector.Cursor, index int) { - stmt := ret.Node().(*ast.ReturnStmt) - signature, ok := c.enclosingSignature(ret) - if !ok || signature.Results().Len() != len(stmt.Results) || c.isPlaceholder(stmt, index) { - return - } - if c.mayBeAbsent(signature.Results().At(index).Type(), false) { - c.report(stmt.Results[index], "nil returned") - } -} - -// isPlaceholder reports whether another result carries the outcome, such as an -// error or a false ok flag, so the nil only fills its slot. -func (c *checker) isPlaceholder(stmt *ast.ReturnStmt, index int) bool { - for i, result := range stmt.Results { - value := c.pass.TypesInfo.Types[result] - if i == index || c.isNil(result) { - continue - } - if value.Value == nil || (value.Value.Kind() == constant.Bool && !constant.BoolVal(value.Value)) { - return true - } - } - return false -} - -// keepsNil reports whether a node passes a nil operand through unchanged, as -// parentheses and conversions such as (*T)(nil) do. -func (c *checker) keepsNil(node ast.Node) bool { - switch node := node.(type) { - case *ast.ParenExpr: - return true - case *ast.CallExpr: - return len(node.Args) == 1 && c.pass.TypesInfo.Types[node.Fun].IsType() - } - return false -} - -func (c *checker) isNil(expr ast.Expr) bool { - for { - expr = ast.Unparen(expr) - call, ok := expr.(*ast.CallExpr) - if !ok || !c.keepsNil(call) { - return c.pass.TypesInfo.Types[expr].IsNil() - } - expr = call.Args[0] - } -} - -// checkMapValue reports nil stored as a map value, where a missing key already -// means absent. Tests skip it, as their tables pass nil inputs deliberately. -func (c *checker) checkMapValue(cur inspector.Cursor, mapType types.Type) { - underlying, isMap := mapType.Underlying().(*types.Map) - if !isMap || c.inTestFile(cur.Node()) { - return - } - if named, ok := types.Unalias(mapType).(*types.Named); ok && !c.isFirstParty(named.Obj()) { - return // A map type declared elsewhere keeps its package's conventions. - } - if c.mayBeAbsent(underlying.Elem(), false) { - c.report(cur.Node(), "nil stored in map") - } -} - -func (c *checker) checkStore(cur inspector.Cursor, target Option[*types.Var]) { - variable, ok := target.Get() - if ok && variable.Name() != "_" && c.isFirstParty(variable) && c.mayBeAbsent(variable.Type(), false) { - c.report(cur.Node(), "nil stored in "+variable.Name()) - } -} - -func (c *checker) checkArgument(cur inspector.Cursor, call *ast.CallExpr, index int) { - callee := typeutil.Callee(c.pass.TypesInfo, call) - signature, ok := c.pass.TypesInfo.TypeOf(call.Fun).Underlying().(*types.Signature) - if callee == nil || !ok || !c.isFirstParty(callee) || c.inTestFile(call) { - return - } - params := signature.Params() - var paramType types.Type - switch { - case !signature.Variadic() || index < params.Len()-1: - paramType = params.At(index).Type() - case call.Ellipsis == token.NoPos: - paramType = params.At(params.Len() - 1).Type().(*types.Slice).Elem() - default: - return - } - if c.mayBeAbsent(paramType, false) { - c.report(cur.Node(), "nil passed to "+callee.Name()) - } -} - -// checkComparison reports a nil comparison that tests whether a first-party value -// is present. Slices and maps count since nil there separates absent from empty. -func (c *checker) checkComparison(cur inspector.Cursor, nilSide edge.Kind) { - comparison := cur.Node().(*ast.BinaryExpr) - if comparison.Op != token.EQL && comparison.Op != token.NEQ { - return - } - operand := comparison.X - if nilSide == edge.BinaryExpr_X { - operand = comparison.Y - } - object, ok := c.referencedObject(operand) - if !ok || !c.isFirstParty(object) || !c.mayBeAbsent(c.pass.TypesInfo.TypeOf(operand), true) { - return - } - if isLocal(object) && !c.tracked[object] { - return // Its nil came from code outside the module. - } - if comparison.Op == token.EQL && c.isRequirementCheck(cur) { - return - } - c.report(comparison, "nil compared with "+object.Name()) -} - -// isRequirementCheck reports whether a nil comparison fails when the value is -// missing, which treats the value as required rather than optional. -func (c *checker) isRequirementCheck(cur inspector.Cursor) bool { - for cur.ParentEdgeKind() == edge.ParenExpr_X || - (cur.ParentEdgeKind() == edge.BinaryExpr_X || cur.ParentEdgeKind() == edge.BinaryExpr_Y) && - cur.Parent().Node().(*ast.BinaryExpr).Op == token.LOR { - cur = cur.Parent() - } - if cur.ParentEdgeKind() != edge.IfStmt_Cond { - return false - } - body := cur.Parent().Node().(*ast.IfStmt).Body.List - return len(body) > 0 && c.fails(body[len(body)-1]) -} - -// fails reports whether a statement panics or returns a non-nil error. -func (c *checker) fails(stmt ast.Stmt) bool { - switch stmt := stmt.(type) { - case *ast.ExprStmt: - call, ok := stmt.X.(*ast.CallExpr) - if !ok { - return false - } - builtin, ok := typeutil.Callee(c.pass.TypesInfo, call).(*types.Builtin) - return ok && builtin.Name() == "panic" - case *ast.ReturnStmt: - for _, result := range stmt.Results { - value := c.pass.TypesInfo.Types[result] - if types.Implements(value.Type, c.errorType) && !value.IsNil() { - return true - } - } - } - return false -} - -// mayBeAbsent reports whether nil can mean absent for a type. It cannot for errors, -// self-referential pointers ending a linked structure, or any holding JSON null. -func (c *checker) mayBeAbsent(valueType types.Type, includeCollections bool) bool { - if _, ok := types.Unalias(valueType).(*types.TypeParam); ok || types.Implements(valueType, c.errorType) { - return false - } - switch underlying := valueType.Underlying().(type) { - case *types.Pointer: - return !isSelfReferential(valueType, underlying) - case *types.Interface: - return !underlying.Empty() - case *types.Signature, *types.Chan: - return true - case *types.Slice, *types.Map: - return includeCollections - } - return false -} - -func isSelfReferential(pointer types.Type, underlying *types.Pointer) bool { - structType, ok := underlying.Elem().Underlying().(*types.Struct) - if !ok { - return false - } - for field := range structType.Fields() { - if types.Identical(field.Type(), pointer) { - return true - } - } - return false -} - -// isFirstParty reports whether an object belongs to the analysed module and was -// written by hand. -func (c *checker) isFirstParty(object types.Object) bool { - pkg := object.Pkg() - if pkg == nil || !inModule(pkg.Path(), c.module) { - return false - } - return !c.pass.ImportPackageFact(pkg, new(generatedPackage)) -} - -func (c *checker) referencedVar(expr ast.Expr) Option[*types.Var] { - object, ok := c.referencedObject(expr) - if !ok { - return None[*types.Var]() - } - variable, ok := object.(*types.Var) - if !ok { - return None[*types.Var]() - } - return Some(variable) -} - -// referencedObject returns the variable, field, or called function an expression -// names. -func (c *checker) referencedObject(expr ast.Expr) (object types.Object, ok bool) { - switch expr := ast.Unparen(expr).(type) { - case *ast.Ident: - object = c.pass.TypesInfo.ObjectOf(expr) - case *ast.SelectorExpr: - object = c.pass.TypesInfo.ObjectOf(expr.Sel) - case *ast.CallExpr: - object = typeutil.Callee(c.pass.TypesInfo, expr) - } - switch object.(type) { - case *types.Var, *types.Func: - return object, true - } - return nil, false -} - -func (c *checker) enclosingSignature(cur inspector.Cursor) (signature *types.Signature, ok bool) { - for function := range cur.Enclosing((*ast.FuncDecl)(nil), (*ast.FuncLit)(nil)) { - switch function := function.Node().(type) { - case *ast.FuncDecl: - signature, ok = c.pass.TypesInfo.Defs[function.Name].Type().(*types.Signature) - case *ast.FuncLit: - signature, ok = c.pass.TypesInfo.TypeOf(function).(*types.Signature) - } - return signature, ok - } - return nil, false -} - -func inModule(pkgPath, module string) bool { - return pkgPath == module || strings.HasPrefix(pkgPath, module+"/") -} - -// isLocal reports whether an object is a variable declared inside a function, -// excluding parameters and results. -func isLocal(object types.Object) bool { - variable, ok := object.(*types.Var) - return ok && variable.Kind() == types.LocalVar -} - -func (c *checker) inTestFile(node ast.Node) bool { - return strings.HasSuffix(c.pass.Fset.Position(node.Pos()).Filename, "_test.go") -} - -func (c *checker) report(node ast.Node, what string) { - position := c.pass.Fset.Position(node.Pos()) - lines := c.allowed[position.Filename] - if lines[position.Line] || lines[position.Line-1] { - return - } - c.pass.Reportf(node.Pos(), "%s; use Option for a value that may be absent", what) -} - -func allowedLines(pass *analysis.Pass) map[string]map[int]bool { - allowed := map[string]map[int]bool{} - for _, file := range pass.Files { - for _, group := range file.Comments { - for _, comment := range group.List { - if !strings.HasPrefix(comment.Text, allowDirective) { - continue - } - position := pass.Fset.Position(comment.Pos()) - if allowed[position.Filename] == nil { - allowed[position.Filename] = map[int]bool{} - } - allowed[position.Filename][position.Line] = true - } - } - } - return allowed -} - -// trackLocals records the locals whose nil originates in the module: those -// declared without a value and those set from a first-party call. -func (c *checker) trackLocals(root inspector.Cursor) { - for cur := range root.Preorder((*ast.ValueSpec)(nil), (*ast.AssignStmt)(nil)) { - var names []ast.Expr - var values []ast.Expr - switch node := cur.Node().(type) { - case *ast.ValueSpec: - for _, name := range node.Names { - names = append(names, name) - } - values = node.Values - case *ast.AssignStmt: - if node.Tok != token.DEFINE { - continue - } - names, values = node.Lhs, node.Rhs - } - if len(values) > 0 && !c.isFirstPartyCall(values) { - continue - } - for _, name := range names { - if object := c.pass.TypesInfo.Defs[name.(*ast.Ident)]; object != nil && isLocal(object) { - c.tracked[object] = true - } - } - } -} - -func (c *checker) isFirstPartyCall(values []ast.Expr) bool { - if len(values) != 1 { - return false - } - call, ok := ast.Unparen(values[0]).(*ast.CallExpr) - if !ok { - return false - } - callee := typeutil.Callee(c.pass.TypesInfo, call) - return callee != nil && c.isFirstParty(callee) -} - -func allGenerated(files []*ast.File) bool { - for _, file := range files { - if !ast.IsGenerated(file) { - return false - } - } - return len(files) > 0 -} diff --git a/cmd/optionalnil/optionalnil_test.go b/cmd/optionalnil/optionalnil_test.go deleted file mode 100644 index 749679d..0000000 --- a/cmd/optionalnil/optionalnil_test.go +++ /dev/null @@ -1,11 +0,0 @@ -package main - -import ( - "testing" - - "golang.org/x/tools/go/analysis/analysistest" -) - -func TestAnalyzer(t *testing.T) { - analysistest.Run(t, analysistest.TestData(), newOptionalNil(), "./...") -} diff --git a/cmd/optionalnil/testdata/fixture.go b/cmd/optionalnil/testdata/fixture.go deleted file mode 100644 index 09e8394..0000000 --- a/cmd/optionalnil/testdata/fixture.go +++ /dev/null @@ -1,132 +0,0 @@ -package fixture - -import ( - "errors" - "net/http" - "net/url" - - "example.com/fixture/generated" -) - -type value struct{} - -type named interface { - name() string -} - -func (*value) name() string { return "" } - -var _ named = (*value)(nil) - -type node struct { - next *node -} - -type holder struct { - field *value - items []int - count int -} - -func find() *value { - return nil // want `nil returned; use Option` -} - -func converted() (*value, *value) { - return (*value)(nil), (nil) // want `nil returned; use Option` `nil returned; use Option` -} - -func findWithoutError() (*value, error) { - return nil, nil // want `nil returned; use Option` -} - -func load() (*value, error) { - return nil, errors.New("failed") -} - -func lookup() (*value, bool) { - return nil, false -} - -func succeed() error { - return nil -} - -func dynamic() any { - return nil -} - -func last() *node { - return nil -} - -func empty() []int { - return nil -} - -func allowed() *value { - return nil //optionalnil:allow The fixture needs a kept nil. -} - -func allowedAbove() *value { - //optionalnil:allow The fixture needs a kept nil. - return nil -} - -func use(target *value) {} - -func useAll(targets ...*value) {} - -func stores(h *holder, message *generated.Message) { - h.field = nil // want `nil stored in field; use Option` - _ = holder{field: nil} // want `nil stored in field; use Option` - _ = holder{nil, nil, 0} // want `nil stored in field; use Option` - var unset *value = nil // want `nil stored in unset; use Option` - _ = unset - h.items = nil - _ = map[string]*value{"missing": nil} // want `nil stored in map; use Option` - values := map[string]*value{} - values["missing"] = nil // want `nil stored in map; use Option` - _ = map[string][]int{"empty": nil} - _ = generated.Registry{"missing": nil} - message.Field = nil - _ = http.Server{Handler: nil} -} - -func arguments() { - use(nil) // want `nil passed to use; use Option` - useAll(nil) // want `nil passed to useAll; use Option` - use((*value)(nil)) // want `nil passed to use; use Option` - generated.Send(nil) - _, _ = http.NewRequest(http.MethodGet, "/", nil) -} - -func comparisons(h *holder, v any) error { - if h.field != nil { // want `nil compared with field; use Option` - return nil - } - if h.items == nil { // want `nil compared with items; use Option` - return nil - } - var later *value - if later != nil { // want `nil compared with later; use Option` - return nil - } - if found := find(); found != nil { // want `nil compared with found; use Option` - return nil - } - parsed, err := url.Parse("/") - if err != nil || parsed != nil { - return nil - } - if recovered := recover(); recovered != nil { - return nil - } - if v == nil { - return nil - } - if h == nil || h.field == nil { - return errors.New("a field is required") - } - return nil -} diff --git a/cmd/optionalnil/testdata/fixture_test.go b/cmd/optionalnil/testdata/fixture_test.go deleted file mode 100644 index 721015a..0000000 --- a/cmd/optionalnil/testdata/fixture_test.go +++ /dev/null @@ -1,8 +0,0 @@ -package fixture - -func testsPassNil() { - use(nil) - for _, input := range map[string]*value{"Nil": nil} { - use(input) - } -} diff --git a/cmd/optionalnil/testdata/generated/generated.go b/cmd/optionalnil/testdata/generated/generated.go deleted file mode 100644 index 6a1e6d4..0000000 --- a/cmd/optionalnil/testdata/generated/generated.go +++ /dev/null @@ -1,13 +0,0 @@ -// want package:"generatedPackage" - -// Code generated by fixture. DO NOT EDIT. - -package generated - -type Message struct { - Field *string -} - -type Registry map[string]*Message - -func Send(message *Message) {} diff --git a/cmd/optionalnil/testdata/go.mod b/cmd/optionalnil/testdata/go.mod deleted file mode 100644 index 50a0545..0000000 --- a/cmd/optionalnil/testdata/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module example.com/fixture - -go 1.27 diff --git a/go.mod b/go.mod index 8744496..e51def2 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,6 @@ require ( github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 golang.org/x/net v0.59.0 golang.org/x/sync v0.23.0 - golang.org/x/tools v0.51.0 google.golang.org/protobuf v1.36.12 ) @@ -32,7 +31,6 @@ require ( github.com/microsoft/TypeScript/tsc v0.0.0-20261001235638-09b1db061731 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/zeebo/xxh3 v1.1.0 // indirect - golang.org/x/mod v0.41.0 // indirect golang.org/x/sys v0.48.0 // indirect golang.org/x/text v0.42.0 // indirect ) diff --git a/go.sum b/go.sum index 1880a60..fd3d904 100644 --- a/go.sum +++ b/go.sum @@ -64,8 +64,6 @@ github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs= github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o= golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= -golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c= -golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o= golang.org/x/net v0.59.0 h1:5zfYln+w5XCxwrnMMJPufRgNoXEaGxl0wo5GqPXyues= golang.org/x/net v0.59.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg= golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk= @@ -74,8 +72,6 @@ golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo= golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og= golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI= golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E= -golang.org/x/tools v0.51.0 h1:k4Xc/1Om9jwkBJBo4NVLMSARBoWtK10mx+W5BnXCeAI= -golang.org/x/tools v0.51.0/go.mod h1:9eEncMayCV6zRMGhR5eZEC2iBx98qWcF1HZ9Z7wJOoA= google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc= google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= diff --git a/internal/typescript/typescript.go b/internal/typescript/typescript.go index 04a08aa..0a57014 100644 --- a/internal/typescript/typescript.go +++ b/internal/typescript/typescript.go @@ -106,7 +106,7 @@ func StringLiteral(t *Type) (value string, ok bool) { // relative to the program root. func Location(node *Node) string { file := SourceFileOf(node) - //optionalnil:allow The shim mirrors the compiler, whose nodes may have no file. + //nolint:optionalnil The shim mirrors the compiler, whose nodes may have no file. if file == nil { return "" } diff --git a/scripts/optionalnil b/scripts/optionalnil deleted file mode 120000 index e0b00b5..0000000 --- a/scripts/optionalnil +++ /dev/null @@ -1 +0,0 @@ -spectre-ingress \ No newline at end of file