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
32pub mod 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 Enum {
141 name: String,
143 name_span: Span,
148 offset: usize,
153 node: rowan::GreenNode,
158 namespace: Option<String>,
162 ty: Option<Type>,
166}
167
168impl Enum {
169 pub fn name(&self) -> &str {
171 &self.name
172 }
173
174 pub fn name_span(&self) -> Span {
176 self.name_span
177 }
178
179 pub fn offset(&self) -> usize {
181 self.offset
182 }
183
184 pub fn node(&self) -> &rowan::GreenNode {
186 &self.node
187 }
188
189 pub fn definition(&self) -> wdl_ast::v1::EnumDefinition {
193 wdl_ast::v1::EnumDefinition::cast(wdl_ast::SyntaxNode::new_root(self.node.clone()))
194 .expect("stored node should be a valid enum definition")
195 }
196
197 pub fn namespace(&self) -> Option<&str> {
199 self.namespace.as_deref()
200 }
201
202 pub fn ty(&self) -> Option<&Type> {
204 self.ty.as_ref()
205 }
206}
207
208#[derive(Debug, Clone)]
210pub struct Name {
211 span: Span,
213 ty: Type,
215}
216
217impl Name {
218 pub fn span(&self) -> Span {
220 self.span
221 }
222
223 pub fn ty(&self) -> &Type {
225 &self.ty
226 }
227}
228
229#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
231pub struct ScopeIndex(usize);
232
233#[derive(Debug)]
235pub struct Scope {
236 parent: Option<ScopeIndex>,
240 span: Span,
242 names: IndexMap<String, Name>,
244}
245
246impl Scope {
247 fn new(parent: Option<ScopeIndex>, span: Span) -> Self {
249 Self {
250 parent,
251 span,
252 names: Default::default(),
253 }
254 }
255
256 pub fn insert(&mut self, name: impl Into<String>, span: Span, ty: Type) {
258 self.names.insert(name.into(), Name { span, ty });
259 }
260}
261
262#[derive(Debug, Clone, Copy)]
264pub struct ScopeRef<'a> {
265 scopes: &'a [Scope],
267 index: ScopeIndex,
269}
270
271impl<'a> ScopeRef<'a> {
272 fn new(scopes: &'a [Scope], index: ScopeIndex) -> Self {
274 Self { scopes, index }
275 }
276
277 pub fn span(&self) -> Span {
279 self.scopes[self.index.0].span
280 }
281
282 pub fn parent(&self) -> Option<Self> {
286 self.scopes[self.index.0].parent.map(|p| Self {
287 scopes: self.scopes,
288 index: p,
289 })
290 }
291
292 pub fn names(&self) -> impl Iterator<Item = (&str, &Name)> + use<'_> {
294 self.scopes[self.index.0]
295 .names
296 .iter()
297 .map(|(name, n)| (name.as_str(), n))
298 }
299
300 pub fn local(&self, name: &str) -> Option<&Name> {
304 self.scopes[self.index.0].names.get(name)
305 }
306
307 pub fn lookup(&self, name: &str) -> Option<&Name> {
311 let mut current = Some(self.index);
312
313 while let Some(index) = current {
314 if let Some(name) = self.scopes[index.0].names.get(name) {
315 return Some(name);
316 }
317
318 current = self.scopes[index.0].parent;
319 }
320
321 None
322 }
323}
324
325#[derive(Debug)]
327struct ScopeRefMut<'a> {
328 scopes: &'a mut [Scope],
330 index: ScopeIndex,
332}
333
334impl<'a> ScopeRefMut<'a> {
335 fn new(scopes: &'a mut [Scope], index: ScopeIndex) -> Self {
337 Self { scopes, index }
338 }
339
340 pub fn lookup(&self, name: &str) -> Option<&Name> {
344 let mut current = Some(self.index);
345
346 while let Some(index) = current {
347 if let Some(name) = self.scopes[index.0].names.get(name) {
348 return Some(name);
349 }
350
351 current = self.scopes[index.0].parent;
352 }
353
354 None
355 }
356
357 pub fn insert(&mut self, name: impl Into<String>, span: Span, ty: Type) {
359 self.scopes[self.index.0]
360 .names
361 .insert(name.into(), Name { span, ty });
362 }
363
364 pub fn as_scope_ref(&'a self) -> ScopeRef<'a> {
366 ScopeRef {
367 scopes: self.scopes,
368 index: self.index,
369 }
370 }
371}
372
373#[derive(Debug)]
378pub struct ScopeUnion<'a> {
379 scope_refs: Vec<(ScopeRef<'a>, bool)>,
381}
382
383impl<'a> ScopeUnion<'a> {
384 pub fn new() -> Self {
386 Self {
387 scope_refs: Vec::new(),
388 }
389 }
390
391 pub fn insert(&mut self, scope_ref: ScopeRef<'a>, exhaustive: bool) {
393 self.scope_refs.push((scope_ref, exhaustive));
394 }
395
396 pub fn resolve(self) -> Result<HashMap<String, Name>, Vec<Diagnostic>> {
401 let mut errors = Vec::new();
402 let mut ignored: HashSet<String> = HashSet::new();
403
404 let mut names: HashMap<String, Name> = HashMap::new();
406 for (scope_ref, _) in &self.scope_refs {
407 for (name, info) in scope_ref.names() {
408 if ignored.contains(name) {
409 continue;
410 }
411
412 match names.entry(name.to_string()) {
413 Entry::Vacant(entry) => {
414 entry.insert(info.clone());
415 }
416 Entry::Occupied(mut entry) => {
417 let Some(ty) = entry.get().ty.common_type(&info.ty) else {
418 errors.push(no_common_type(
419 &entry.get().ty,
420 entry.get().span,
421 &info.ty,
422 info.span,
423 ));
424 names.remove(name);
425 ignored.insert(name.to_string());
426 continue;
427 };
428
429 entry.get_mut().ty = ty;
430 }
431 }
432 }
433 }
434
435 for (scope_ref, _) in &self.scope_refs {
437 for (name, info) in &mut names {
438 if ignored.contains(name) {
439 continue;
440 }
441
442 if scope_ref.local(name).is_none() {
444 info.ty = info.ty.optional();
445 }
446 }
447 }
448
449 let has_exhaustive = self.scope_refs.iter().any(|(_, exhaustive)| *exhaustive);
451 if !has_exhaustive {
452 for info in names.values_mut() {
453 info.ty = info.ty.optional();
454 }
455 }
456
457 if !errors.is_empty() {
458 return Err(errors);
459 }
460
461 Ok(names)
462 }
463}
464
465#[derive(Debug, Clone, PartialEq, Eq)]
467pub struct Input {
468 ty: Type,
470 required: bool,
475}
476
477impl Input {
478 pub fn ty(&self) -> &Type {
480 &self.ty
481 }
482
483 pub fn required(&self) -> bool {
485 self.required
486 }
487}
488
489#[derive(Debug, Clone, PartialEq, Eq)]
491pub struct Output {
492 ty: Type,
494 name_span: Span,
496}
497
498impl Output {
499 pub(crate) fn new(ty: Type, name_span: Span) -> Self {
501 Self { ty, name_span }
502 }
503
504 pub fn ty(&self) -> &Type {
506 &self.ty
507 }
508
509 pub fn name_span(&self) -> Span {
511 self.name_span
512 }
513}
514
515#[derive(Debug)]
517pub struct Task {
518 name_span: Span,
520 name: String,
522 scopes: Vec<Scope>,
528 inputs: Arc<IndexMap<String, Input>>,
530 outputs: Arc<IndexMap<String, Output>>,
532}
533
534impl Task {
535 pub fn name(&self) -> &str {
537 &self.name
538 }
539
540 pub fn name_span(&self) -> Span {
542 self.name_span
543 }
544
545 pub fn scope(&self) -> ScopeRef<'_> {
547 ScopeRef::new(&self.scopes, ScopeIndex(0))
548 }
549
550 pub fn inputs(&self) -> &IndexMap<String, Input> {
552 &self.inputs
553 }
554
555 pub fn outputs(&self) -> &IndexMap<String, Output> {
557 &self.outputs
558 }
559}
560
561#[derive(Debug)]
563pub struct Workflow {
564 name_span: Span,
566 name: String,
568 scopes: Vec<Scope>,
574 inputs: Arc<IndexMap<String, Input>>,
576 outputs: Arc<IndexMap<String, Output>>,
578 calls: HashMap<String, CallType>,
580 allows_nested_inputs: bool,
582}
583
584impl Workflow {
585 pub fn name(&self) -> &str {
587 &self.name
588 }
589
590 pub fn name_span(&self) -> Span {
592 self.name_span
593 }
594
595 pub fn scope(&self) -> ScopeRef<'_> {
597 ScopeRef::new(&self.scopes, ScopeIndex(0))
598 }
599
600 pub fn inputs(&self) -> &IndexMap<String, Input> {
602 &self.inputs
603 }
604
605 pub fn outputs(&self) -> &IndexMap<String, Output> {
607 &self.outputs
608 }
609
610 pub fn calls(&self) -> &HashMap<String, CallType> {
612 &self.calls
613 }
614
615 pub fn allows_nested_inputs(&self) -> bool {
617 self.allows_nested_inputs
618 }
619}
620
621#[derive(Debug)]
623pub(crate) struct DocumentData {
624 config: Config,
626 root: Option<GreenNode>,
630 id: Arc<String>,
634 uri: Arc<Url>,
636 version: Option<SupportedVersion>,
638 namespaces: IndexMap<String, Namespace>,
640 tasks: IndexMap<String, Task>,
642 workflow: Option<Workflow>,
644 structs: IndexMap<String, Struct>,
646 enums: IndexMap<String, Enum>,
648 parse_diagnostics: Vec<Diagnostic>,
650 analysis_diagnostics: Vec<Diagnostic>,
652}
653
654impl DocumentData {
655 fn new(
657 config: Config,
658 uri: Arc<Url>,
659 root: Option<GreenNode>,
660 version: Option<SupportedVersion>,
661 diagnostics: Vec<Diagnostic>,
662 ) -> Self {
663 Self {
664 config,
665 root,
666 id: Uuid::new_v4().to_string().into(),
667 uri,
668 version,
669 namespaces: Default::default(),
670 tasks: Default::default(),
671 workflow: Default::default(),
672 structs: Default::default(),
673 enums: Default::default(),
674 parse_diagnostics: diagnostics,
675 analysis_diagnostics: Default::default(),
676 }
677 }
678}
679
680#[derive(Debug, Clone)]
684pub struct Document {
685 data: Arc<DocumentData>,
687}
688
689impl Document {
690 pub(crate) fn default_from_uri(uri: Arc<Url>) -> Self {
692 Self {
693 data: Arc::new(DocumentData::new(
694 Default::default(),
695 uri,
696 None,
697 None,
698 Default::default(),
699 )),
700 }
701 }
702
703 pub(crate) fn from_graph_node(
705 config: &Config,
706 graph: &DocumentGraph,
707 index: NodeIndex,
708 ) -> Self {
709 let node = graph.get(index);
710
711 let (wdl_version, diagnostics) = match node.parse_state() {
712 ParseState::NotParsed => panic!("node should have been parsed"),
713 ParseState::Error(_) => return Self::default_from_uri(node.uri().clone()),
714 ParseState::Parsed {
715 wdl_version,
716 diagnostics,
717 ..
718 } => (wdl_version, diagnostics),
719 };
720
721 let root = node.root().expect("node should have been parsed");
722 let (config, wdl_version) = match (root.version_statement(), wdl_version) {
723 (Some(stmt), Some(wdl_version)) => (
724 config.with_diagnostics_config(
725 config.diagnostics_config().excepted_for_node(stmt.inner()),
726 ),
727 *wdl_version,
728 ),
729 _ => {
730 return Self {
733 data: Arc::new(DocumentData::new(
734 config.clone(),
735 node.uri().clone(),
736 Some(root.inner().green().into()),
737 None,
738 diagnostics.to_vec(),
739 )),
740 };
741 }
742 };
743
744 let mut data = DocumentData::new(
745 config.clone(),
746 node.uri().clone(),
747 Some(root.inner().green().into()),
748 Some(wdl_version),
749 diagnostics.to_vec(),
750 );
751 match root.ast_with_version_fallback(config.fallback_version()) {
752 Ast::Unsupported => {}
753 Ast::V1(ast) => v1::populate_document(&mut data, &config, graph, index, &ast),
754 }
755
756 if let Some(severity) = config.diagnostics_config().unused_import {
758 let DocumentData {
759 namespaces,
760 analysis_diagnostics,
761 ..
762 } = &mut data;
763
764 analysis_diagnostics.extend(
765 namespaces
766 .iter()
767 .filter(|(_, ns)| !ns.used && !ns.excepted)
768 .map(|(name, ns)| unused_import(name, ns.span()).with_severity(severity)),
769 );
770 }
771
772 Self {
773 data: Arc::new(data),
774 }
775 }
776
777 pub fn config(&self) -> &Config {
779 &self.data.config
780 }
781
782 pub fn root(&self) -> wdl_ast::Document {
788 wdl_ast::Document::cast(SyntaxNode::new_root(
789 self.data.root.clone().expect("should have a root"),
790 ))
791 .expect("should cast")
792 }
793
794 pub fn id(&self) -> &Arc<String> {
798 &self.data.id
799 }
800
801 pub fn uri(&self) -> &Arc<Url> {
803 &self.data.uri
804 }
805
806 pub fn path(&self) -> Cow<'_, str> {
813 if let Ok(path) = self.data.uri.to_file_path() {
814 if let Some(path) = std::env::current_dir()
815 .ok()
816 .and_then(|cwd| path.strip_prefix(cwd).ok().and_then(Path::to_str))
817 {
818 return path.to_string().into();
819 }
820
821 if let Ok(path) = path.into_os_string().into_string() {
822 return path.into();
823 }
824 }
825
826 self.data.uri.as_str().into()
827 }
828
829 pub fn version(&self) -> Option<SupportedVersion> {
834 self.data.version
835 }
836
837 pub fn namespaces(&self) -> impl Iterator<Item = (&str, &Namespace)> {
839 self.data.namespaces.iter().map(|(n, ns)| (n.as_str(), ns))
840 }
841
842 pub fn namespace(&self, name: &str) -> Option<&Namespace> {
844 self.data.namespaces.get(name)
845 }
846
847 pub fn tasks(&self) -> impl Iterator<Item = &Task> {
849 self.data.tasks.iter().map(|(_, t)| t)
850 }
851
852 pub fn task_by_name(&self, name: &str) -> Option<&Task> {
854 self.data.tasks.get(name)
855 }
856
857 pub fn workflow(&self) -> Option<&Workflow> {
861 self.data.workflow.as_ref()
862 }
863
864 pub fn structs(&self) -> impl Iterator<Item = (&str, &Struct)> {
866 self.data.structs.iter().map(|(n, s)| (n.as_str(), s))
867 }
868
869 pub fn struct_by_name(&self, name: &str) -> Option<&Struct> {
871 self.data.structs.get(name)
872 }
873
874 pub fn enums(&self) -> impl Iterator<Item = (&str, &Enum)> {
876 self.data.enums.iter().map(|(n, e)| (n.as_str(), e))
877 }
878
879 pub fn enum_by_name(&self, name: &str) -> Option<&Enum> {
881 self.data.enums.get(name)
882 }
883
884 pub fn get_custom_type(&self, name: &str) -> Option<Type> {
886 if let Some(s) = self.struct_by_name(name) {
887 return s.ty().cloned();
888 }
889
890 if let Some(s) = self.enum_by_name(name) {
891 return s.ty().cloned();
892 }
893
894 None
895 }
896
897 pub fn get_variant_cache_key(
899 &self,
900 name: &str,
901 variant: &str,
902 ) -> Option<crate::types::EnumVariantCacheKey> {
903 let (enum_index, _, r#enum) = self.data.enums.get_full(name)?;
904 let enum_ty = r#enum.ty()?.as_enum()?;
905 let variant_index = enum_ty.variants().iter().position(|v| v == variant)?;
906 Some(crate::types::EnumVariantCacheKey::new(
907 enum_index,
908 variant_index,
909 ))
910 }
911
912 pub fn parse_diagnostics(&self) -> &[Diagnostic] {
914 &self.data.parse_diagnostics
915 }
916
917 pub fn analysis_diagnostics(&self) -> &[Diagnostic] {
919 &self.data.analysis_diagnostics
920 }
921
922 pub fn diagnostics(&self) -> impl Iterator<Item = &Diagnostic> {
924 self.data
925 .parse_diagnostics
926 .iter()
927 .chain(self.data.analysis_diagnostics.iter())
928 }
929
930 pub fn sort_diagnostics(&mut self) -> Self {
936 let data = &mut self.data;
937 let inner = Arc::get_mut(data).expect("should only have one reference");
938 inner.parse_diagnostics.sort();
939 inner.analysis_diagnostics.sort();
940 Self { data: data.clone() }
941 }
942
943 pub fn extend_diagnostics(&mut self, diagnostics: Vec<Diagnostic>) -> Self {
949 let data = &mut self.data;
950 let inner = Arc::get_mut(data).expect("should only have one reference");
951 inner.analysis_diagnostics.extend(diagnostics);
952 Self { data: data.clone() }
953 }
954
955 pub fn find_scope_by_position(&self, position: usize) -> Option<ScopeRef<'_>> {
957 fn find_scope(scopes: &[Scope], position: usize) -> Option<ScopeRef<'_>> {
959 let mut index = match scopes.binary_search_by_key(&position, |s| s.span.start()) {
960 Ok(index) => index,
961 Err(index) => {
962 if index == 0 {
965 return None;
966 }
967
968 index - 1
969 }
970 };
971
972 loop {
975 let scope = &scopes[index];
976 if scope.span.contains(position) {
977 return Some(ScopeRef::new(scopes, ScopeIndex(index)));
978 }
979
980 if index == 0 {
981 return None;
982 }
983
984 index -= 1;
985 }
986 }
987
988 if let Some(workflow) = &self.data.workflow
990 && workflow.scope().span().contains(position)
991 {
992 return find_scope(&workflow.scopes, position);
993 }
994
995 let task = match self
997 .data
998 .tasks
999 .binary_search_by_key(&position, |_, t| t.scope().span().start())
1000 {
1001 Ok(index) => &self.data.tasks[index],
1002 Err(index) => {
1003 if index == 0 {
1006 return None;
1007 }
1008
1009 &self.data.tasks[index - 1]
1010 }
1011 };
1012
1013 if task.scope().span().contains(position) {
1014 return find_scope(&task.scopes, position);
1015 }
1016
1017 None
1018 }
1019
1020 pub fn has_errors(&self) -> bool {
1029 if self.diagnostics().any(|d| d.severity() == Severity::Error) {
1031 return true;
1032 }
1033
1034 for (_, ns) in self.namespaces() {
1036 if ns.document.has_errors() {
1037 return true;
1038 }
1039 }
1040
1041 false
1042 }
1043
1044 pub fn visit<V: crate::Visitor>(&self, diagnostics: &mut crate::Diagnostics, visitor: &mut V) {
1047 crate::visit(self, diagnostics, visitor)
1048 }
1049}