• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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