1 // Copyright 2021 The Tint Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include <string>
16
17 #include "gtest/gtest.h"
18
19 #include "fuzzers/tint_ast_fuzzer/mutations/replace_identifier.h"
20 #include "fuzzers/tint_ast_fuzzer/mutator.h"
21 #include "fuzzers/tint_ast_fuzzer/probability_context.h"
22
23 #include "fuzzers/tint_ast_fuzzer/node_id_map.h"
24
25 #include "src/ast/call_statement.h"
26 #include "src/program_builder.h"
27 #include "src/reader/wgsl/parser.h"
28 #include "src/writer/wgsl/generator.h"
29
30 namespace tint {
31 namespace fuzzers {
32 namespace ast_fuzzer {
33 namespace {
34
TEST(ReplaceIdentifierTest,NotApplicable_Simple)35 TEST(ReplaceIdentifierTest, NotApplicable_Simple) {
36 std::string content = R"(
37 fn main() {
38 let a = 5;
39 let c = 6;
40 let b = a + 5;
41
42 let d = vec2<i32>(1, 2);
43 let e = d.x;
44 }
45 )";
46 Source::File file("test.wgsl", content);
47 auto program = reader::wgsl::Parse(&file);
48 ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
49
50 NodeIdMap node_id_map(program);
51
52 const auto& main_fn_stmts = program.AST().Functions()[0]->body->statements;
53
54 const auto* a_var =
55 main_fn_stmts[0]->As<ast::VariableDeclStatement>()->variable;
56 ASSERT_NE(a_var, nullptr);
57
58 const auto* b_var =
59 main_fn_stmts[2]->As<ast::VariableDeclStatement>()->variable;
60 ASSERT_NE(b_var, nullptr);
61
62 const auto* e_var =
63 main_fn_stmts[4]->As<ast::VariableDeclStatement>()->variable;
64 ASSERT_NE(e_var, nullptr);
65
66 auto a_var_id = node_id_map.GetId(a_var);
67 ASSERT_NE(a_var_id, 0);
68
69 auto b_var_id = node_id_map.GetId(b_var);
70 ASSERT_NE(b_var_id, 0);
71
72 const auto* sum_expr = b_var->constructor->As<ast::BinaryExpression>();
73 ASSERT_NE(sum_expr, nullptr);
74
75 auto a_ident_id = node_id_map.GetId(sum_expr->lhs);
76 ASSERT_NE(a_ident_id, 0);
77
78 auto sum_expr_id = node_id_map.GetId(sum_expr);
79 ASSERT_NE(sum_expr_id, 0);
80
81 auto e_var_id = node_id_map.GetId(e_var);
82 ASSERT_NE(e_var_id, 0);
83
84 auto vec_member_access_id = node_id_map.GetId(
85 e_var->constructor->As<ast::MemberAccessorExpression>()->member);
86 ASSERT_NE(vec_member_access_id, 0);
87
88 // use_id is invalid.
89 EXPECT_FALSE(MutationReplaceIdentifier(0, a_var_id)
90 .IsApplicable(program, node_id_map));
91
92 // use_id is not an identifier expression.
93 EXPECT_FALSE(MutationReplaceIdentifier(sum_expr_id, a_var_id)
94 .IsApplicable(program, node_id_map));
95
96 // use_id is an identifier but not a variable user.
97 EXPECT_FALSE(MutationReplaceIdentifier(vec_member_access_id, a_var_id)
98 .IsApplicable(program, node_id_map));
99
100 // replacement_id is invalid.
101 EXPECT_FALSE(MutationReplaceIdentifier(a_ident_id, 0)
102 .IsApplicable(program, node_id_map));
103
104 // replacement_id is not a variable.
105 EXPECT_FALSE(MutationReplaceIdentifier(a_ident_id, sum_expr_id)
106 .IsApplicable(program, node_id_map));
107
108 // Can't replace a variable with itself.
109 EXPECT_FALSE(MutationReplaceIdentifier(a_ident_id, a_var_id)
110 .IsApplicable(program, node_id_map));
111
112 // Replacement is not in scope.
113 EXPECT_FALSE(MutationReplaceIdentifier(a_ident_id, b_var_id)
114 .IsApplicable(program, node_id_map));
115 EXPECT_FALSE(MutationReplaceIdentifier(a_ident_id, e_var_id)
116 .IsApplicable(program, node_id_map));
117 }
118
TEST(ReplaceIdentifierTest,GlobalVarNotInScope)119 TEST(ReplaceIdentifierTest, GlobalVarNotInScope) {
120 // Can't use the global variable if it's not in scope.
121 std::string shader = R"(
122 var<private> a: i32;
123
124 fn f() {
125 a = 3;
126 }
127
128 var<private> b: i32;
129 )";
130 Source::File file("test.wgsl", shader);
131 auto program = reader::wgsl::Parse(&file);
132 ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
133
134 NodeIdMap node_id_map(program);
135
136 auto use_id = node_id_map.GetId(program.AST()
137 .Functions()[0]
138 ->body->statements[0]
139 ->As<ast::AssignmentStatement>()
140 ->lhs);
141 ASSERT_NE(use_id, 0);
142
143 auto replacement_id = node_id_map.GetId(program.AST().GlobalVariables()[1]);
144 ASSERT_NE(replacement_id, 0);
145
146 ASSERT_FALSE(MutationReplaceIdentifier(use_id, replacement_id)
147 .IsApplicable(program, node_id_map));
148 }
149
TEST(ReplaceIdentifierTest,NotApplicable1)150 TEST(ReplaceIdentifierTest, NotApplicable1) {
151 // Can't replace `a` with `b` since the store type is wrong (the same storage
152 // class though).
153 std::string shader = R"(
154 var<private> a: i32;
155 var<private> b: u32;
156 fn f() {
157 *&a = 4;
158 }
159 )";
160 Source::File file("test.wgsl", shader);
161 auto program = reader::wgsl::Parse(&file);
162 ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
163
164 NodeIdMap node_id_map(program);
165
166 auto replacement_id = node_id_map.GetId(program.AST().GlobalVariables()[1]);
167 ASSERT_NE(replacement_id, 0);
168
169 auto use_id = node_id_map.GetId(program.AST()
170 .Functions()[0]
171 ->body->statements[0]
172 ->As<ast::AssignmentStatement>()
173 ->lhs->As<ast::UnaryOpExpression>()
174 ->expr->As<ast::UnaryOpExpression>()
175 ->expr);
176 ASSERT_NE(use_id, 0);
177
178 ASSERT_FALSE(MutationReplaceIdentifier(use_id, replacement_id)
179 .IsApplicable(program, node_id_map));
180 }
181
TEST(ReplaceIdentifierTest,NotApplicable2)182 TEST(ReplaceIdentifierTest, NotApplicable2) {
183 // Can't replace `a` with `b` since the store type is wrong (the storage
184 // class is different though).
185 std::string shader = R"(
186 var<private> a: i32;
187 fn f() {
188 var b: u32;
189 *&a = 4;
190 }
191 )";
192 Source::File file("test.wgsl", shader);
193 auto program = reader::wgsl::Parse(&file);
194 ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
195
196 NodeIdMap node_id_map(program);
197
198 auto replacement_id = node_id_map.GetId(program.AST()
199 .Functions()[0]
200 ->body->statements[0]
201 ->As<ast::VariableDeclStatement>()
202 ->variable);
203 ASSERT_NE(replacement_id, 0);
204
205 auto use_id = node_id_map.GetId(program.AST()
206 .Functions()[0]
207 ->body->statements[1]
208 ->As<ast::AssignmentStatement>()
209 ->lhs->As<ast::UnaryOpExpression>()
210 ->expr->As<ast::UnaryOpExpression>()
211 ->expr);
212 ASSERT_NE(use_id, 0);
213
214 ASSERT_FALSE(MutationReplaceIdentifier(use_id, replacement_id)
215 .IsApplicable(program, node_id_map));
216 }
217
TEST(ReplaceIdentifierTest,NotApplicable3)218 TEST(ReplaceIdentifierTest, NotApplicable3) {
219 // Can't replace `a` with `b` since the latter is not a reference (the store
220 // type is the same, though).
221 std::string shader = R"(
222 var<private> a: i32;
223 fn f() {
224 let b = 45;
225 *&a = 4;
226 }
227 )";
228 Source::File file("test.wgsl", shader);
229 auto program = reader::wgsl::Parse(&file);
230 ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
231
232 NodeIdMap node_id_map(program);
233
234 auto replacement_id = node_id_map.GetId(program.AST()
235 .Functions()[0]
236 ->body->statements[0]
237 ->As<ast::VariableDeclStatement>()
238 ->variable);
239 ASSERT_NE(replacement_id, 0);
240
241 auto use_id = node_id_map.GetId(program.AST()
242 .Functions()[0]
243 ->body->statements[1]
244 ->As<ast::AssignmentStatement>()
245 ->lhs->As<ast::UnaryOpExpression>()
246 ->expr->As<ast::UnaryOpExpression>()
247 ->expr);
248 ASSERT_NE(use_id, 0);
249
250 ASSERT_FALSE(MutationReplaceIdentifier(use_id, replacement_id)
251 .IsApplicable(program, node_id_map));
252 }
253
TEST(ReplaceIdentifierTest,NotApplicable4)254 TEST(ReplaceIdentifierTest, NotApplicable4) {
255 // Can't replace `a` with `b` since the latter is not a reference (the store
256 // type is the same, though).
257 std::string shader = R"(
258 var<private> a: i32;
259 fn f(b: i32) {
260 *&a = 4;
261 }
262 )";
263 Source::File file("test.wgsl", shader);
264 auto program = reader::wgsl::Parse(&file);
265 ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
266
267 NodeIdMap node_id_map(program);
268
269 auto replacement_id =
270 node_id_map.GetId(program.AST().Functions()[0]->params[0]);
271 ASSERT_NE(replacement_id, 0);
272
273 auto use_id = node_id_map.GetId(program.AST()
274 .Functions()[0]
275 ->body->statements[0]
276 ->As<ast::AssignmentStatement>()
277 ->lhs->As<ast::UnaryOpExpression>()
278 ->expr->As<ast::UnaryOpExpression>()
279 ->expr);
280 ASSERT_NE(use_id, 0);
281
282 ASSERT_FALSE(MutationReplaceIdentifier(use_id, replacement_id)
283 .IsApplicable(program, node_id_map));
284 }
285
TEST(ReplaceIdentifierTest,NotApplicable5)286 TEST(ReplaceIdentifierTest, NotApplicable5) {
287 // Can't replace `a` with `b` since the latter has a wrong access mode
288 // (`read` for uniform storage class).
289 std::string shader = R"(
290 [[block]]
291 struct S {
292 a: i32;
293 };
294
295 var<private> a: S;
296 [[group(1), binding(1)]] var<uniform> b: S;
297 fn f() {
298 *&a = S(4);
299 }
300 )";
301 Source::File file("test.wgsl", shader);
302 auto program = reader::wgsl::Parse(&file);
303 ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
304
305 NodeIdMap node_id_map(program);
306
307 auto replacement_id = node_id_map.GetId(program.AST().GlobalVariables()[1]);
308 ASSERT_NE(replacement_id, 0);
309
310 auto use_id = node_id_map.GetId(program.AST()
311 .Functions()[0]
312 ->body->statements[0]
313 ->As<ast::AssignmentStatement>()
314 ->lhs->As<ast::UnaryOpExpression>()
315 ->expr->As<ast::UnaryOpExpression>()
316 ->expr);
317 ASSERT_NE(use_id, 0);
318
319 ASSERT_FALSE(MutationReplaceIdentifier(use_id, replacement_id)
320 .IsApplicable(program, node_id_map));
321 }
322
TEST(ReplaceIdentifierTest,NotApplicable6)323 TEST(ReplaceIdentifierTest, NotApplicable6) {
324 // Can't replace `ptr_b` with `a` since the latter is not a pointer.
325 std::string shader = R"(
326 [[block]]
327 struct S {
328 a: i32;
329 };
330
331 var<private> a: S;
332 [[group(1), binding(1)]] var<uniform> b: S;
333 fn f() {
334 let ptr_b = &b;
335 *&a = *ptr_b;
336 }
337 )";
338 Source::File file("test.wgsl", shader);
339 auto program = reader::wgsl::Parse(&file);
340 ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
341
342 NodeIdMap node_id_map(program);
343
344 auto replacement_id = node_id_map.GetId(program.AST().GlobalVariables()[0]);
345 ASSERT_NE(replacement_id, 0);
346
347 auto use_id = node_id_map.GetId(program.AST()
348 .Functions()[0]
349 ->body->statements[1]
350 ->As<ast::AssignmentStatement>()
351 ->rhs->As<ast::UnaryOpExpression>()
352 ->expr);
353 ASSERT_NE(use_id, 0);
354
355 ASSERT_FALSE(MutationReplaceIdentifier(use_id, replacement_id)
356 .IsApplicable(program, node_id_map));
357 }
358
TEST(ReplaceIdentifierTest,NotApplicable8)359 TEST(ReplaceIdentifierTest, NotApplicable8) {
360 // Can't replace `ptr_b` with `c` since the latter has a wrong access mode and
361 // storage class.
362 std::string shader = R"(
363 [[block]]
364 struct S {
365 a: i32;
366 };
367
368 var<private> a: S;
369 [[group(1), binding(1)]] var<uniform> b: S;
370 [[group(1), binding(2)]] var<storage, write> c: S;
371 fn f() {
372 let ptr_b = &b;
373 *&a = *ptr_b;
374 }
375 )";
376 Source::File file("test.wgsl", shader);
377 auto program = reader::wgsl::Parse(&file);
378 ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
379
380 NodeIdMap node_id_map(program);
381
382 auto replacement_id = node_id_map.GetId(program.AST().GlobalVariables()[2]);
383 ASSERT_NE(replacement_id, 0);
384
385 auto use_id = node_id_map.GetId(program.AST()
386 .Functions()[0]
387 ->body->statements[1]
388 ->As<ast::AssignmentStatement>()
389 ->rhs->As<ast::UnaryOpExpression>()
390 ->expr);
391 ASSERT_NE(use_id, 0);
392
393 ASSERT_FALSE(MutationReplaceIdentifier(use_id, replacement_id)
394 .IsApplicable(program, node_id_map));
395 }
396
TEST(ReplaceIdentifierTest,NotApplicable9)397 TEST(ReplaceIdentifierTest, NotApplicable9) {
398 // Can't replace `b` with `e` since the latter is not a reference.
399 std::string shader = R"(
400 [[block]]
401 struct S {
402 a: i32;
403 };
404
405 var<private> a: S;
406 let e = 3;
407 [[group(1), binding(1)]] var<uniform> b: S;
408 fn f() {
409 *&a = *&b;
410 }
411 )";
412 Source::File file("test.wgsl", shader);
413 auto program = reader::wgsl::Parse(&file);
414 ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
415
416 NodeIdMap node_id_map(program);
417
418 auto replacement_id = node_id_map.GetId(program.AST().GlobalVariables()[1]);
419 ASSERT_NE(replacement_id, 0);
420
421 auto use_id = node_id_map.GetId(program.AST()
422 .Functions()[0]
423 ->body->statements[0]
424 ->As<ast::AssignmentStatement>()
425 ->rhs->As<ast::UnaryOpExpression>()
426 ->expr->As<ast::UnaryOpExpression>()
427 ->expr);
428 ASSERT_NE(use_id, 0);
429
430 ASSERT_FALSE(MutationReplaceIdentifier(use_id, replacement_id)
431 .IsApplicable(program, node_id_map));
432 }
433
TEST(ReplaceIdentifierTest,NotApplicable10)434 TEST(ReplaceIdentifierTest, NotApplicable10) {
435 // Can't replace `b` with `e` since the latter has a wrong access mode.
436 std::string shader = R"(
437 [[block]]
438 struct S {
439 a: i32;
440 };
441
442 var<private> a: S;
443 [[group(0), binding(0)]] var<storage, write> e: S;
444 [[group(1), binding(1)]] var<uniform> b: S;
445 fn f() {
446 *&a = *&b;
447 }
448 )";
449 Source::File file("test.wgsl", shader);
450 auto program = reader::wgsl::Parse(&file);
451 ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
452
453 NodeIdMap node_id_map(program);
454
455 auto replacement_id = node_id_map.GetId(program.AST().GlobalVariables()[1]);
456 ASSERT_NE(replacement_id, 0);
457
458 auto use_id = node_id_map.GetId(program.AST()
459 .Functions()[0]
460 ->body->statements[0]
461 ->As<ast::AssignmentStatement>()
462 ->rhs->As<ast::UnaryOpExpression>()
463 ->expr->As<ast::UnaryOpExpression>()
464 ->expr);
465 ASSERT_NE(use_id, 0);
466
467 ASSERT_FALSE(MutationReplaceIdentifier(use_id, replacement_id)
468 .IsApplicable(program, node_id_map));
469 }
470
TEST(ReplaceIdentifierTest,Applicable1)471 TEST(ReplaceIdentifierTest, Applicable1) {
472 // Can replace `a` with `b` (same storage class).
473 std::string shader = R"(
474 fn f() {
475 var b : vec2<u32>;
476 var a = vec2<u32>(34u, 45u);
477 (*&a)[1] = 3u;
478 }
479 )";
480 Source::File file("test.wgsl", shader);
481 auto program = reader::wgsl::Parse(&file);
482 ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
483
484 NodeIdMap node_id_map(program);
485
486 auto use_id = node_id_map.GetId(program.AST()
487 .Functions()[0]
488 ->body->statements[2]
489 ->As<ast::AssignmentStatement>()
490 ->lhs->As<ast::IndexAccessorExpression>()
491 ->object->As<ast::UnaryOpExpression>()
492 ->expr->As<ast::UnaryOpExpression>()
493 ->expr);
494 ASSERT_NE(use_id, 0);
495
496 auto replacement_id = node_id_map.GetId(program.AST()
497 .Functions()[0]
498 ->body->statements[0]
499 ->As<ast::VariableDeclStatement>()
500 ->variable);
501 ASSERT_NE(replacement_id, 0);
502
503 ASSERT_TRUE(MaybeApplyMutation(
504 program, MutationReplaceIdentifier(use_id, replacement_id), node_id_map,
505 &program, &node_id_map, nullptr));
506 ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
507
508 writer::wgsl::Options options;
509 auto result = writer::wgsl::Generate(&program, options);
510 ASSERT_TRUE(result.success) << result.error;
511
512 std::string expected_shader = R"(fn f() {
513 var b : vec2<u32>;
514 var a = vec2<u32>(34u, 45u);
515 (*(&(b)))[1] = 3u;
516 }
517 )";
518 ASSERT_EQ(expected_shader, result.wgsl);
519 }
520
TEST(ReplaceIdentifierTest,Applicable2)521 TEST(ReplaceIdentifierTest, Applicable2) {
522 // Can replace `ptr_a` with `b` - the function parameter.
523 std::string shader = R"(
524 fn f(b: ptr<function, vec2<u32>>) {
525 var a = vec2<u32>(34u, 45u);
526 let ptr_a = &a;
527 (*ptr_a)[1] = 3u;
528 }
529 )";
530 Source::File file("test.wgsl", shader);
531 auto program = reader::wgsl::Parse(&file);
532 ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
533
534 NodeIdMap node_id_map(program);
535
536 auto use_id = node_id_map.GetId(program.AST()
537 .Functions()[0]
538 ->body->statements[2]
539 ->As<ast::AssignmentStatement>()
540 ->lhs->As<ast::IndexAccessorExpression>()
541 ->object->As<ast::UnaryOpExpression>()
542 ->expr);
543 ASSERT_NE(use_id, 0);
544
545 auto replacement_id =
546 node_id_map.GetId(program.AST().Functions()[0]->params[0]);
547 ASSERT_NE(replacement_id, 0);
548
549 ASSERT_TRUE(MaybeApplyMutation(
550 program, MutationReplaceIdentifier(use_id, replacement_id), node_id_map,
551 &program, &node_id_map, nullptr));
552 ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
553
554 writer::wgsl::Options options;
555 auto result = writer::wgsl::Generate(&program, options);
556 ASSERT_TRUE(result.success) << result.error;
557
558 std::string expected_shader = R"(fn f(b : ptr<function, vec2<u32>>) {
559 var a = vec2<u32>(34u, 45u);
560 let ptr_a = &(a);
561 (*(b))[1] = 3u;
562 }
563 )";
564 ASSERT_EQ(expected_shader, result.wgsl);
565 }
566
TEST(ReplaceIdentifierTest,NotApplicable12)567 TEST(ReplaceIdentifierTest, NotApplicable12) {
568 // Can't replace `a` with `b` (both are references with different storage
569 // class).
570 std::string shader = R"(
571 var<private> b : vec2<u32>;
572 fn f() {
573 var a = vec2<u32>(34u, 45u);
574 (*&a)[1] = 3u;
575 }
576 )";
577 Source::File file("test.wgsl", shader);
578 auto program = reader::wgsl::Parse(&file);
579 ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
580
581 NodeIdMap node_id_map(program);
582
583 auto use_id = node_id_map.GetId(program.AST()
584 .Functions()[0]
585 ->body->statements[1]
586 ->As<ast::AssignmentStatement>()
587 ->lhs->As<ast::IndexAccessorExpression>()
588 ->object->As<ast::UnaryOpExpression>()
589 ->expr->As<ast::UnaryOpExpression>()
590 ->expr);
591 ASSERT_NE(use_id, 0);
592
593 auto replacement_id = node_id_map.GetId(program.AST().GlobalVariables()[0]);
594 ASSERT_NE(replacement_id, 0);
595
596 ASSERT_FALSE(MutationReplaceIdentifier(use_id, replacement_id)
597 .IsApplicable(program, node_id_map));
598 }
599
TEST(ReplaceIdentifierTest,NotApplicable13)600 TEST(ReplaceIdentifierTest, NotApplicable13) {
601 // Can't replace `a` with `b` (both are references with different storage
602 // class).
603 std::string shader = R"(
604 var<private> b : vec2<u32>;
605 fn f() {
606 var a = vec2<u32>(34u, 45u);
607 let c = (*&a)[1];
608 }
609 )";
610 Source::File file("test.wgsl", shader);
611 auto program = reader::wgsl::Parse(&file);
612 ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
613
614 NodeIdMap node_id_map(program);
615
616 auto use_id = node_id_map.GetId(
617 program.AST()
618 .Functions()[0]
619 ->body->statements[1]
620 ->As<ast::VariableDeclStatement>()
621 ->variable->constructor->As<ast::IndexAccessorExpression>()
622 ->object->As<ast::UnaryOpExpression>()
623 ->expr->As<ast::UnaryOpExpression>()
624 ->expr);
625 ASSERT_NE(use_id, 0);
626
627 auto replacement_id = node_id_map.GetId(program.AST().GlobalVariables()[0]);
628 ASSERT_NE(replacement_id, 0);
629
630 ASSERT_FALSE(MutationReplaceIdentifier(use_id, replacement_id)
631 .IsApplicable(program, node_id_map));
632 }
633
TEST(ReplaceIdentifierTest,NotApplicable14)634 TEST(ReplaceIdentifierTest, NotApplicable14) {
635 // Can't replace `ptr_a` with `ptr_b` (both are pointers with different
636 // storage class).
637 std::string shader = R"(
638 var<private> b: vec2<u32>;
639 fn f() {
640 var a = vec2<u32>(34u, 45u);
641 let ptr_a = &a;
642 let ptr_b = &b;
643 let c = (*ptr_a)[1];
644 }
645 )";
646 Source::File file("test.wgsl", shader);
647 auto program = reader::wgsl::Parse(&file);
648 ASSERT_TRUE(program.IsValid()) << program.Diagnostics().str();
649
650 NodeIdMap node_id_map(program);
651
652 auto use_id = node_id_map.GetId(
653 program.AST()
654 .Functions()[0]
655 ->body->statements[3]
656 ->As<ast::VariableDeclStatement>()
657 ->variable->constructor->As<ast::IndexAccessorExpression>()
658 ->object->As<ast::UnaryOpExpression>()
659 ->expr);
660 ASSERT_NE(use_id, 0);
661
662 auto replacement_id = node_id_map.GetId(program.AST()
663 .Functions()[0]
664 ->body->statements[2]
665 ->As<ast::VariableDeclStatement>()
666 ->variable);
667 ASSERT_NE(replacement_id, 0);
668 ASSERT_FALSE(MutationReplaceIdentifier(use_id, replacement_id)
669 .IsApplicable(program, node_id_map));
670 }
671
672 } // namespace
673 } // namespace ast_fuzzer
674 } // namespace fuzzers
675 } // namespace tint
676