|
| 1 | +package unexportedapi |
| 2 | + |
| 3 | +type Foo struct { |
| 4 | + Bar *oops |
| 5 | +} |
| 6 | + |
| 7 | +type oops struct { |
| 8 | + v int |
| 9 | +} |
| 10 | + |
| 11 | +type Okay struct { |
| 12 | + Sure int |
| 13 | + Value ***Okay2 |
| 14 | +} |
| 15 | + |
| 16 | +type Okay2 struct { |
| 17 | + VeryGood struct{} |
| 18 | +} |
| 19 | + |
| 20 | +func OkayFunc(v *Okay) *Okay2 { |
| 21 | + if v == nil { |
| 22 | + return nil |
| 23 | + } |
| 24 | + return **v.Value |
| 25 | +} |
| 26 | + |
| 27 | +// Test cases for various scenarios |
| 28 | + |
| 29 | +// Exported function with unexported parameter type |
| 30 | +func BadFunc(x unexported) {} |
| 31 | + |
| 32 | +// Exported function with unexported return type |
| 33 | +func AnotherBadFunc() *unexported { |
| 34 | + return nil |
| 35 | +} |
| 36 | + |
| 37 | +// Exported function with unexported type in slice |
| 38 | +func SliceFunc(x []unexported) {} |
| 39 | + |
| 40 | +// Exported function with unexported type in map |
| 41 | +func MapFunc(x map[string]unexported) {} |
| 42 | + |
| 43 | +// Exported function with unexported type in map key |
| 44 | +func MapKeyFunc(x map[unexported]string) {} |
| 45 | + |
| 46 | +// Exported function with unexported type in channel |
| 47 | +func ChanFunc(x chan unexported) {} |
| 48 | + |
| 49 | +// Exported type alias to unexported type |
| 50 | +type BadAlias = unexported |
| 51 | + |
| 52 | +// Exported type with unexported embedded field (OK since unexported has no exported members) |
| 53 | +type OkayEmbed struct { |
| 54 | + unexported |
| 55 | +} |
| 56 | + |
| 57 | +// Unexported type with exported field |
| 58 | +type unexportedWithExportedField struct { |
| 59 | + ExportedField int |
| 60 | +} |
| 61 | + |
| 62 | +// Bad - exported type embedding unexported type with exported members |
| 63 | +type BadEmbed struct { |
| 64 | + unexportedWithExportedField |
| 65 | +} |
| 66 | + |
| 67 | +// Unexported type - should not trigger |
| 68 | +type okayUnexported struct { |
| 69 | + field unexported |
| 70 | +} |
| 71 | + |
| 72 | +// Exported interface with unexported type in method |
| 73 | +type BadInterface interface { |
| 74 | + Method(x unexported) |
| 75 | +} |
| 76 | + |
| 77 | +// Exported interface with unexported return type |
| 78 | +type AnotherBadInterface interface { |
| 79 | + Method() unexported |
| 80 | +} |
| 81 | + |
| 82 | +type unexported struct { |
| 83 | + x int |
| 84 | +} |
| 85 | + |
| 86 | +// Exported function with multiple return values including unexported |
| 87 | +func MultiReturn() (int, unexported, error) { |
| 88 | + return 0, unexported{}, nil |
| 89 | +} |
| 90 | + |
| 91 | +// Exported variable with unexported type |
| 92 | +var BadVar unexported |
| 93 | + |
| 94 | +// Exported const with unexported type (should not be possible, but let's be safe) |
| 95 | +// const BadConst unexported = unexported{} // This won't compile anyway |
| 96 | + |
| 97 | +// Array of unexported type |
| 98 | +type BadArray [10]unexported |
| 99 | + |
| 100 | +// Exported function with variadic unexported parameter |
| 101 | +func VariadicFunc(args ...unexported) {} |
| 102 | + |
| 103 | +// Exported type with method returning unexported type |
| 104 | +type ExportedWithMethod struct{} |
| 105 | + |
| 106 | +func (e ExportedWithMethod) Method() unexported { |
| 107 | + return unexported{} |
| 108 | +} |
| 109 | + |
| 110 | +// Exported type with pointer receiver method returning unexported type |
| 111 | +func (e *ExportedWithMethod) PointerMethod() *unexported { |
| 112 | + return nil |
| 113 | +} |
| 114 | + |
| 115 | +// Generic type with unexported type constraint (Go 1.18+) |
| 116 | +type GenericExported[T any] struct { |
| 117 | + Value T |
| 118 | +} |
| 119 | + |
| 120 | +// Okay - unexported method on exported type (methods are not part of exported API unless on exported interface) |
| 121 | +func (e ExportedWithMethod) unexportedMethod() unexported { |
| 122 | + return unexported{} |
| 123 | +} |
| 124 | + |
| 125 | +// Test variables initialized with function calls |
| 126 | + |
| 127 | +// Helper functions for testing |
| 128 | +func helperReturnsExported() *Okay2 { |
| 129 | + return &Okay2{} |
| 130 | +} |
| 131 | + |
| 132 | +func helperReturnsUnexported() unexported { |
| 133 | + return unexported{} |
| 134 | +} |
| 135 | + |
| 136 | +// Okay - exported variable initialized by calling unexported function that returns exported type |
| 137 | +var OkayVarFromUnexportedFunc = helperReturnsExported() |
| 138 | + |
| 139 | +// Bad - exported variable initialized by calling exported function that returns unexported type |
| 140 | +var BadVarFromFunc = helperReturnsUnexported() |
| 141 | + |
| 142 | +// Okay - exported variable with explicit type (implementation doesn't matter) |
| 143 | +var OkayVarExplicitType *Okay2 = helperReturnsExported() |
| 144 | + |
| 145 | +// Bad - exported variable with explicit unexported type |
| 146 | +var BadVarExplicitType unexported = helperReturnsUnexported() |
| 147 | + |
| 148 | +// Test type aliases |
| 149 | +type ( |
| 150 | + ExportedString string |
| 151 | + unexportedString string |
| 152 | +) |
| 153 | + |
| 154 | +// Okay - exported function using exported type alias |
| 155 | +func OkayTypeAlias(s ExportedString) {} |
| 156 | + |
| 157 | +// Bad - exported function using unexported type alias |
| 158 | +func BadTypeAlias(s unexportedString) {} |
| 159 | + |
| 160 | +// Test unexported types with exported methods (for interface satisfaction) |
| 161 | +type unexportedImpl struct { |
| 162 | + value int |
| 163 | +} |
| 164 | + |
| 165 | +// Okay - exported method on unexported type (not part of public API, often used for interface satisfaction) |
| 166 | +func (u *unexportedImpl) ExportedMethod() int { |
| 167 | + return u.value |
| 168 | +} |
| 169 | + |
| 170 | +// Okay - exported method on unexported type can return unexported types |
| 171 | +func (u *unexportedImpl) AnotherMethod() unexported { |
| 172 | + return unexported{} |
| 173 | +} |
| 174 | + |
| 175 | +// Test for avoiding duplicate errors on embedded types with methods |
| 176 | + |
| 177 | +type BaseWithBadMethod struct{} |
| 178 | + |
| 179 | +// This method has an unexported return type - should be flagged once |
| 180 | +func (b *BaseWithBadMethod) GetUnexported() *unexported { |
| 181 | + return nil |
| 182 | +} |
| 183 | + |
| 184 | +// This type embeds BaseWithBadMethod - should NOT re-report the GetUnexported method issue |
| 185 | +type DerivedEmbedding struct { |
| 186 | + BaseWithBadMethod |
| 187 | +} |
| 188 | + |
| 189 | +// Test embedding unexported type with exported method that references unexported type |
| 190 | +type unexportedBase struct{} |
| 191 | + |
| 192 | +// This exported method on unexported type won't be checked (unexported type methods are skipped) |
| 193 | +func (u *unexportedBase) MethodWithBadReturn() *unexported { |
| 194 | + return nil |
| 195 | +} |
| 196 | + |
| 197 | +// This embeds an unexported type - what happens? |
| 198 | +// OK because methods on unexported types aren't checked |
| 199 | +type EmbeddingUnexportedBase struct { |
| 200 | + unexportedBase |
| 201 | +} |
| 202 | + |
| 203 | +// Test embedding unexported type with exported field that references unexported type |
| 204 | +type unexportedBaseWithField struct { |
| 205 | + ExportedField *unexported |
| 206 | +} |
| 207 | + |
| 208 | +// Bad - embeds unexported type with exported field that references unexported type |
| 209 | +type EmbeddingUnexportedBaseWithField struct { |
| 210 | + unexportedBaseWithField |
| 211 | +} |
0 commit comments