Giter VIP home page Giter VIP logo

go-exhaustruct's Introduction

exhaustruct

Package Version Go version GitHub Workflow Status (with branch) License


exhaustruct is a golang analyzer that finds structures with uninitialized fields

Installation

go get -u github.com/GaijinEntertainment/go-exhaustruct/v3/cmd/exhaustruct

Usage

exhaustruct [-flag] [package]

Flags:
  -i value
        Regular expression to match type names, can receive multiple flags.
        Anonymous structs can be matched by '<anonymous>' alias.
        4ex:
                github.com/GaijinEntertainment/go-exhaustruct/v3/analyzer\.<anonymous>
                github.com/GaijinEntertainment/go-exhaustruct/v3/analyzer\.TypeInfo
        
  -e value
        Regular expression to exclude type names, can receive multiple flags.
        Anonymous structs can be matched by '<anonymous>' alias.
        4ex:
                github.com/GaijinEntertainment/go-exhaustruct/v3/analyzer\.<anonymous>
                github.com/GaijinEntertainment/go-exhaustruct/v3/analyzer\.TypeInfo

Comment directives

exhaustruct supports comment directives to mark individual structures as ignored during linting or enforce it's check regardless global configuration. Comment directives have precedence over global configuration.

  • //exhaustruct:ignore - ignore structure during linting
  • //exhaustruct:enforce - enforce structure check during linting, even in case global configuration says it should be ignored.

Note: all directives can be placed in the end of structure declaration or on the line above it.

Also, any additional comment can be placed same line right after the directive or anywhere around it, but directive should be at the very beginning of the line. It is recommended to comment directives, especially when ignoring structures - it will help to understand the reason later.

Example

// Package a.go
package a

type Shape struct {
	Length int
	Width  int

	volume    int
	Perimeter int `exhaustruct:"optional"`
}

// valid
var a Shape = Shape{
	Length: 5,
	Width:  3,
	volume: 5,
}

// invalid, `volume` is missing
var b Shape = Shape{
	Length: 5,
	Width:  3,
}

// Package b.go
package b

import "a"

// valid
var b Shape = a.Shape{
	Length: 5,
	Width:  3,
}

// invalid, `Width` is missing
var b Shape = a.Shape{
	Length: 5,
}

go-exhaustruct's People

Contributors

dependabot[bot] avatar ldez avatar navijation avatar xobotyi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

go-exhaustruct's Issues

Setting to allow zero-values for structs

In my code I often have return foobar{}, errors.New("some error") to return an empty struct when there is an error. Currently linter complains about that.

I can go around that by doing return *new(foobar), errors.New("some error") but that is too verbose for me.

So I wonder, could there be a setting to allow foobar{} and not complain about it? So if you initialize without any field, then nothing happens. If you add any field, then linter requires to add all of them.

Allow specifying optional field types using configuration

With the introduction of generics, we now have various implementations of optionals, such as this. It would be beneficial to add the ability to specify these field types as optional using configuration.

Suggestion:
Introduce a new configuration option -optypes that accepts a list of regular expressions, similar to the current approach with exclude and include structs.

 import "github.com/samber/mo"

type Shape struct {
	Length int
	Width  int

	volume    mo.Option[int]
}

Running with go run cmd/exhaustruct/main.go -optypes="github.com/samber/mo.Option\[.*\]" . won't return errors.
I've also created a prototype.

Doubts:
What if we want to overwrite optypes for specific case?

Support allowlisting via a comment rather than global config

Having to specify every covered struct in a global configuration file isn't ideal for reasons such as build incrementalism. I suggest also supporting an approach similar to the one used in nishanths/exhaustive, except at the struct level.

//exhaustruct:enforce
type Params struct {
    RequiredInt       int
    RequiredString string
}

type Data struct {
    OptionalInt       int
    OptionalString string
}

Unlike in that other analyzer, it probably makes sense to always enforce presence at every site of struct initialization by default if the struct is configured to require parameters. This is consistent with e.g. how most languages deal with named, required parameters to function calls.

A global configuration flag -explicit-exhaustive-struct with default value false would be required to maintain backwards compatibility (in the sense of ensuring previously failed builds continue to fail).

type definition in function scope not supported

example of go file^

package main

import "fmt"

func f1() {
	type args struct {
		n int
	}

	val := args{n: 123}
	fmt.Println(val)
}

