Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

staticcheck: detect unintentional encoder and decoder implementations #715

Open
ainar-g opened this issue Mar 24, 2020 · 6 comments
Open

Comments

@ainar-g
Copy link
Contributor

ainar-g commented Mar 24, 2020

(Couldn't think of a more descriptive title, feel free to change.)

See this tweet from @katiehockman.

func main() {
	t := struct {
		time.Time
		N int
	}{time.Now(), 5}

	m, _ := json.Marshal(t)
	fmt.Printf("%s", m)
}

This will print 2009-11-10T23:00:00Z, because time.Time implements json.Marshaler.

The structs that we can definitely mark are the structs that (1) have fields with json tags, (2) embed marshalers, and (3) don't implement the interface themselves:

type bad struct {
	time.Time
	N int `json:"n"`
}
@ainar-g ainar-g added the needs-triage Newly filed issue that needs triage label Mar 24, 2020
@FiloSottile
Copy link
Sponsor

I'd argue that any type that embeds a type that implements TextMarshaler, BinaryMarshaler, or json.Marshaler (and unmarshalers) should get flagged. It's almost certainly not intended, and if it is it can be implemented in the parent type by calling the embedded method.

@ainar-g
Copy link
Contributor Author

ainar-g commented Mar 24, 2020

@FiloSottile I think, that's way too radical. Consider something like:

type TimeWithSomeContext struct {
	time.Time

	Processed bool
	Status    int
	// …
}

I want TimeWithSomeContext to look the same as the usual time in my JSON, because the users of the JSON API don't care about Processed, Status, etc., so embedding time.Time is perfectly fine here. Forcing all programmers to add a MarshalJSON method here would be too much, and also not quite something that's in staticcheck's philosophy (at least as I understand it from Dominik's previous comments).

@broady
Copy link

broady commented Mar 24, 2020

@ainar-g you'd do this to resolve the error. seems nice to be explicit:

type TimeWithSomeContext struct {
	Time time.Time

	Processed bool
	Status    int
	// …
}

func (t TimeWithSomeContext) MarshalJSON() ([]byte, error) {
	return t.Time.MarshalJSON()
}

@dominikh
Copy link
Owner

I agree with Ainar. We can very likely flag structs that use tags, but flagging any struct that embeds a marshaler will lead to some false positives, and we always try to err on the side of false negatives, not false positives. It's fine to miss some bugs, it's not fine to claim bugs where there aren't any.

We could consider adding an opt-in for more strict checking a la Filippo, and then you can decide to enforce that rule in your own code base.

@dominikh dominikh added new-check and removed needs-triage Newly filed issue that needs triage labels Mar 24, 2020
@FiloSottile
Copy link
Sponsor

FiloSottile commented Mar 25, 2020 via email

@dominikh
Copy link
Owner

dominikh commented Mar 25, 2020

Could you also flag locations where such a type is passed to json.Marshal?

The statically known ones, yes. For the case of embedding but no struct tags, we can only flag the locations where json.Marshal etc are being called; merely embedding a marshaler in itself is not an error if the type is never being marshaled.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants