Brad Garcia | ff7cfaf | 2013-12-04 10:37:01 -0500 | [diff] [blame] | 1 | // Copyright 2013 The Go Authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | package godoc |
| 6 | |
| 7 | import ( |
Jay Conrod | e1bdc76 | 2017-02-10 16:20:20 -0500 | [diff] [blame] | 8 | "bytes" |
Brad Fitzpatrick | 0f65b31 | 2016-11-29 23:53:36 +0000 | [diff] [blame] | 9 | "go/parser" |
| 10 | "go/token" |
Jay Conrod | 00f7cd5 | 2017-02-07 17:34:43 -0500 | [diff] [blame] | 11 | "strings" |
Brad Garcia | ff7cfaf | 2013-12-04 10:37:01 -0500 | [diff] [blame] | 12 | "testing" |
aarzilli | c6fca02 | 2022-02-01 18:45:42 +0100 | [diff] [blame] | 13 | |
| 14 | "golang.org/x/tools/internal/typeparams" |
Brad Garcia | ff7cfaf | 2013-12-04 10:37:01 -0500 | [diff] [blame] | 15 | ) |
| 16 | |
| 17 | func TestPkgLinkFunc(t *testing.T) { |
| 18 | for _, tc := range []struct { |
| 19 | path string |
| 20 | want string |
| 21 | }{ |
Alan Donovan | 6c93dbf | 2014-09-10 09:02:54 -0400 | [diff] [blame] | 22 | {"/src/fmt", "pkg/fmt"}, |
| 23 | {"src/fmt", "pkg/fmt"}, |
Brad Garcia | ff7cfaf | 2013-12-04 10:37:01 -0500 | [diff] [blame] | 24 | {"/fmt", "pkg/fmt"}, |
Brad Garcia | 936715c | 2014-06-27 10:25:57 -0400 | [diff] [blame] | 25 | {"fmt", "pkg/fmt"}, |
Brad Garcia | ff7cfaf | 2013-12-04 10:37:01 -0500 | [diff] [blame] | 26 | } { |
Robert Griesemer | 42a4cd3 | 2014-04-07 12:54:28 -0700 | [diff] [blame] | 27 | if got := pkgLinkFunc(tc.path); got != tc.want { |
| 28 | t.Errorf("pkgLinkFunc(%v) = %v; want %v", tc.path, got, tc.want) |
Brad Garcia | ff7cfaf | 2013-12-04 10:37:01 -0500 | [diff] [blame] | 29 | } |
| 30 | } |
| 31 | } |
| 32 | |
| 33 | func TestSrcPosLinkFunc(t *testing.T) { |
| 34 | for _, tc := range []struct { |
| 35 | src string |
| 36 | line int |
| 37 | low int |
| 38 | high int |
| 39 | want string |
| 40 | }{ |
Alan Donovan | 6c93dbf | 2014-09-10 09:02:54 -0400 | [diff] [blame] | 41 | {"/src/fmt/print.go", 42, 30, 50, "/src/fmt/print.go?s=30:50#L32"}, |
| 42 | {"/src/fmt/print.go", 2, 1, 5, "/src/fmt/print.go?s=1:5#L1"}, |
| 43 | {"/src/fmt/print.go", 2, 0, 0, "/src/fmt/print.go#L2"}, |
| 44 | {"/src/fmt/print.go", 0, 0, 0, "/src/fmt/print.go"}, |
| 45 | {"/src/fmt/print.go", 0, 1, 5, "/src/fmt/print.go?s=1:5#L1"}, |
| 46 | {"fmt/print.go", 0, 0, 0, "/src/fmt/print.go"}, |
| 47 | {"fmt/print.go", 0, 1, 5, "/src/fmt/print.go?s=1:5#L1"}, |
Brad Garcia | ff7cfaf | 2013-12-04 10:37:01 -0500 | [diff] [blame] | 48 | } { |
Robert Griesemer | 42a4cd3 | 2014-04-07 12:54:28 -0700 | [diff] [blame] | 49 | if got := srcPosLinkFunc(tc.src, tc.line, tc.low, tc.high); got != tc.want { |
| 50 | t.Errorf("srcLinkFunc(%v, %v, %v, %v) = %v; want %v", tc.src, tc.line, tc.low, tc.high, got, tc.want) |
Brad Garcia | ff7cfaf | 2013-12-04 10:37:01 -0500 | [diff] [blame] | 51 | } |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | func TestSrcLinkFunc(t *testing.T) { |
| 56 | for _, tc := range []struct { |
| 57 | src string |
| 58 | want string |
| 59 | }{ |
Alan Donovan | 6c93dbf | 2014-09-10 09:02:54 -0400 | [diff] [blame] | 60 | {"/src/fmt/print.go", "/src/fmt/print.go"}, |
| 61 | {"src/fmt/print.go", "/src/fmt/print.go"}, |
| 62 | {"/fmt/print.go", "/src/fmt/print.go"}, |
| 63 | {"fmt/print.go", "/src/fmt/print.go"}, |
Brad Garcia | ff7cfaf | 2013-12-04 10:37:01 -0500 | [diff] [blame] | 64 | } { |
Robert Griesemer | 42a4cd3 | 2014-04-07 12:54:28 -0700 | [diff] [blame] | 65 | if got := srcLinkFunc(tc.src); got != tc.want { |
| 66 | t.Errorf("srcLinkFunc(%v) = %v; want %v", tc.src, got, tc.want) |
Brad Garcia | ff7cfaf | 2013-12-04 10:37:01 -0500 | [diff] [blame] | 67 | } |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | func TestQueryLinkFunc(t *testing.T) { |
| 72 | for _, tc := range []struct { |
| 73 | src string |
| 74 | query string |
| 75 | line int |
| 76 | want string |
| 77 | }{ |
Alan Donovan | 6c93dbf | 2014-09-10 09:02:54 -0400 | [diff] [blame] | 78 | {"/src/fmt/print.go", "Sprintf", 33, "/src/fmt/print.go?h=Sprintf#L33"}, |
| 79 | {"/src/fmt/print.go", "Sprintf", 0, "/src/fmt/print.go?h=Sprintf"}, |
| 80 | {"src/fmt/print.go", "EOF", 33, "/src/fmt/print.go?h=EOF#L33"}, |
| 81 | {"src/fmt/print.go", "a%3f+%26b", 1, "/src/fmt/print.go?h=a%3f+%26b#L1"}, |
Brad Garcia | ff7cfaf | 2013-12-04 10:37:01 -0500 | [diff] [blame] | 82 | } { |
Robert Griesemer | 42a4cd3 | 2014-04-07 12:54:28 -0700 | [diff] [blame] | 83 | if got := queryLinkFunc(tc.src, tc.query, tc.line); got != tc.want { |
| 84 | t.Errorf("queryLinkFunc(%v, %v, %v) = %v; want %v", tc.src, tc.query, tc.line, got, tc.want) |
Brad Garcia | ff7cfaf | 2013-12-04 10:37:01 -0500 | [diff] [blame] | 85 | } |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | func TestDocLinkFunc(t *testing.T) { |
| 90 | for _, tc := range []struct { |
| 91 | src string |
| 92 | ident string |
| 93 | want string |
| 94 | }{ |
Brad Garcia | 936715c | 2014-06-27 10:25:57 -0400 | [diff] [blame] | 95 | {"fmt", "Sprintf", "/pkg/fmt/#Sprintf"}, |
| 96 | {"fmt", "EOF", "/pkg/fmt/#EOF"}, |
Brad Garcia | ff7cfaf | 2013-12-04 10:37:01 -0500 | [diff] [blame] | 97 | } { |
Robert Griesemer | 42a4cd3 | 2014-04-07 12:54:28 -0700 | [diff] [blame] | 98 | if got := docLinkFunc(tc.src, tc.ident); got != tc.want { |
| 99 | t.Errorf("docLinkFunc(%v, %v) = %v; want %v", tc.src, tc.ident, got, tc.want) |
| 100 | } |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | func TestSanitizeFunc(t *testing.T) { |
| 105 | for _, tc := range []struct { |
| 106 | src string |
| 107 | want string |
| 108 | }{ |
| 109 | {}, |
| 110 | {"foo", "foo"}, |
| 111 | {"func f()", "func f()"}, |
| 112 | {"func f(a int,)", "func f(a int)"}, |
| 113 | {"func f(a int,\n)", "func f(a int)"}, |
| 114 | {"func f(\n\ta int,\n\tb int,\n\tc int,\n)", "func f(a int, b int, c int)"}, |
| 115 | {" ( a, b, c ) ", "(a, b, c)"}, |
| 116 | {"( a, b, c int, foo bar , )", "(a, b, c int, foo bar)"}, |
| 117 | {"{ a, b}", "{a, b}"}, |
| 118 | {"[ a, b]", "[a, b]"}, |
| 119 | } { |
| 120 | if got := sanitizeFunc(tc.src); got != tc.want { |
| 121 | t.Errorf("sanitizeFunc(%v) = %v; want %v", tc.src, got, tc.want) |
Brad Garcia | ff7cfaf | 2013-12-04 10:37:01 -0500 | [diff] [blame] | 122 | } |
| 123 | } |
| 124 | } |
Brad Fitzpatrick | 0f65b31 | 2016-11-29 23:53:36 +0000 | [diff] [blame] | 125 | |
| 126 | // Test that we add <span id="StructName.FieldName"> elements |
| 127 | // to the HTML of struct fields. |
| 128 | func TestStructFieldsIDAttributes(t *testing.T) { |
Jay Conrod | e1bdc76 | 2017-02-10 16:20:20 -0500 | [diff] [blame] | 129 | got := linkifySource(t, []byte(` |
Brad Fitzpatrick | 0f65b31 | 2016-11-29 23:53:36 +0000 | [diff] [blame] | 130 | package foo |
| 131 | |
| 132 | type T struct { |
Brad Fitzpatrick | e5f9a3d | 2016-11-30 23:49:04 +0000 | [diff] [blame] | 133 | NoDoc string |
Brad Fitzpatrick | 0f65b31 | 2016-11-29 23:53:36 +0000 | [diff] [blame] | 134 | |
Brad Fitzpatrick | e5f9a3d | 2016-11-30 23:49:04 +0000 | [diff] [blame] | 135 | // Doc has a comment. |
| 136 | Doc string |
| 137 | |
| 138 | // Opt, if non-nil, is an option. |
| 139 | Opt *int |
Brad Fitzpatrick | 61efd71 | 2017-01-20 18:35:00 +0000 | [diff] [blame] | 140 | |
| 141 | // Опция - другое поле. |
| 142 | Опция bool |
Brad Fitzpatrick | 0f65b31 | 2016-11-29 23:53:36 +0000 | [diff] [blame] | 143 | } |
Brad Fitzpatrick | 61efd71 | 2017-01-20 18:35:00 +0000 | [diff] [blame] | 144 | `)) |
| 145 | want := `type T struct { |
| 146 | <span id="T.NoDoc"></span>NoDoc <a href="/pkg/builtin/#string">string</a> |
| 147 | |
| 148 | <span id="T.Doc"></span><span class="comment">// Doc has a comment.</span> |
| 149 | Doc <a href="/pkg/builtin/#string">string</a> |
| 150 | |
| 151 | <span id="T.Opt"></span><span class="comment">// Opt, if non-nil, is an option.</span> |
| 152 | Opt *<a href="/pkg/builtin/#int">int</a> |
| 153 | |
| 154 | <span id="T.Опция"></span><span class="comment">// Опция - другое поле.</span> |
| 155 | Опция <a href="/pkg/builtin/#bool">bool</a> |
| 156 | }` |
| 157 | if got != want { |
| 158 | t.Errorf("got: %s\n\nwant: %s\n", got, want) |
| 159 | } |
| 160 | } |
| 161 | |
Jay Conrod | 3bcb6ef | 2017-04-11 15:25:53 -0400 | [diff] [blame] | 162 | // Test that we add <span id="ConstName"> elements to the HTML |
| 163 | // of definitions in const and var specs. |
| 164 | func TestValueSpecIDAttributes(t *testing.T) { |
| 165 | got := linkifySource(t, []byte(` |
| 166 | package foo |
| 167 | |
| 168 | const ( |
| 169 | NoDoc string = "NoDoc" |
| 170 | |
| 171 | // Doc has a comment |
| 172 | Doc = "Doc" |
| 173 | |
| 174 | NoVal |
| 175 | )`)) |
| 176 | want := `const ( |
| 177 | <span id="NoDoc">NoDoc</span> <a href="/pkg/builtin/#string">string</a> = "NoDoc" |
| 178 | |
| 179 | <span class="comment">// Doc has a comment</span> |
| 180 | <span id="Doc">Doc</span> = "Doc" |
| 181 | |
| 182 | <span id="NoVal">NoVal</span> |
| 183 | )` |
| 184 | if got != want { |
| 185 | t.Errorf("got: %s\n\nwant: %s\n", got, want) |
| 186 | } |
| 187 | } |
| 188 | |
Jay Conrod | e1bdc76 | 2017-02-10 16:20:20 -0500 | [diff] [blame] | 189 | func TestCompositeLitLinkFields(t *testing.T) { |
| 190 | got := linkifySource(t, []byte(` |
| 191 | package foo |
| 192 | |
| 193 | type T struct { |
| 194 | X int |
| 195 | } |
| 196 | |
| 197 | var S T = T{X: 12}`)) |
| 198 | want := `type T struct { |
| 199 | <span id="T.X"></span>X <a href="/pkg/builtin/#int">int</a> |
| 200 | } |
Jay Conrod | 3bcb6ef | 2017-04-11 15:25:53 -0400 | [diff] [blame] | 201 | var <span id="S">S</span> <a href="#T">T</a> = <a href="#T">T</a>{<a href="#T.X">X</a>: 12}` |
Jay Conrod | e1bdc76 | 2017-02-10 16:20:20 -0500 | [diff] [blame] | 202 | if got != want { |
| 203 | t.Errorf("got: %s\n\nwant: %s\n", got, want) |
| 204 | } |
| 205 | } |
| 206 | |
Masahiro Furudate | ce12915 | 2017-05-07 04:40:42 +0900 | [diff] [blame] | 207 | func TestFuncDeclNotLink(t *testing.T) { |
| 208 | // Function. |
| 209 | got := linkifySource(t, []byte(` |
| 210 | package http |
| 211 | |
| 212 | func Get(url string) (resp *Response, err error)`)) |
| 213 | want := `func Get(url <a href="/pkg/builtin/#string">string</a>) (resp *<a href="#Response">Response</a>, err <a href="/pkg/builtin/#error">error</a>)` |
| 214 | if got != want { |
| 215 | t.Errorf("got: %s\n\nwant: %s\n", got, want) |
| 216 | } |
| 217 | |
| 218 | // Method. |
| 219 | got = linkifySource(t, []byte(` |
| 220 | package http |
| 221 | |
| 222 | func (h Header) Get(key string) string`)) |
| 223 | want = `func (h <a href="#Header">Header</a>) Get(key <a href="/pkg/builtin/#string">string</a>) <a href="/pkg/builtin/#string">string</a>` |
| 224 | if got != want { |
| 225 | t.Errorf("got: %s\n\nwant: %s\n", got, want) |
| 226 | } |
| 227 | } |
| 228 | |
Jay Conrod | e1bdc76 | 2017-02-10 16:20:20 -0500 | [diff] [blame] | 229 | func linkifySource(t *testing.T, src []byte) string { |
Brad Fitzpatrick | 61efd71 | 2017-01-20 18:35:00 +0000 | [diff] [blame] | 230 | p := &Presentation{ |
| 231 | DeclLinks: true, |
| 232 | } |
Brad Fitzpatrick | 0f65b31 | 2016-11-29 23:53:36 +0000 | [diff] [blame] | 233 | fset := token.NewFileSet() |
| 234 | af, err := parser.ParseFile(fset, "foo.go", src, parser.ParseComments) |
| 235 | if err != nil { |
| 236 | t.Fatal(err) |
| 237 | } |
Jay Conrod | e1bdc76 | 2017-02-10 16:20:20 -0500 | [diff] [blame] | 238 | var buf bytes.Buffer |
Brad Fitzpatrick | 0f65b31 | 2016-11-29 23:53:36 +0000 | [diff] [blame] | 239 | pi := &PageInfo{ |
| 240 | FSet: fset, |
| 241 | } |
Jay Conrod | e1bdc76 | 2017-02-10 16:20:20 -0500 | [diff] [blame] | 242 | sep := "" |
| 243 | for _, decl := range af.Decls { |
| 244 | buf.WriteString(sep) |
| 245 | sep = "\n" |
| 246 | buf.WriteString(p.node_htmlFunc(pi, decl, true)) |
| 247 | } |
| 248 | return buf.String() |
Brad Fitzpatrick | 61efd71 | 2017-01-20 18:35:00 +0000 | [diff] [blame] | 249 | } |
Brad Fitzpatrick | 0f65b31 | 2016-11-29 23:53:36 +0000 | [diff] [blame] | 250 | |
Brad Fitzpatrick | 61efd71 | 2017-01-20 18:35:00 +0000 | [diff] [blame] | 251 | func TestScanIdentifier(t *testing.T) { |
| 252 | tests := []struct { |
| 253 | in, want string |
| 254 | }{ |
| 255 | {"foo bar", "foo"}, |
| 256 | {"foo/bar", "foo"}, |
| 257 | {" foo", ""}, |
| 258 | {"фоо", "фоо"}, |
| 259 | {"f123", "f123"}, |
| 260 | {"123f", ""}, |
| 261 | } |
| 262 | for _, tt := range tests { |
| 263 | got := scanIdentifier([]byte(tt.in)) |
| 264 | if string(got) != tt.want { |
| 265 | t.Errorf("scanIdentifier(%q) = %q; want %q", tt.in, got, tt.want) |
| 266 | } |
Brad Fitzpatrick | 0f65b31 | 2016-11-29 23:53:36 +0000 | [diff] [blame] | 267 | } |
| 268 | } |
Jay Conrod | 00f7cd5 | 2017-02-07 17:34:43 -0500 | [diff] [blame] | 269 | |
| 270 | func TestReplaceLeadingIndentation(t *testing.T) { |
| 271 | oldIndent := strings.Repeat(" ", 2) |
| 272 | newIndent := strings.Repeat(" ", 4) |
| 273 | tests := []struct { |
| 274 | src, want string |
| 275 | }{ |
| 276 | {" foo\n bar\n baz", " foo\n bar\n baz"}, |
| 277 | {" '`'\n '`'\n", " '`'\n '`'\n"}, |
| 278 | {" '\\''\n '`'\n", " '\\''\n '`'\n"}, |
| 279 | {" \"`\"\n \"`\"\n", " \"`\"\n \"`\"\n"}, |
| 280 | {" `foo\n bar`", " `foo\n bar`"}, |
| 281 | {" `foo\\`\n bar", " `foo\\`\n bar"}, |
| 282 | {" '\\`'`foo\n bar", " '\\`'`foo\n bar"}, |
| 283 | { |
| 284 | " if true {\n foo := `One\n \tTwo\nThree`\n }\n", |
| 285 | " if true {\n foo := `One\n \tTwo\n Three`\n }\n", |
| 286 | }, |
| 287 | } |
| 288 | for _, tc := range tests { |
| 289 | if got := replaceLeadingIndentation(tc.src, oldIndent, newIndent); got != tc.want { |
| 290 | t.Errorf("replaceLeadingIndentation:\n%v\n---\nhave:\n%v\n---\nwant:\n%v\n", |
| 291 | tc.src, got, tc.want) |
| 292 | } |
| 293 | } |
| 294 | } |
Sina Siadat | 5128de7 | 2016-09-16 17:12:50 +0430 | [diff] [blame] | 295 | |
| 296 | func TestSrcBreadcrumbFunc(t *testing.T) { |
| 297 | for _, tc := range []struct { |
| 298 | path string |
| 299 | want string |
| 300 | }{ |
| 301 | {"src/", `<span class="text-muted">src/</span>`}, |
| 302 | {"src/fmt/", `<a href="/src">src</a>/<span class="text-muted">fmt/</span>`}, |
| 303 | {"src/fmt/print.go", `<a href="/src">src</a>/<a href="/src/fmt">fmt</a>/<span class="text-muted">print.go</span>`}, |
| 304 | } { |
| 305 | if got := srcBreadcrumbFunc(tc.path); got != tc.want { |
| 306 | t.Errorf("srcBreadcrumbFunc(%v) = %v; want %v", tc.path, got, tc.want) |
| 307 | } |
| 308 | } |
| 309 | } |
| 310 | |
| 311 | func TestSrcToPkgLinkFunc(t *testing.T) { |
| 312 | for _, tc := range []struct { |
| 313 | path string |
| 314 | want string |
| 315 | }{ |
| 316 | {"src/", `<a href="/pkg">Index</a>`}, |
| 317 | {"src/fmt/", `<a href="/pkg/fmt">fmt</a>`}, |
Andrew Bonventre | a237aba | 2017-08-03 18:01:02 -0400 | [diff] [blame] | 318 | {"pkg/", `<a href="/pkg">Index</a>`}, |
| 319 | {"pkg/LICENSE", `<a href="/pkg">Index</a>`}, |
Sina Siadat | 5128de7 | 2016-09-16 17:12:50 +0430 | [diff] [blame] | 320 | } { |
| 321 | if got := srcToPkgLinkFunc(tc.path); got != tc.want { |
| 322 | t.Errorf("srcToPkgLinkFunc(%v) = %v; want %v", tc.path, got, tc.want) |
| 323 | } |
| 324 | } |
| 325 | } |
Mostyn Bramley-Moore | 9e9f7f6 | 2018-07-31 17:27:32 +0000 | [diff] [blame] | 326 | |
| 327 | func TestFilterOutBuildAnnotations(t *testing.T) { |
| 328 | // TODO: simplify this by using a multiline string once we stop |
| 329 | // using go vet from 1.10 on the build dashboard. |
| 330 | // https://golang.org/issue/26627 |
| 331 | src := []byte("// +build !foo\n" + |
| 332 | "// +build !anothertag\n" + |
| 333 | "\n" + |
| 334 | "// non-tag comment\n" + |
| 335 | "\n" + |
| 336 | "package foo\n" + |
| 337 | "\n" + |
| 338 | "func bar() int {\n" + |
| 339 | " return 42\n" + |
| 340 | "}\n") |
| 341 | |
| 342 | fset := token.NewFileSet() |
| 343 | af, err := parser.ParseFile(fset, "foo.go", src, parser.ParseComments) |
| 344 | if err != nil { |
| 345 | t.Fatal(err) |
| 346 | } |
| 347 | |
| 348 | var found bool |
| 349 | for _, cg := range af.Comments { |
| 350 | if strings.HasPrefix(cg.Text(), "+build ") { |
| 351 | found = true |
| 352 | break |
| 353 | } |
| 354 | } |
| 355 | if !found { |
| 356 | t.Errorf("TestFilterOutBuildAnnotations is broken: missing build tag in test input") |
| 357 | } |
| 358 | |
| 359 | found = false |
| 360 | for _, cg := range filterOutBuildAnnotations(af.Comments) { |
| 361 | if strings.HasPrefix(cg.Text(), "+build ") { |
| 362 | t.Errorf("filterOutBuildAnnotations failed to filter build tag") |
| 363 | } |
| 364 | |
| 365 | if strings.Contains(cg.Text(), "non-tag comment") { |
| 366 | found = true |
| 367 | } |
| 368 | } |
| 369 | if !found { |
| 370 | t.Errorf("filterOutBuildAnnotations should not remove non-build tag comment") |
| 371 | } |
| 372 | } |
aarzilli | c6fca02 | 2022-02-01 18:45:42 +0100 | [diff] [blame] | 373 | |
| 374 | func TestLinkifyGenerics(t *testing.T) { |
| 375 | if !typeparams.Enabled { |
| 376 | t.Skip("type params are not enabled at this Go version") |
| 377 | } |
| 378 | |
| 379 | got := linkifySource(t, []byte(` |
| 380 | package foo |
| 381 | |
| 382 | type T struct { |
| 383 | field *T |
| 384 | } |
| 385 | |
| 386 | type ParametricStruct[T any] struct { |
| 387 | field *T |
| 388 | } |
| 389 | |
| 390 | func F1[T any](arg T) { } |
| 391 | |
| 392 | func F2(arg T) { } |
| 393 | |
| 394 | func (*ParametricStruct[T]) M(arg T) { } |
| 395 | |
| 396 | func (*T) M(arg T) { } |
| 397 | |
| 398 | type ParametricStruct2[T1, T2 any] struct { |
| 399 | a T1 |
| 400 | b T2 |
| 401 | } |
| 402 | |
| 403 | func (*ParametricStruct2[T1, T2]) M(a T1, b T2) { } |
| 404 | |
| 405 | |
| 406 | `)) |
| 407 | |
| 408 | want := `type T struct { |
| 409 | <span id="T.field"></span>field *<a href="#T">T</a> |
| 410 | } |
| 411 | type ParametricStruct[T <a href="/pkg/builtin/#any">any</a>] struct { |
| 412 | <span id="ParametricStruct.field"></span>field *T |
| 413 | } |
| 414 | func F1[T <a href="/pkg/builtin/#any">any</a>](arg T) {} |
| 415 | func F2(arg <a href="#T">T</a>) {} |
| 416 | func (*<a href="#ParametricStruct">ParametricStruct</a>[T]) M(arg T) {} |
| 417 | func (*<a href="#T">T</a>) M(arg <a href="#T">T</a>) {} |
| 418 | type ParametricStruct2[T1, T2 <a href="/pkg/builtin/#any">any</a>] struct { |
| 419 | <span id="ParametricStruct2.a"></span>a T1 |
| 420 | <span id="ParametricStruct2.b"></span>b T2 |
| 421 | } |
| 422 | func (*<a href="#ParametricStruct2">ParametricStruct2</a>[T1, T2]) M(a T1, b T2) {}` |
| 423 | |
| 424 | if got != want { |
| 425 | t.Errorf("got: %s\n\nwant: %s\n", got, want) |
| 426 | } |
| 427 | } |