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 arrayvec::ArrayString;
11use indexmap::IndexMap;
12use petgraph::graph::NodeIndex;
13use rowan::GreenNode;
14use rowan::TextRange;
15use rowan::TextSize;
16use url::Url;
17use uuid::Uuid;
18use wdl_ast::Ast;
19use wdl_ast::AstNode;
20use wdl_ast::Diagnostic;
21use wdl_ast::Severity;
22use wdl_ast::Span;
23use wdl_ast::SupportedVersion;
24use wdl_ast::SyntaxNode;
25
26use crate::config::Config;
27use crate::diagnostics::Context;
28use crate::diagnostics::no_common_type;
29use crate::diagnostics::unused_import;
30use crate::graph::DocumentGraph;
31use crate::graph::ParseState;
32use crate::types::CallType;
33use crate::types::Optional;
34use crate::types::Type;
35
36pub mod v1;
37
38pub const TASK_VAR_NAME: &str = "task";
41
42#[derive(Debug)]
44pub struct Namespace {
45 span: Span,
47 source: Arc<Url>,
49 document: Document,
51 used: bool,
53 excepted: bool,
56}
57
58impl Namespace {
59 pub fn span(&self) -> Span {
61 self.span
62 }
63
64 pub fn source(&self) -> &Arc<Url> {
66 &self.source
67 }
68
69 pub fn document(&self) -> &Document {
71 &self.document
72 }
73}
74
75#[derive(Debug, Clone)]
77pub struct Struct {
78 name: String,
80 name_span: Span,
85 offset: usize,
90 node: rowan::GreenNode,
94 namespace: Option<String>,
98 ty: Option<Type>,
102}
103
104impl Struct {
105 pub fn name(&self) -> &str {
107 &self.name
108 }
109
110 pub fn name_span(&self) -> Span {
112 self.name_span
113 }
114
115 pub fn offset(&self) -> usize {
117 self.offset
118 }
119
120 pub fn node(&self) -> &rowan::GreenNode {
122 &self.node
123 }
124
125 pub fn namespace(&self) -> Option<&str> {
130 self.namespace.as_deref()
131 }
132
133 pub fn ty(&self) -> Option<&Type> {
138 self.ty.as_ref()
139 }
140}
141
142#[derive(Debug, Clone)]
144pub struct Enum {
145 name: String,
147 name_span: Span,
152 offset: usize,
157 node: rowan::GreenNode,
162 namespace: Option<String>,
166 ty: Option<Type>,
170}
171
172impl Enum {
173 pub fn name(&self) -> &str {
175 &self.name
176 }
177
178 pub fn name_span(&self) -> Span {
180 self.name_span
181 }
182
183 pub fn offset(&self) -> usize {
185 self.offset
186 }
187
188 pub fn node(&self) -> &rowan::GreenNode {
190 &self.node
191 }
192
193 pub fn definition(&self) -> wdl_ast::v1::EnumDefinition {
197 wdl_ast::v1::EnumDefinition::cast(wdl_ast::SyntaxNode::new_root(self.node.clone()))
198 .expect("stored node should be a valid enum definition")
199 }
200
201 pub fn namespace(&self) -> Option<&str> {
203 self.namespace.as_deref()
204 }
205
206 pub fn ty(&self) -> Option<&Type> {
208 self.ty.as_ref()
209 }
210}
211
212#[derive(Debug, Clone)]
214pub struct Name {
215 span: Span,
217 ty: Type,
219}
220
221impl Name {
222 pub fn span(&self) -> Span {
224 self.span
225 }
226
227 pub fn ty(&self) -> &Type {
229 &self.ty
230 }
231}
232
233#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
235pub struct ScopeIndex(usize);
236
237#[derive(Debug)]
239pub struct Scope {
240 parent: Option<ScopeIndex>,
244 span: Span,
246 names: IndexMap<String, Name>,
248}
249
250impl Scope {
251 fn new(parent: Option<ScopeIndex>, span: Span) -> Self {
253 Self {
254 parent,
255 span,
256 names: Default::default(),
257 }
258 }
259
260 pub fn insert(&mut self, name: impl Into<String>, span: Span, ty: Type) {
262 self.names.insert(name.into(), Name { span, ty });
263 }
264}
265
266#[derive(Debug, Clone, Copy)]
268pub struct ScopeRef<'a> {
269 scopes: &'a [Scope],
271 index: ScopeIndex,
273}
274
275impl<'a> ScopeRef<'a> {
276 fn new(scopes: &'a [Scope], index: ScopeIndex) -> Self {
278 Self { scopes, index }
279 }
280
281 pub fn span(&self) -> Span {
283 self.scopes[self.index.0].span
284 }
285
286 pub fn parent(&self) -> Option<Self> {
290 self.scopes[self.index.0].parent.map(|p| Self {
291 scopes: self.scopes,
292 index: p,
293 })
294 }
295
296 pub fn names(&self) -> impl Iterator<Item = (&str, &Name)> + use<'_> {
298 self.scopes[self.index.0]
299 .names
300 .iter()
301 .map(|(name, n)| (name.as_str(), n))
302 }
303
304 pub fn local(&self, name: &str) -> Option<&Name> {
308 self.scopes[self.index.0].names.get(name)
309 }
310
311 pub fn lookup(&self, name: &str) -> Option<&Name> {
315 let mut current = Some(self.index);
316
317 while let Some(index) = current {
318 if let Some(name) = self.scopes[index.0].names.get(name) {
319 return Some(name);
320 }
321
322 current = self.scopes[index.0].parent;
323 }
324
325 None
326 }
327}
328
329#[derive(Debug)]
331struct ScopeRefMut<'a> {
332 scopes: &'a mut [Scope],
334 index: ScopeIndex,
336}
337
338impl<'a> ScopeRefMut<'a> {
339 fn new(scopes: &'a mut [Scope], index: ScopeIndex) -> Self {
341 Self { scopes, index }
342 }
343
344 pub fn lookup(&self, name: &str) -> Option<&Name> {
348 let mut current = Some(self.index);
349
350 while let Some(index) = current {
351 if let Some(name) = self.scopes[index.0].names.get(name) {
352 return Some(name);
353 }
354
355 current = self.scopes[index.0].parent;
356 }
357
358 None
359 }
360
361 pub fn insert(&mut self, name: impl Into<String>, span: Span, ty: Type) {
363 self.scopes[self.index.0]
364 .names
365 .insert(name.into(), Name { span, ty });
366 }
367
368 pub fn as_scope_ref(&'a self) -> ScopeRef<'a> {
370 ScopeRef {
371 scopes: self.scopes,
372 index: self.index,
373 }
374 }
375}
376
377#[derive(Debug)]
382pub struct ScopeUnion<'a> {
383 scope_refs: Vec<(ScopeRef<'a>, bool)>,
385}
386
387impl<'a> ScopeUnion<'a> {
388 pub fn new() -> Self {
390 Self {
391 scope_refs: Vec::new(),
392 }
393 }
394
395 pub fn insert(&mut self, scope_ref: ScopeRef<'a>, exhaustive: bool) {
397 self.scope_refs.push((scope_ref, exhaustive));
398 }
399
400 pub fn resolve(self) -> Result<HashMap<String, Name>, Vec<Diagnostic>> {
405 let mut errors = Vec::new();
406 let mut ignored: HashSet<String> = HashSet::new();
407
408 let mut names: HashMap<String, Name> = HashMap::new();
410 for (scope_ref, _) in &self.scope_refs {
411 for (name, info) in scope_ref.names() {
412 if ignored.contains(name) {
413 continue;
414 }
415
416 match names.entry(name.to_string()) {
417 Entry::Vacant(entry) => {
418 entry.insert(info.clone());
419 }
420 Entry::Occupied(mut entry) => {
421 let Some(ty) = entry.get().ty.common_type(&info.ty) else {
422 errors.push(no_common_type(
423 &entry.get().ty,
424 entry.get().span,
425 &info.ty,
426 info.span,
427 ));
428 names.remove(name);
429 ignored.insert(name.to_string());
430 continue;
431 };
432
433 entry.get_mut().ty = ty;
434 }
435 }
436 }
437 }
438
439 for (scope_ref, _) in &self.scope_refs {
441 for (name, info) in &mut names {
442 if ignored.contains(name) {
443 continue;
444 }
445
446 if scope_ref.local(name).is_none() {
448 info.ty = info.ty.optional();
449 }
450 }
451 }
452
453 let has_exhaustive = self.scope_refs.iter().any(|(_, exhaustive)| *exhaustive);
455 if !has_exhaustive {
456 for info in names.values_mut() {
457 info.ty = info.ty.optional();
458 }
459 }
460
461 if !errors.is_empty() {
462 return Err(errors);
463 }
464
465 Ok(names)
466 }
467}
468
469#[derive(Debug, Clone, PartialEq, Eq)]
471pub struct Input {
472 ty: Type,
474 required: bool,
479}
480
481impl Input {
482 pub fn ty(&self) -> &Type {
484 &self.ty
485 }
486
487 pub fn required(&self) -> bool {
489 self.required
490 }
491}
492
493#[derive(Debug, Clone, PartialEq, Eq)]
495pub struct Output {
496 ty: Type,
498 name_span: Span,
500}
501
502impl Output {
503 pub(crate) fn new(ty: Type, name_span: Span) -> Self {
505 Self { ty, name_span }
506 }
507
508 pub fn ty(&self) -> &Type {
510 &self.ty
511 }
512
513 pub fn name_span(&self) -> Span {
515 self.name_span
516 }
517}
518
519#[derive(Debug)]
521pub struct Task {
522 name_span: Span,
524 name: String,
526 scopes: Vec<Scope>,
532 inputs: Arc<IndexMap<String, Input>>,
534 outputs: Arc<IndexMap<String, Output>>,
536}
537
538impl Task {
539 pub fn name(&self) -> &str {
541 &self.name
542 }
543
544 pub fn name_span(&self) -> Span {
546 self.name_span
547 }
548
549 pub fn scope(&self) -> ScopeRef<'_> {
551 ScopeRef::new(&self.scopes, ScopeIndex(0))
552 }
553
554 pub fn inputs(&self) -> &IndexMap<String, Input> {
556 &self.inputs
557 }
558
559 pub fn outputs(&self) -> &IndexMap<String, Output> {
561 &self.outputs
562 }
563}
564
565#[derive(Debug)]
567pub struct Workflow {
568 name_span: Span,
570 name: String,
572 scopes: Vec<Scope>,
578 inputs: Arc<IndexMap<String, Input>>,
580 outputs: Arc<IndexMap<String, Output>>,
582 calls: HashMap<String, CallType>,
584 allows_nested_inputs: bool,
586}
587
588impl Workflow {
589 pub fn name(&self) -> &str {
591 &self.name
592 }
593
594 pub fn name_span(&self) -> Span {
596 self.name_span
597 }
598
599 pub fn scope(&self) -> ScopeRef<'_> {
601 ScopeRef::new(&self.scopes, ScopeIndex(0))
602 }
603
604 pub fn inputs(&self) -> &IndexMap<String, Input> {
606 &self.inputs
607 }
608
609 pub fn outputs(&self) -> &IndexMap<String, Output> {
611 &self.outputs
612 }
613
614 pub fn calls(&self) -> &HashMap<String, CallType> {
616 &self.calls
617 }
618
619 pub fn allows_nested_inputs(&self) -> bool {
621 self.allows_nested_inputs
622 }
623}
624
625#[derive(Debug)]
627pub(crate) struct DocumentData {
628 config: Config,
630 root: Option<GreenNode>,
634 id: Arc<String>,
638 uri: Arc<Url>,
640 version: Option<SupportedVersion>,
642 namespaces: IndexMap<String, Namespace>,
644 tasks: IndexMap<String, Task>,
646 workflow: Option<Workflow>,
648 structs: IndexMap<String, Struct>,
650 enums: IndexMap<String, Enum>,
652 parse_diagnostics: Vec<Diagnostic>,
654 analysis_diagnostics: Vec<Diagnostic>,
656}
657
658impl DocumentData {
659 fn new(
661 config: Config,
662 uri: Arc<Url>,
663 root: Option<GreenNode>,
664 version: Option<SupportedVersion>,
665 diagnostics: Vec<Diagnostic>,
666 ) -> Self {
667 Self {
668 config,
669 root,
670 id: Uuid::new_v4().to_string().into(),
671 uri,
672 version,
673 namespaces: Default::default(),
674 tasks: Default::default(),
675 workflow: Default::default(),
676 structs: Default::default(),
677 enums: Default::default(),
678 parse_diagnostics: diagnostics,
679 analysis_diagnostics: Default::default(),
680 }
681 }
682
683 pub fn context(&self, name: &str) -> Option<Context> {
689 if let Some(ns) = self.namespaces.get(name) {
691 Some(Context::Namespace(ns.span()))
692 } else if let Some(task) = self.tasks.get(name) {
693 Some(Context::Task(task.name_span()))
694 } else if let Some(wf) = &self.workflow
695 && wf.name == name
696 {
697 Some(Context::Workflow(wf.name_span()))
698 } else if let Some(s) = self.structs.get(name) {
699 Some(Context::Struct(s.name_span()))
700 } else {
701 self.enums.get(name).map(|e| Context::Enum(e.name_span()))
703 }
704 }
705}
706
707#[derive(Debug, Clone)]
711pub struct Document {
712 data: Arc<DocumentData>,
714}
715
716impl Document {
717 pub(crate) fn default_from_uri(uri: Arc<Url>) -> Self {
719 Self {
720 data: Arc::new(DocumentData::new(
721 Default::default(),
722 uri,
723 None,
724 None,
725 Default::default(),
726 )),
727 }
728 }
729
730 pub(crate) fn from_graph_node(
732 config: &Config,
733 graph: &DocumentGraph,
734 index: NodeIndex,
735 ) -> Self {
736 let node = graph.get(index);
737
738 let (wdl_version, diagnostics) = match node.parse_state() {
739 ParseState::NotParsed => panic!("node should have been parsed"),
740 ParseState::Error(_) => return Self::default_from_uri(node.uri().clone()),
741 ParseState::Parsed {
742 wdl_version,
743 diagnostics,
744 ..
745 } => (wdl_version, diagnostics),
746 };
747
748 let root = node.root().expect("node should have been parsed");
749 let (config, wdl_version) = match (root.version_statement(), wdl_version) {
750 (Some(stmt), Some(wdl_version)) => (
751 config.with_diagnostics_config(
752 config.diagnostics_config().excepted_for_node(stmt.inner()),
753 ),
754 *wdl_version,
755 ),
756 _ => {
757 return Self {
760 data: Arc::new(DocumentData::new(
761 config.clone(),
762 node.uri().clone(),
763 Some(root.inner().green().into()),
764 None,
765 diagnostics.to_vec(),
766 )),
767 };
768 }
769 };
770
771 let mut data = DocumentData::new(
772 config.clone(),
773 node.uri().clone(),
774 Some(root.inner().green().into()),
775 Some(wdl_version),
776 diagnostics.to_vec(),
777 );
778 match root.ast_with_version_fallback(config.fallback_version()) {
779 Ast::Unsupported => {}
780 Ast::V1(ast) => v1::populate_document(&mut data, &config, graph, index, &ast),
781 }
782
783 if let Some(severity) = config.diagnostics_config().unused_import {
785 let DocumentData {
786 namespaces,
787 analysis_diagnostics,
788 ..
789 } = &mut data;
790
791 analysis_diagnostics.extend(
792 namespaces
793 .iter()
794 .filter(|(_, ns)| !ns.used && !ns.excepted)
795 .map(|(name, ns)| unused_import(name, ns.span()).with_severity(severity)),
796 );
797 }
798
799 Self {
800 data: Arc::new(data),
801 }
802 }
803
804 pub fn config(&self) -> &Config {
806 &self.data.config
807 }
808
809 pub fn root(&self) -> wdl_ast::Document {
815 wdl_ast::Document::cast(SyntaxNode::new_root(
816 self.data.root.clone().expect("should have a root"),
817 ))
818 .expect("should cast")
819 }
820
821 pub fn id(&self) -> &Arc<String> {
825 &self.data.id
826 }
827
828 pub fn uri(&self) -> &Arc<Url> {
830 &self.data.uri
831 }
832
833 pub fn path(&self) -> Cow<'_, str> {
840 if let Ok(path) = self.data.uri.to_file_path() {
841 if let Some(path) = std::env::current_dir()
842 .ok()
843 .and_then(|cwd| path.strip_prefix(cwd).ok().and_then(Path::to_str))
844 {
845 return path.to_string().into();
846 }
847
848 if let Ok(path) = path.into_os_string().into_string() {
849 return path.into();
850 }
851 }
852
853 self.data.uri.as_str().into()
854 }
855
856 pub fn hash_span(&self, span: Span) -> Option<ArrayString<64>> {
864 let text = self.root().inner().text();
865 let text_len = usize::from(text.len());
866 if span.end() > text_len {
867 return None;
868 }
869 let range = TextRange::new(
870 TextSize::new(span.start() as u32),
871 TextSize::new(span.end() as u32),
872 );
873 let slice = text.slice(range);
874 let mut hasher = blake3::Hasher::new();
875 slice.for_each_chunk(|chunk| {
876 hasher.update(chunk.as_bytes());
877 });
878 Some(hasher.finalize().to_hex())
879 }
880
881 pub fn version(&self) -> Option<SupportedVersion> {
886 self.data.version
887 }
888
889 pub fn namespaces(&self) -> impl Iterator<Item = (&str, &Namespace)> {
891 self.data.namespaces.iter().map(|(n, ns)| (n.as_str(), ns))
892 }
893
894 pub fn namespace(&self, name: &str) -> Option<&Namespace> {
896 self.data.namespaces.get(name)
897 }
898
899 pub fn tasks(&self) -> impl Iterator<Item = &Task> {
901 self.data.tasks.iter().map(|(_, t)| t)
902 }
903
904 pub fn task_by_name(&self, name: &str) -> Option<&Task> {
906 self.data.tasks.get(name)
907 }
908
909 pub fn workflow(&self) -> Option<&Workflow> {
913 self.data.workflow.as_ref()
914 }
915
916 pub fn structs(&self) -> impl Iterator<Item = (&str, &Struct)> {
918 self.data.structs.iter().map(|(n, s)| (n.as_str(), s))
919 }
920
921 pub fn struct_by_name(&self, name: &str) -> Option<&Struct> {
923 self.data.structs.get(name)
924 }
925
926 pub fn enums(&self) -> impl Iterator<Item = (&str, &Enum)> {
928 self.data.enums.iter().map(|(n, e)| (n.as_str(), e))
929 }
930
931 pub fn enum_by_name(&self, name: &str) -> Option<&Enum> {
933 self.data.enums.get(name)
934 }
935
936 pub fn get_custom_type(&self, name: &str) -> Option<Type> {
938 if let Some(s) = self.struct_by_name(name) {
939 return s.ty().cloned();
940 }
941
942 if let Some(s) = self.enum_by_name(name) {
943 return s.ty().cloned();
944 }
945
946 None
947 }
948
949 pub fn get_variant_cache_key(
951 &self,
952 name: &str,
953 variant: &str,
954 ) -> Option<crate::types::EnumVariantCacheKey> {
955 let (enum_index, _, r#enum) = self.data.enums.get_full(name)?;
956 let enum_ty = r#enum.ty()?.as_enum()?;
957 let variant_index = enum_ty.variants().iter().position(|v| v == variant)?;
958 Some(crate::types::EnumVariantCacheKey::new(
959 enum_index,
960 variant_index,
961 ))
962 }
963
964 pub fn parse_diagnostics(&self) -> &[Diagnostic] {
966 &self.data.parse_diagnostics
967 }
968
969 pub fn analysis_diagnostics(&self) -> &[Diagnostic] {
971 &self.data.analysis_diagnostics
972 }
973
974 pub fn diagnostics(&self) -> impl Iterator<Item = &Diagnostic> {
976 self.data
977 .parse_diagnostics
978 .iter()
979 .chain(self.data.analysis_diagnostics.iter())
980 }
981
982 pub fn sort_diagnostics(&mut self) -> Self {
988 let data = &mut self.data;
989 let inner = Arc::get_mut(data).expect("should only have one reference");
990 inner.parse_diagnostics.sort();
991 inner.analysis_diagnostics.sort();
992 Self { data: data.clone() }
993 }
994
995 pub fn extend_diagnostics(&mut self, diagnostics: Vec<Diagnostic>) -> Self {
1001 let data = &mut self.data;
1002 let inner = Arc::get_mut(data).expect("should only have one reference");
1003 inner.analysis_diagnostics.extend(diagnostics);
1004 Self { data: data.clone() }
1005 }
1006
1007 pub fn find_scope_by_position(&self, position: usize) -> Option<ScopeRef<'_>> {
1009 fn find_scope(scopes: &[Scope], position: usize) -> Option<ScopeRef<'_>> {
1011 let mut index = match scopes.binary_search_by_key(&position, |s| s.span.start()) {
1012 Ok(index) => index,
1013 Err(index) => {
1014 if index == 0 {
1017 return None;
1018 }
1019
1020 index - 1
1021 }
1022 };
1023
1024 loop {
1027 let scope = &scopes[index];
1028 if scope.span.contains(position) {
1029 return Some(ScopeRef::new(scopes, ScopeIndex(index)));
1030 }
1031
1032 if index == 0 {
1033 return None;
1034 }
1035
1036 index -= 1;
1037 }
1038 }
1039
1040 if let Some(workflow) = &self.data.workflow
1042 && workflow.scope().span().contains(position)
1043 {
1044 return find_scope(&workflow.scopes, position);
1045 }
1046
1047 let task = match self
1049 .data
1050 .tasks
1051 .binary_search_by_key(&position, |_, t| t.scope().span().start())
1052 {
1053 Ok(index) => &self.data.tasks[index],
1054 Err(index) => {
1055 if index == 0 {
1058 return None;
1059 }
1060
1061 &self.data.tasks[index - 1]
1062 }
1063 };
1064
1065 if task.scope().span().contains(position) {
1066 return find_scope(&task.scopes, position);
1067 }
1068
1069 None
1070 }
1071
1072 pub fn has_errors(&self) -> bool {
1081 if self.diagnostics().any(|d| d.severity() == Severity::Error) {
1083 return true;
1084 }
1085
1086 for (_, ns) in self.namespaces() {
1088 if ns.document.has_errors() {
1089 return true;
1090 }
1091 }
1092
1093 false
1094 }
1095
1096 pub fn visit<V: crate::Visitor>(&self, diagnostics: &mut crate::Diagnostics, visitor: &mut V) {
1099 crate::visit(self, diagnostics, visitor)
1100 }
1101}