gaijinentertainment / go-exhaustruct Goto Github PK
View Code? Open in Web Editor NEWgolang analyzer that finds structures with uninitialized fields
License: MIT License
golang analyzer that finds structures with uninitialized fields
License: MIT License
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"}
Due to cahce, linter now panics for cases when lots of same-name local types defined with defferent amount of fields.
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).
This:
type user struct {
Name string
Age int
}
func test() {
_ = []user{
{
Name: "",
},
}
}
Does not trigger exhaustruct.
As noted in golangci/golangci-lint#4304, the latest changes to this analyzer are blocked from merging into golangci-lint due to the Go version upgrade. This problem might be resolved in a few weeks with a golangci-lint upgrade, but creating this issue to track and in case you want to unblock the changes sooner @xobotyi.
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.
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,
})
}
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.
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
}
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.
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.
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.
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?
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!
At least that's what I've observed in my codebase with go 1.18, which is currently about 5k lines of code.
Do you have any idea why? This is by far my favourite linter, it solves the biggest issue I have with golang.
I was working on a refactor, and I moved packages around
The linter stopped detecting properly and didn't error.
I think it should give an error if the pattern was not found, because then something is wrong with your configuration.
If that is too risky, maybe then allow a configurable flag to error if no file was found from a pattern.
I volunteer to implementing this feature if necessary.
Thank you!
providing -fix
flag doesn't apply fixes as it is stated in help.
-fix
apply all suggested fixes
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).
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.