1use std::collections::HashSet;
2
3use wesl_macros::query;
4use wgsl_parse::SyntaxNode;
5use wgsl_parse::syntax::{
6 Expression, ExpressionNode, FunctionCall, GlobalDeclaration, Ident, ImportContent,
7 TranslationUnit, TypeExpression,
8};
9use wgsl_types::idents::{BUILTIN_CONSTRUCTOR_NAMES, BUILTIN_FUNCTION_NAMES};
10
11use crate::{
12 error::{Diagnostic, Error, ValidateError},
13 idents::builtin_ident,
14 pass::Visit,
15};
16
17type E = ValidateError;
18
19fn check_defined_symbols(module: &TranslationUnit) -> Result<(), Diagnostic<Error>> {
25 fn check_ty(ty: &TypeExpression) -> Result<(), Diagnostic<Error>> {
26 if ty.path.is_none()
27 && ty.ident.use_count() == 1
28 && builtin_ident(&ty.ident.name()).is_none()
29 && *ty.ident.name() != "_"
31 {
32 Err(E::UndefinedSymbol(ty.ident.to_string()).into())
33 } else {
34 for arg in ty.template_args.iter().flatten() {
35 check_expr(&arg.expression)?;
36 }
37 Ok(())
38 }
39 }
40 fn check_expr(expr: &ExpressionNode) -> Result<(), Diagnostic<Error>> {
41 if let Expression::TypeOrIdentifier(ty) = expr.node() {
42 check_ty(ty).map_err(|d| d.with_span(expr.span()))
43 } else if let Expression::FunctionCall(call) = expr.node() {
44 check_ty(&call.ty).map_err(|d| d.with_span(expr.span()))?;
45 for expr in &call.arguments {
46 check_expr(expr)?;
47 }
48 Ok(())
49 } else {
50 for expr in Visit::<ExpressionNode>::visit(expr.node()) {
51 check_expr(expr)?;
52 }
53 Ok(())
54 }
55 }
56 fn check_decl(decl: &GlobalDeclaration) -> Result<(), Diagnostic<Error>> {
57 let decl_name = decl.ident().map(|ident| ident.name().to_string());
58 for expr in Visit::<ExpressionNode>::visit(decl) {
59 check_expr(expr).map_err(|mut d| {
60 d.detail.declaration = decl_name.clone();
61 d
62 })?;
63 }
64
65 for ty in query!(decl.{
68 GlobalDeclaration::Declaration.ty.[],
69 GlobalDeclaration::TypeAlias.ty,
70 GlobalDeclaration::Struct.members.[].ty,
71 GlobalDeclaration::Function.{ parameters.[].ty, return_type.[] }
72 }) {
73 check_ty(ty).map_err(|mut d| {
74 d.detail.declaration = decl_name.clone();
75 d
76 })?;
77 }
78 Ok(())
79 }
80
81 for decl in &module.global_declarations {
82 check_decl(decl)?;
83 }
84 Ok(())
85}
86
87fn check_function_calls(module: &TranslationUnit) -> Result<(), Diagnostic<Error>> {
88 fn check_call(call: &FunctionCall, ident: &Ident, module: &TranslationUnit) -> Result<(), E> {
89 let decl = module
90 .global_declarations
91 .iter()
92 .find(|decl| decl.ident().is_some_and(|id| &id == ident))
93 .map(|decl| decl.node());
94
95 match decl {
96 Some(GlobalDeclaration::Function(decl)) => {
97 if call.arguments.len() != decl.parameters.len() {
98 return Err(E::ParamCount(
99 ident.to_string(),
100 decl.parameters.len(),
101 call.arguments.len(),
102 ));
103 }
104 }
105 Some(GlobalDeclaration::Struct(decl)) => {
106 if call.arguments.len() != decl.members.len() && !call.arguments.is_empty() {
107 return Err(E::ParamCount(
108 ident.to_string(),
109 decl.members.len(),
110 call.arguments.len(),
111 ));
112 }
113 }
114 Some(GlobalDeclaration::TypeAlias(decl)) => {
115 if decl.ty.template_args.is_some() {
116 } else {
118 check_call(call, &decl.ty.ident, module)?;
119 }
120 }
121 Some(_) => return Err(E::NotCallable(ident.to_string())),
122 None => {
123 if BUILTIN_FUNCTION_NAMES
124 .iter()
125 .any(|name| name == &*ident.name())
126 {
127 } else if BUILTIN_CONSTRUCTOR_NAMES
129 .iter()
130 .any(|name| name == &*ident.name())
131 {
132 } else {
134 return Err(E::NotCallable(ident.to_string()));
136 }
137 }
138 };
139 Ok(())
140 }
141 fn check_expr(expr: &Expression, module: &TranslationUnit) -> Result<(), E> {
142 if let Expression::FunctionCall(call) = expr {
143 check_call(call, &call.ty.ident, module)?;
144 }
145 Ok(())
146 }
147 for decl in &module.global_declarations {
148 for expr in Visit::<ExpressionNode>::visit(decl.node()) {
149 check_expr(expr, module).map_err(|e| {
150 let mut err = Diagnostic::from(e);
151 err.detail.span = Some(expr.span());
152 err.detail.declaration = decl.ident().map(|id| id.name().to_string());
153 err
154 })?;
155 }
156 }
157 Ok(())
158}
159
160fn check_duplicate_decl(module: &TranslationUnit) -> Result<(), Diagnostic<Error>> {
161 let mut unique = HashSet::new();
162
163 fn check_ident(id: &Ident, unique: &mut HashSet<String>) -> Result<(), Diagnostic<Error>> {
164 if unique.contains(id.name().as_str()) {
165 Err(Diagnostic::from(E::Duplicate(id.to_string())).with_declaration(id.to_string()))
166 } else {
167 unique.insert(id.to_string());
168 Ok(())
169 }
170 }
171
172 fn check_import_content(
173 cont: &ImportContent,
174 unique: &mut HashSet<String>,
175 ) -> Result<(), Diagnostic<Error>> {
176 match cont {
177 ImportContent::Item(item) => {
178 let id = item.rename.as_ref().unwrap_or(&item.ident);
179 check_ident(id, unique)?;
180 }
181 ImportContent::Collection(coll) => {
182 for item in coll {
183 check_import_content(&item.content, unique)?;
184 }
185 }
186 }
187 Ok(())
188 }
189
190 for import in &module.imports {
191 if import
192 .attributes()
193 .iter()
194 .any(|attr| attr.is_if() || attr.is_elif() || attr.is_else())
195 {
196 continue;
198 }
199 check_import_content(&import.content, &mut unique)?;
200 }
201
202 for decl in &module.global_declarations {
203 if decl
204 .attributes()
205 .iter()
206 .any(|attr| attr.is_if() || attr.is_elif() || attr.is_else())
207 {
208 continue;
210 }
211 if let Some(id) = decl.ident() {
212 check_ident(&id, &mut unique)?;
213 }
214 }
215 Ok(())
216}
217
218fn check_cycles(module: &TranslationUnit) -> Result<(), Diagnostic<Error>> {
219 fn check_decl(
220 id: &Ident,
221 decl: &GlobalDeclaration,
222 unique: &mut HashSet<Ident>,
223 module: &TranslationUnit,
224 ) -> Result<(), E> {
225 let mut res = Ok(());
226
227 Visit::<TypeExpression>::visit_rec(decl, &mut |ty| {
228 if res.is_ok() {
229 if ty.ident == *id {
230 res = Err(E::Cycle(id.to_string(), decl.ident().unwrap().to_string()));
231 } else if unique.insert(ty.ident.clone())
232 && let Some(decl) = module
233 .global_declarations
234 .iter()
235 .find(|decl| decl.ident().as_ref() == Some(&ty.ident))
236 {
237 res = check_decl(id, decl, unique, module);
238 }
239 }
240 });
241
242 res
243 }
244 for decl in &module.global_declarations {
245 if let Some(id) = decl.ident() {
246 let mut unique = HashSet::new();
247 check_decl(&id, decl, &mut unique, module)
248 .map_err(|e| Diagnostic::from(e).with_declaration(id.to_string()))?;
249 }
250 }
251 Ok(())
252}
253
254pub fn validate_wesl(module: &TranslationUnit) -> Result<(), Diagnostic<Error>> {
266 check_defined_symbols(module)?;
267 check_duplicate_decl(module)?;
268 check_cycles(module)?;
269 Ok(())
270}
271
272pub fn validate_wgsl(module: &TranslationUnit) -> Result<(), Diagnostic<Error>> {
281 check_defined_symbols(module)?;
282 check_duplicate_decl(module)?;
283 check_cycles(module)?;
284 check_function_calls(module)?;
285 Ok(())
286}