func f2() {
	type args struct {
		str string
	}

	val := args{str: "asdf"}
	fmt.Println(val)
}

linter output:

tst.go:15:7: type `args` is unused (unused)
	type args struct {
	     ^
tst.go:6:7: type `args` is unused (unused)
	type args struct {
	     ^
tst.go:19:9: n is missing in args (exhaustruct)
	val := args{str: "asdf"}

Setting include/exclude package of code not package of struct

I'm using exhaustruct with golangci-lint.

I'd like to specify the package or path to check, not the defined package of the struct.
Is it possible?

I mean that I'd like to check the structs used in the go/cmd package, and the structs are defined in the go/pkg package.

Support ignoring non-exported fields

If I have private struct with some exported fields (uppercase) and some not (lowercase) I would like that this linter complains only if I forget about an exported field. While other fields can be omitted (they are designed so).

Support allowlisting via struct literal level comment rather than global config

Similar to #37, but at the level of struct literals rather than struct definitions.

The use case: ensuring that "type mapper" functions are properly updated when the destination type gets updated. For example

type InternalStatement struct {
	Balance   int
	StartDate time.Time
	EndDate   time.Time
}

type ExternalStatement struct {
	Balance   money.Amount `json:"balance"`
	StartDate string       `json:"start_date"`
	EndDate   string       `json:"end_date"`
}

func ToExternalStatement(statement *InternalStatement) *ExternalStatement {
	//exhaustruct:enforce
	return &ExternalStatement{
		Balance:   money.FromCents(statement.Balance),
		StartDate: formatMonthDay(statement.StartDate),
		EndDate:   formatMonthDay(statement.EndDate),
	}
}

If the external statement acquires a new field such as "overdue balance", the analyzer will catch if the mapper has not been updated to populate this new field. However, it is might not necessary for every external statement instantiation to require every field be populated.

Generic struct is ignored

If struct has type parameters (aka generics), the linter doesn't find uninitialized fields.

type MyStruct[T any] struct {
	A T
	B string
}

var x = MyStruct[int]{
	A: 10,
	// linter doesn't catch missing B
}

Anonymous structs should not be checked for exhaustiveness

In my experience, the most common use case for an anonymous struct is in unit test case loops. These should not typically be exhaustive-enforced because many fields are omitted by default to improve the brevity of the test case, e.g. test writers do not always want to specify expectErr: false everywhere.

Exhaustruct's current behavior on anonymous structs will be prohibitive for many if not most company codebases, including all the ones I've worked on in the past.

For backwards compatibility, this behavior should be configured with a flag.

Add support for sruct composition

Hi.
Nice tool you developed here. I love the idea behind it.
However, I have a problem with it.
Take a look at the program below:

type A struct {
}

func (a *A) SomeFunc() {}

type B struct {
	A
	someField int
}

func main() {
	_ = B{someField: 42}
}

You raise an error in this case, but in my opinion, it's a perfectly reasonable piece of code. I only add the A struct in B to use its method implementation. I'm not interested in filling its fields, But exhaustruct makes me to do it.

 main.B is missing field A

I don't want to add an exception on struct B either because I may forget filling other fields of it in the future and I need exhastruct to check on its other fields.
Please contact me if you have any other questions regarding this issue.
Thanks!

The exclude rules support files?

I tried as following rules, in golangci-lint:

linters-settings:
  exhaustruct:
    include:
      - "^.*Factory$"
      - "^.*Base$"
    exclude:
      - "^.*_test.go$"

but it doesn't work, so I wonder if it supports or not.

Multiply-nested named types for structs will not be subject to enforcement

I noticed here that the underlying type of a named type must be of type struct in order for enforcement to occur. However, this excludes named types with underlying named types with underlying struct types (and so on).

package main

import "fmt"

type A struct {
	X int
	Y int
}

type B A

func main() {
	fmt.Println(B{ // never enforced
		X: 5,
	})
}

It's not clear if this is intentional behavior. The most reasonable way to handle this from my perspective, as would be consistent with nishanths/exhaustive, would be to treat this the same as

type B  struct{
	X int
	Y int
}

func main() {
	fmt.Println(B{  // enforced based on matching rules for type B
		X: 5,
	})
}

fix flag doesn't work

providing -fix flag doesn't apply fixes as it is stated in help.
-fix
apply all suggested fixes

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.