unexported-return: allow unexported type aliases for exported types#1456
unexported-return: allow unexported type aliases for exported types#1456tie wants to merge 1 commit intomgechev:masterfrom
Conversation
|
Thank you for your report. Personally, I don't think the behavior is wrong currently. See this: // example.go
package example
import "time"
// Exported type
type ExportedType = time.Time
// Unexported alias for an exported type
type internalAlias = ExportedType
// Exported function returning unexported alias
func GetInternalAlias() internalAlias {
return time.Now()
}
// Unexported alias for an exported function type
type internalFuncAlias = func()
// Exported function returning unexported alias
func GetFuncAlias() internalFuncAlias {
return func() {
println("hello")
}
}$ go doc -all .
package example // import "example"
FUNCTIONS
func GetInternalAlias() internalAlias
Exported function returning unexported alias
func GetFuncAlias() internalFuncAlias
Exported function returning unexported alias
TYPES
type ExportedType = time.Time
Exported typeAs you may notice, an unexported types are now shown in the docs, and it's not clear what is the original exported type. This is exactly what this rule is meant for. |
I’d assume that this rule is mostly meant to avoid returning types that cannot be passed around, i.e. E.g. we do allow interface types: Perhaps we can make this behavior configurable via an argument? |
That said, revive also allows returning unexported interfaces with unexported methods, which I think shouldn’t be valid per this rule. |
|
@tie returning interfaces is in general considered to be a bad practice (see). But even if the case is valid, generically speaking it is unfriendly to the user of your package to export a function signature that has unexported symbols in it. Of course, relying on IDE capabilities to discover the types and/or reading the original source can be an option, but it's still something that is being detected by this rule. Note, that this example is perfectly compilable, and usable: package main
import (
"example/exported"
)
func main() {
v := exported.Unexported()
_ = v // can’t pass around the value since the type is not exported
}
-- exported/exported.go --
package exported
type unexported struct{}
func Unexported() unexported {
return unexported{}
}But still is unfriendly to the user. Note, in certain cases you can even do more with unexported types: It is perfectly compilable and runnable as well, but is again unfriendly to the user. |
Allow unexported type aliases if there is an “exported” type that can be used instead of an alias.
Closes #1455