1use std::borrow::Cow;
4use std::collections::HashMap;
5use std::collections::HashSet;
6use std::collections::hash_map::Entry;
7use std::path::Path;
8use std::sync::Arc;
9
10use indexmap::IndexMap;
11use petgraph::graph::NodeIndex;
12use rowan::GreenNode;
13use url::Url;
14use uuid::Uuid;
15use wdl_ast::Ast;
16use wdl_ast::AstNode;
17use wdl_ast::Diagnostic;
18use wdl_ast::Severity;
19use wdl_ast::Span;
20use wdl_ast::SupportedVersion;
21use wdl_ast::SyntaxNode;
22
23use crate::config::Config;
24use crate::diagnostics::no_common_type;
25use crate::diagnostics::unused_import;
26use crate::graph::DocumentGraph;
27use crate::graph::ParseState;
28use crate::types::CallType;
29use crate::types::Optional;
30use crate::types::Type;
31
32mod v1;
33
34pub const TASK_VAR_NAME: &str = "task";
37
38#[derive(Debug)]
40pub struct Namespace {
41 span: Span,
43 source: Arc<Url>,
45 document: Document,
47 used: bool,
49 excepted: bool,
52}
53
54impl Namespace {
55 pub fn span(&self) -> Span {
57 self.span
58 }
59
60 pub fn source(&self) -> &Arc<Url> {
62 &self.source
63 }
64
65 pub fn document(&self) -> &Document {
67 &self.document
68 }
69}
70
71#[derive(Debug, Clone)]
73pub struct Struct {
74 name: String,
76 name_span: Span,
81 offset: usize,
86 node: rowan::GreenNode,
90 namespace: Option<String>,
94 ty: Option<Type>,
98}
99
100impl Struct {
101 pub fn name(&self) -> &str {
103 &self.name
104 }
105
106 pub fn name_span(&self) -> Span {
108 self.name_span
109 }
110
111 pub fn offset(&self) -> usize {
113 self.offset
114 }
115
116 pub fn node(&self) -> &rowan::GreenNode {
118 &self.node
119 }
120
121 pub fn namespace(&self) -> Option<&str> {
126 self.namespace.as_deref()
127 }
128
129 pub fn ty(&self) -> Option<&Type> {
134 self.ty.as_ref()
135 }
136}
137
138#[derive(Debug, Clone)]
140pub struct Name {
141 span: Span,
143 ty: Type,
145}
146
147impl Name {
148 pub fn span(&self) -> Span {
150 self.span
151 }
152
153 pub fn ty(&self) -> &Type {
155 &self.ty
156 }
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
161pub struct ScopeIndex(usize);
162
163#[derive(Debug)]
165pub struct Scope {
166 parent: Option<ScopeIndex>,
170 span: Span,
172 names: IndexMap<String, Name>,
174}
175
176impl Scope {
177 fn new(parent: Option<ScopeIndex>, span: Span) -> Self {
179 Self {
180 parent,
181 span,
182 names: Default::default(),
183 }
184 }
185
186 pub fn insert(&mut self, name: impl Into<String>, span: Span, ty: Type) {
188 self.names.insert(name.into(), Name { span, ty });
189 }
190}
191
192#[derive(Debug, Clone, Copy)]
194pub struct ScopeRef<'a> {
195 scopes: &'a [Scope],
197 index: ScopeIndex,
199}
200
201impl<'a> ScopeRef<'a> {
202 fn new(scopes: &'a [Scope], index: ScopeIndex) -> Self {
204 Self { scopes, index }
205 }
206
207 pub fn span(&self) -> Span {
209 self.scopes[self.index.0].span
210 }
211
212 pub fn parent(&self) -> Option<Self> {
216 self.scopes[self.index.0].parent.map(|p| Self {
217 scopes: self.scopes,
218 index: p,
219 })
220 }
221
222 pub fn names(&self) -> impl Iterator<Item = (&str, &Name)> + use<'_> {
224 self.scopes[self.index.0]
225 .names
226 .iter()
227 .map(|(name, n)| (name.as_str(), n))
228 }
229
230 pub fn local(&self, name: &str) -> Option<&Name> {
234 self.scopes[self.index.0].names.get(name)
235 }
236
237 pub fn lookup(&self, name: &str) -> Option<&Name> {
241 let mut current = Some(self.index);
242
243 while let Some(index) = current {
244 if let Some(name) = self.scopes[index.0].names.get(name) {
245 return Some(name);
246 }
247
248 current = self.scopes[index.0].parent;
249 }
250
251 None
252 }
253}
254
255#[derive(Debug)]
257struct ScopeRefMut<'a> {
258 scopes: &'a mut [Scope],
260 index: ScopeIndex,
262}
263
264impl<'a> ScopeRefMut<'a> {
265 fn new(scopes: &'a mut [Scope], index: ScopeIndex) -> Self {
267 Self { scopes, index }
268 }
269
270 pub fn lookup(&self, name: &str) -> Option<&Name> {
274 let mut current = Some(self.index);
275
276 while let Some(index) = current {
277 if let Some(name) = self.scopes[index.0].names.get(name) {
278 return Some(name);
279 }
280
281 current = self.scopes[index.0].parent;
282 }
283
284 None
285 }
286
287 pub fn insert(&mut self, name: impl Into<String>, span: Span, ty: Type) {
289 self.scopes[self.index.0]
290 .names
291 .insert(name.into(), Name { span, ty });
292 }
293
294 pub fn as_scope_ref(&'a self) -> ScopeRef<'a> {
296 ScopeRef {
297 scopes: self.scopes,
298 index: self.index,
299 }
300 }
301}
302
303#[derive(Debug)]
308pub struct ScopeUnion<'a> {
309 scope_refs: Vec<(ScopeRef<'a>, bool)>,
311}
312
313impl<'a> ScopeUnion<'a> {
314 pub fn new() -> Self {
316 Self {
317 scope_refs: Vec::new(),
318 }
319 }
320
321 pub fn insert(&mut self, scope_ref: ScopeRef<'a>, exhaustive: bool) {
323 self.scope_refs.push((scope_ref, exhaustive));
324 }
325
326 pub fn resolve(self) -> Result<HashMap<String, Name>, Vec<Diagnostic>> {
331 let mut errors = Vec::new();
332 let mut ignored: HashSet<String> = HashSet::new();
333
334 let mut names: HashMap<String, Name> = HashMap::new();
336 for (scope_ref, _) in &self.scope_refs {
337 for (name, info) in scope_ref.names() {
338 if ignored.contains(name) {
339 continue;
340 }
341
342 match names.entry(name.to_string()) {
343 Entry::Vacant(entry) => {
344 entry.insert(info.clone());
345 }
346 Entry::Occupied(mut entry) => {
347 let Some(ty) = entry.get().ty.common_type(&info.ty) else {
348 errors.push(no_common_type(
349 &entry.get().ty,
350 entry.get().span,
351 &info.ty,
352 info.span,
353 ));
354 names.remove(name);
355 ignored.insert(name.to_string());
356 continue;
357 };
358
359 entry.get_mut().ty = ty;
360 }
361 }
362 }
363 }
364
365 for (scope_ref, _) in &self.scope_refs {
367 for (name, info) in &mut names {
368 if ignored.contains(name) {
369 continue;
370 }
371
372 if scope_ref.local(name).is_none() {
374 info.ty = info.ty.optional();
375 }
376 }
377 }
378
379 let has_exhaustive = self.scope_refs.iter().any(|(_, exhaustive)| *exhaustive);
381 if !has_exhaustive {
382 for info in names.values_mut() {
383 info.ty = info.ty.optional();
384 }
385 }
386
387 if !errors.is_empty() {
388 return Err(errors);
389 }
390
391 Ok(names)
392 }
393}
394
395#[derive(Debug, Clone, PartialEq, Eq)]
397pub struct Input {
398 ty: Type,
400 required: bool,
405}
406
407impl Input {
408 pub fn ty(&self) -> &Type {
410 &self.ty
411 }
412
413 pub fn required(&self) -> bool {
415 self.required
416 }
417}
418
419#[derive(Debug, Clone, PartialEq, Eq)]
421pub struct Output {
422 ty: Type,
424 name_span: Span,
426}
427
428impl Output {
429 pub(crate) fn new(ty: Type, name_span: Span) -> Self {
431 Self { ty, name_span }
432 }
433
434 pub fn ty(&self) -> &Type {
436 &self.ty
437 }
438
439 pub fn name_span(&self) -> Span {
441 self.name_span
442 }
443}
444
445#[derive(Debug)]
447pub struct Task {
448 name_span: Span,
450 name: String,
452 scopes: Vec<Scope>,
458 inputs: Arc<IndexMap<String, Input>>,
460 outputs: Arc<IndexMap<String, Output>>,
462}
463
464impl Task {
465 pub fn name(&self) -> &str {
467 &self.name
468 }
469
470 pub fn name_span(&self) -> Span {
472 self.name_span
473 }
474
475 pub fn scope(&self) -> ScopeRef<'_> {
477 ScopeRef::new(&self.scopes, ScopeIndex(0))
478 }
479
480 pub fn inputs(&self) -> &IndexMap<String, Input> {
482 &self.inputs
483 }
484
485 pub fn outputs(&self) -> &IndexMap<String, Output> {
487 &self.outputs
488 }
489}
490
491#[derive(Debug)]
493pub struct Workflow {
494 name_span: Span,
496 name: String,
498 scopes: Vec<Scope>,
504 inputs: Arc<IndexMap<String, Input>>,
506 outputs: Arc<IndexMap<String, Output>>,
508 calls: HashMap<String, CallType>,
510 allows_nested_inputs: bool,
512}
513
514impl Workflow {
515 pub fn name(&self) -> &str {
517 &self.name
518 }
519
520 pub fn name_span(&self) -> Span {
522 self.name_span
523 }
524
525 pub fn scope(&self) -> ScopeRef<'_> {
527 ScopeRef::new(&self.scopes, ScopeIndex(0))
528 }
529
530 pub fn inputs(&self) -> &IndexMap<String, Input> {
532 &self.inputs
533 }
534
535 pub fn outputs(&self) -> &IndexMap<String, Output> {
537 &self.outputs
538 }
539
540 pub fn calls(&self) -> &HashMap<String, CallType> {
542 &self.calls
543 }
544
545 pub fn allows_nested_inputs(&self) -> bool {
547 self.allows_nested_inputs
548 }
549}
550
551#[derive(Debug)]
553struct DocumentData {
554 config: Config,
556 root: Option<GreenNode>,
560 id: Arc<String>,
564 uri: Arc<Url>,
566 version: Option<SupportedVersion>,
568 namespaces: IndexMap<String, Namespace>,
570 tasks: IndexMap<String, Task>,
572 workflow: Option<Workflow>,
574 structs: IndexMap<String, Struct>,
576 parse_diagnostics: Vec<Diagnostic>,
578 analysis_diagnostics: Vec<Diagnostic>,
580}
581
582impl DocumentData {
583 fn new(
585 config: Config,
586 uri: Arc<Url>,
587 root: Option<GreenNode>,
588 version: Option<SupportedVersion>,
589 diagnostics: Vec<Diagnostic>,
590 ) -> Self {
591 Self {
592 config,
593 root,
594 id: Uuid::new_v4().to_string().into(),
595 uri,
596 version,
597 namespaces: Default::default(),
598 tasks: Default::default(),
599 workflow: Default::default(),
600 structs: Default::default(),
601 parse_diagnostics: diagnostics,
602 analysis_diagnostics: Default::default(),
603 }
604 }
605}
606
607#[derive(Debug, Clone)]
611pub struct Document {
612 data: Arc<DocumentData>,
614}
615
616impl Document {
617 pub(crate) fn default_from_uri(uri: Arc<Url>) -> Self {
619 Self {
620 data: Arc::new(DocumentData::new(
621 Default::default(),
622 uri,
623 None,
624 None,
625 Default::default(),
626 )),
627 }
628 }
629
630 pub(crate) fn from_graph_node(
632 config: &Config,
633 graph: &DocumentGraph,
634 index: NodeIndex,
635 ) -> Self {
636 let node = graph.get(index);
637
638 let (wdl_version, diagnostics) = match node.parse_state() {
639 ParseState::NotParsed => panic!("node should have been parsed"),
640 ParseState::Error(_) => return Self::default_from_uri(node.uri().clone()),
641 ParseState::Parsed {
642 wdl_version,
643 diagnostics,
644 ..
645 } => (wdl_version, diagnostics),
646 };
647
648 let root = node.root().expect("node should have been parsed");
649 let (config, wdl_version) = match (root.version_statement(), wdl_version) {
650 (Some(stmt), Some(wdl_version)) => (
651 config.with_diagnostics_config(
652 config.diagnostics_config().excepted_for_node(stmt.inner()),
653 ),
654 *wdl_version,
655 ),
656 _ => {
657 return Self {
660 data: Arc::new(DocumentData::new(
661 config.clone(),
662 node.uri().clone(),
663 Some(root.inner().green().into()),
664 None,
665 diagnostics.to_vec(),
666 )),
667 };
668 }
669 };
670
671 let mut data = DocumentData::new(
672 config.clone(),
673 node.uri().clone(),
674 Some(root.inner().green().into()),
675 Some(wdl_version),
676 diagnostics.to_vec(),
677 );
678 match root.ast_with_version_fallback(config.fallback_version()) {
679 Ast::Unsupported => {}
680 Ast::V1(ast) => v1::populate_document(&mut data, &config, graph, index, &ast),
681 }
682
683 if let Some(severity) = config.diagnostics_config().unused_import {
685 let DocumentData {
686 namespaces,
687 analysis_diagnostics,
688 ..
689 } = &mut data;
690
691 analysis_diagnostics.extend(
692 namespaces
693 .iter()
694 .filter(|(_, ns)| !ns.used && !ns.excepted)
695 .map(|(name, ns)| unused_import(name, ns.span()).with_severity(severity)),
696 );
697 }
698
699 Self {
700 data: Arc::new(data),
701 }
702 }
703
704 pub fn config(&self) -> &Config {
706 &self.data.config
707 }
708
709 pub fn root(&self) -> wdl_ast::Document {
715 wdl_ast::Document::cast(SyntaxNode::new_root(
716 self.data.root.clone().expect("should have a root"),
717 ))
718 .expect("should cast")
719 }
720
721 pub fn id(&self) -> &Arc<String> {
725 &self.data.id
726 }
727
728 pub fn uri(&self) -> &Arc<Url> {
730 &self.data.uri
731 }
732
733 pub fn path(&self) -> Cow<'_, str> {
740 if let Ok(path) = self.data.uri.to_file_path() {
741 if let Some(path) = std::env::current_dir()
742 .ok()
743 .and_then(|cwd| path.strip_prefix(cwd).ok().and_then(Path::to_str))
744 {
745 return path.to_string().into();
746 }
747
748 if let Ok(path) = path.into_os_string().into_string() {
749 return path.into();
750 }
751 }
752
753 self.data.uri.as_str().into()
754 }
755
756 pub fn version(&self) -> Option<SupportedVersion> {
761 self.data.version
762 }
763
764 pub fn namespaces(&self) -> impl Iterator<Item = (&str, &Namespace)> {
766 self.data.namespaces.iter().map(|(n, ns)| (n.as_str(), ns))
767 }
768
769 pub fn namespace(&self, name: &str) -> Option<&Namespace> {
771 self.data.namespaces.get(name)
772 }
773
774 pub fn tasks(&self) -> impl Iterator<Item = &Task> {
776 self.data.tasks.iter().map(|(_, t)| t)
777 }
778
779 pub fn task_by_name(&self, name: &str) -> Option<&Task> {
781 self.data.tasks.get(name)
782 }
783
784 pub fn workflow(&self) -> Option<&Workflow> {
788 self.data.workflow.as_ref()
789 }
790
791 pub fn structs(&self) -> impl Iterator<Item = (&str, &Struct)> {
793 self.data.structs.iter().map(|(n, s)| (n.as_str(), s))
794 }
795
796 pub fn struct_by_name(&self, name: &str) -> Option<&Struct> {
798 self.data.structs.get(name)
799 }
800
801 pub fn parse_diagnostics(&self) -> &[Diagnostic] {
803 &self.data.parse_diagnostics
804 }
805
806 pub fn analysis_diagnostics(&self) -> &[Diagnostic] {
808 &self.data.analysis_diagnostics
809 }
810
811 pub fn diagnostics(&self) -> impl Iterator<Item = &Diagnostic> {
813 self.data
814 .parse_diagnostics
815 .iter()
816 .chain(self.data.analysis_diagnostics.iter())
817 }
818
819 pub fn sort_diagnostics(&mut self) -> Self {
825 let data = &mut self.data;
826 let inner = Arc::get_mut(data).expect("should only have one reference");
827 inner.parse_diagnostics.sort();
828 inner.analysis_diagnostics.sort();
829 Self { data: data.clone() }
830 }
831
832 pub fn extend_diagnostics(&mut self, diagnostics: Vec<Diagnostic>) -> Self {
838 let data = &mut self.data;
839 let inner = Arc::get_mut(data).expect("should only have one reference");
840 inner.analysis_diagnostics.extend(diagnostics);
841 Self { data: data.clone() }
842 }
843
844 pub fn find_scope_by_position(&self, position: usize) -> Option<ScopeRef<'_>> {
846 fn find_scope(scopes: &[Scope], position: usize) -> Option<ScopeRef<'_>> {
848 let mut index = match scopes.binary_search_by_key(&position, |s| s.span.start()) {
849 Ok(index) => index,
850 Err(index) => {
851 if index == 0 {
854 return None;
855 }
856
857 index - 1
858 }
859 };
860
861 loop {
864 let scope = &scopes[index];
865 if scope.span.contains(position) {
866 return Some(ScopeRef::new(scopes, ScopeIndex(index)));
867 }
868
869 if index == 0 {
870 return None;
871 }
872
873 index -= 1;
874 }
875 }
876
877 if let Some(workflow) = &self.data.workflow
879 && workflow.scope().span().contains(position)
880 {
881 return find_scope(&workflow.scopes, position);
882 }
883
884 let task = match self
886 .data
887 .tasks
888 .binary_search_by_key(&position, |_, t| t.scope().span().start())
889 {
890 Ok(index) => &self.data.tasks[index],
891 Err(index) => {
892 if index == 0 {
895 return None;
896 }
897
898 &self.data.tasks[index - 1]
899 }
900 };
901
902 if task.scope().span().contains(position) {
903 return find_scope(&task.scopes, position);
904 }
905
906 None
907 }
908
909 pub fn has_errors(&self) -> bool {
918 if self.diagnostics().any(|d| d.severity() == Severity::Error) {
920 return true;
921 }
922
923 for (_, ns) in self.namespaces() {
925 if ns.document.has_errors() {
926 return true;
927 }
928 }
929
930 false
931 }
932
933 pub fn visit<V: crate::Visitor>(&self, diagnostics: &mut crate::Diagnostics, visitor: &mut V) {
936 crate::visit(self, diagnostics, visitor)
937 }
938}