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 span: Span,
528 scopes: Vec<Scope>,
534 inputs: Arc<IndexMap<String, Input>>,
536 outputs: Arc<IndexMap<String, Output>>,
538}
539
540impl Task {
541 pub fn name(&self) -> &str {
543 &self.name
544 }
545
546 pub fn name_span(&self) -> Span {
548 self.name_span
549 }
550
551 pub fn span(&self) -> Span {
553 self.span
554 }
555
556 pub fn scope(&self) -> ScopeRef<'_> {
558 ScopeRef::new(&self.scopes, ScopeIndex(0))
559 }
560
561 pub fn inputs(&self) -> &IndexMap<String, Input> {
563 &self.inputs
564 }
565
566 pub fn outputs(&self) -> &IndexMap<String, Output> {
568 &self.outputs
569 }
570}
571
572#[derive(Debug)]
574pub struct Workflow {
575 name_span: Span,
577 name: String,
579 span: Span,
581 scopes: Vec<Scope>,
587 inputs: Arc<IndexMap<String, Input>>,
589 outputs: Arc<IndexMap<String, Output>>,
591 calls: HashMap<String, CallType>,
593 allows_nested_inputs: bool,
595}
596
597impl Workflow {
598 pub fn name(&self) -> &str {
600 &self.name
601 }
602
603 pub fn name_span(&self) -> Span {
605 self.name_span
606 }
607
608 pub fn span(&self) -> Span {
610 self.span
611 }
612
613 pub fn scope(&self) -> ScopeRef<'_> {
615 ScopeRef::new(&self.scopes, ScopeIndex(0))
616 }
617
618 pub fn inputs(&self) -> &IndexMap<String, Input> {
620 &self.inputs
621 }
622
623 pub fn outputs(&self) -> &IndexMap<String, Output> {
625 &self.outputs
626 }
627
628 pub fn calls(&self) -> &HashMap<String, CallType> {
630 &self.calls
631 }
632
633 pub fn allows_nested_inputs(&self) -> bool {
635 self.allows_nested_inputs
636 }
637}
638
639#[derive(Debug)]
641pub enum Callable<'a> {
642 Workflow(&'a Workflow),
644 Task(&'a Task),
646}
647
648impl Callable<'_> {
649 pub fn name(&self) -> &str {
651 match self {
652 Callable::Workflow(w) => w.name(),
653 Callable::Task(t) => t.name(),
654 }
655 }
656
657 pub fn name_span(&self) -> Span {
659 match self {
660 Callable::Workflow(w) => w.name_span(),
661 Callable::Task(t) => t.name_span(),
662 }
663 }
664
665 pub fn span(&self) -> Span {
667 match self {
668 Callable::Workflow(w) => w.span(),
669 Callable::Task(t) => t.span(),
670 }
671 }
672}
673
674#[derive(Debug)]
676pub(crate) struct DocumentData {
677 config: Config,
679 root: Option<GreenNode>,
683 id: Arc<String>,
687 uri: Arc<Url>,
689 version: Option<SupportedVersion>,
691 namespaces: IndexMap<String, Namespace>,
693 tasks: IndexMap<String, Task>,
695 workflow: Option<Workflow>,
697 structs: IndexMap<String, Struct>,
699 enums: IndexMap<String, Enum>,
701 parse_diagnostics: Vec<Diagnostic>,
703 analysis_diagnostics: Vec<Diagnostic>,
705}
706
707impl DocumentData {
708 fn new(
710 config: Config,
711 uri: Arc<Url>,
712 root: Option<GreenNode>,
713 version: Option<SupportedVersion>,
714 diagnostics: Vec<Diagnostic>,
715 ) -> Self {
716 Self {
717 config,
718 root,
719 id: Uuid::new_v4().to_string().into(),
720 uri,
721 version,
722 namespaces: Default::default(),
723 tasks: Default::default(),
724 workflow: Default::default(),
725 structs: Default::default(),
726 enums: Default::default(),
727 parse_diagnostics: diagnostics,
728 analysis_diagnostics: Default::default(),
729 }
730 }
731
732 pub fn context(&self, name: &str) -> Option<Context> {
738 if let Some(ns) = self.namespaces.get(name) {
740 Some(Context::Namespace(ns.span()))
741 } else if let Some(task) = self.tasks.get(name) {
742 Some(Context::Task(task.name_span()))
743 } else if let Some(wf) = &self.workflow
744 && wf.name == name
745 {
746 Some(Context::Workflow(wf.name_span()))
747 } else if let Some(s) = self.structs.get(name) {
748 Some(Context::Struct(s.name_span()))
749 } else {
750 self.enums.get(name).map(|e| Context::Enum(e.name_span()))
752 }
753 }
754}
755
756#[derive(Debug, Clone)]
760pub struct Document {
761 data: Arc<DocumentData>,
763}
764
765impl Document {
766 pub(crate) fn default_from_uri(uri: Arc<Url>) -> Self {
768 Self {
769 data: Arc::new(DocumentData::new(
770 Default::default(),
771 uri,
772 None,
773 None,
774 Default::default(),
775 )),
776 }
777 }
778
779 pub(crate) fn from_graph_node(
781 config: &Config,
782 graph: &DocumentGraph,
783 index: NodeIndex,
784 ) -> Self {
785 let node = graph.get(index);
786
787 let (wdl_version, diagnostics) = match node.parse_state() {
788 ParseState::NotParsed => panic!("node should have been parsed"),
789 ParseState::Error(_) => return Self::default_from_uri(node.uri().clone()),
790 ParseState::Parsed {
791 wdl_version,
792 diagnostics,
793 ..
794 } => (wdl_version, diagnostics),
795 };
796
797 let root = node.root().expect("node should have been parsed");
798 let (config, wdl_version) = match (root.version_statement(), wdl_version) {
799 (Some(stmt), Some(wdl_version)) => (
800 config.with_diagnostics_config(
801 config.diagnostics_config().excepted_for_node(stmt.inner()),
802 ),
803 *wdl_version,
804 ),
805 _ => {
806 return Self {
809 data: Arc::new(DocumentData::new(
810 config.clone(),
811 node.uri().clone(),
812 Some(root.inner().green().into()),
813 None,
814 diagnostics.to_vec(),
815 )),
816 };
817 }
818 };
819
820 let mut data = DocumentData::new(
821 config.clone(),
822 node.uri().clone(),
823 Some(root.inner().green().into()),
824 Some(wdl_version),
825 diagnostics.to_vec(),
826 );
827 match root.ast_with_version_fallback(config.fallback_version()) {
828 Ast::Unsupported => {}
829 Ast::V1(ast) => v1::populate_document(&mut data, &config, graph, index, &ast),
830 }
831
832 if let Some(severity) = config.diagnostics_config().unused_import {
834 let DocumentData {
835 namespaces,
836 analysis_diagnostics,
837 ..
838 } = &mut data;
839
840 analysis_diagnostics.extend(
841 namespaces
842 .iter()
843 .filter(|(_, ns)| !ns.used && !ns.excepted)
844 .map(|(name, ns)| unused_import(name, ns.span()).with_severity(severity)),
845 );
846 }
847
848 Self {
849 data: Arc::new(data),
850 }
851 }
852
853 pub fn config(&self) -> &Config {
855 &self.data.config
856 }
857
858 pub fn root(&self) -> wdl_ast::Document {
864 wdl_ast::Document::cast(SyntaxNode::new_root(
865 self.data.root.clone().expect("should have a root"),
866 ))
867 .expect("should cast")
868 }
869
870 pub fn id(&self) -> &Arc<String> {
874 &self.data.id
875 }
876
877 pub fn uri(&self) -> &Arc<Url> {
879 &self.data.uri
880 }
881
882 pub fn path(&self) -> Cow<'_, str> {
889 if let Ok(path) = self.data.uri.to_file_path() {
890 if let Some(path) = std::env::current_dir()
891 .ok()
892 .and_then(|cwd| path.strip_prefix(cwd).ok().and_then(Path::to_str))
893 {
894 return path.to_string().into();
895 }
896
897 if let Ok(path) = path.into_os_string().into_string() {
898 return path.into();
899 }
900 }
901
902 self.data.uri.as_str().into()
903 }
904
905 pub fn hash_span(&self, span: Span) -> Option<ArrayString<64>> {
913 let text = self.root().inner().text();
914 let text_len = usize::from(text.len());
915 if span.end() > text_len {
916 return None;
917 }
918 let range = TextRange::new(
919 TextSize::new(span.start() as u32),
920 TextSize::new(span.end() as u32),
921 );
922 let slice = text.slice(range);
923 let mut hasher = blake3::Hasher::new();
924 slice.for_each_chunk(|chunk| {
925 hasher.update(chunk.as_bytes());
926 });
927 Some(hasher.finalize().to_hex())
928 }
929
930 pub fn version(&self) -> Option<SupportedVersion> {
935 self.data.version
936 }
937
938 pub fn namespaces(&self) -> impl Iterator<Item = (&str, &Namespace)> {
940 self.data.namespaces.iter().map(|(n, ns)| (n.as_str(), ns))
941 }
942
943 pub fn namespace(&self, name: &str) -> Option<&Namespace> {
945 self.data.namespaces.get(name)
946 }
947
948 pub fn tasks(&self) -> impl Iterator<Item = &Task> {
950 self.data.tasks.iter().map(|(_, t)| t)
951 }
952
953 pub fn task_by_name(&self, name: &str) -> Option<&Task> {
955 self.data.tasks.get(name)
956 }
957
958 pub fn workflow(&self) -> Option<&Workflow> {
962 self.data.workflow.as_ref()
963 }
964
965 pub fn callable_by_name(&self, name: &str) -> Option<Callable<'_>> {
970 if let Some(workflow) = self.workflow()
971 && workflow.name() == name
972 {
973 return Some(Callable::Workflow(workflow));
974 }
975
976 if let Some(task) = self.task_by_name(name) {
977 return Some(Callable::Task(task));
978 }
979
980 None
981 }
982
983 pub fn structs(&self) -> impl Iterator<Item = (&str, &Struct)> {
985 self.data.structs.iter().map(|(n, s)| (n.as_str(), s))
986 }
987
988 pub fn struct_by_name(&self, name: &str) -> Option<&Struct> {
990 self.data.structs.get(name)
991 }
992
993 pub fn enums(&self) -> impl Iterator<Item = (&str, &Enum)> {
995 self.data.enums.iter().map(|(n, e)| (n.as_str(), e))
996 }
997
998 pub fn enum_by_name(&self, name: &str) -> Option<&Enum> {
1000 self.data.enums.get(name)
1001 }
1002
1003 pub fn get_custom_type(&self, name: &str) -> Option<Type> {
1005 if let Some(s) = self.struct_by_name(name) {
1006 return s.ty().cloned();
1007 }
1008
1009 if let Some(s) = self.enum_by_name(name) {
1010 return s.ty().cloned();
1011 }
1012
1013 None
1014 }
1015
1016 pub fn get_choice_cache_key(
1018 &self,
1019 name: &str,
1020 choice: &str,
1021 ) -> Option<crate::types::EnumChoiceCacheKey> {
1022 let (enum_index, _, r#enum) = self.data.enums.get_full(name)?;
1023 let enum_ty = r#enum.ty()?.as_enum()?;
1024 let choice_index = enum_ty.choices().iter().position(|v| v == choice)?;
1025 Some(crate::types::EnumChoiceCacheKey::new(
1026 enum_index,
1027 choice_index,
1028 ))
1029 }
1030
1031 pub fn parse_diagnostics(&self) -> &[Diagnostic] {
1033 &self.data.parse_diagnostics
1034 }
1035
1036 pub fn analysis_diagnostics(&self) -> &[Diagnostic] {
1038 &self.data.analysis_diagnostics
1039 }
1040
1041 pub fn diagnostics(&self) -> impl Iterator<Item = &Diagnostic> {
1043 self.data
1044 .parse_diagnostics
1045 .iter()
1046 .chain(self.data.analysis_diagnostics.iter())
1047 }
1048
1049 pub fn sort_diagnostics(&mut self) -> Self {
1055 let data = &mut self.data;
1056 let inner = Arc::get_mut(data).expect("should only have one reference");
1057 inner.parse_diagnostics.sort();
1058 inner.analysis_diagnostics.sort();
1059 Self { data: data.clone() }
1060 }
1061
1062 pub fn extend_diagnostics(&mut self, diagnostics: Vec<Diagnostic>) -> Self {
1068 let data = &mut self.data;
1069 let inner = Arc::get_mut(data).expect("should only have one reference");
1070 inner.analysis_diagnostics.extend(diagnostics);
1071 Self { data: data.clone() }
1072 }
1073
1074 pub fn find_scope_by_position(&self, position: usize) -> Option<ScopeRef<'_>> {
1076 fn find_scope(scopes: &[Scope], position: usize) -> Option<ScopeRef<'_>> {
1078 let mut index = match scopes.binary_search_by_key(&position, |s| s.span.start()) {
1079 Ok(index) => index,
1080 Err(index) => {
1081 if index == 0 {
1084 return None;
1085 }
1086
1087 index - 1
1088 }
1089 };
1090
1091 loop {
1094 let scope = &scopes[index];
1095 if scope.span.contains(position) {
1096 return Some(ScopeRef::new(scopes, ScopeIndex(index)));
1097 }
1098
1099 if index == 0 {
1100 return None;
1101 }
1102
1103 index -= 1;
1104 }
1105 }
1106
1107 if let Some(workflow) = &self.data.workflow
1109 && workflow.scope().span().contains(position)
1110 {
1111 return find_scope(&workflow.scopes, position);
1112 }
1113
1114 let task = match self
1116 .data
1117 .tasks
1118 .binary_search_by_key(&position, |_, t| t.scope().span().start())
1119 {
1120 Ok(index) => &self.data.tasks[index],
1121 Err(index) => {
1122 if index == 0 {
1125 return None;
1126 }
1127
1128 &self.data.tasks[index - 1]
1129 }
1130 };
1131
1132 if task.scope().span().contains(position) {
1133 return find_scope(&task.scopes, position);
1134 }
1135
1136 None
1137 }
1138
1139 pub fn has_errors(&self) -> bool {
1148 if self.diagnostics().any(|d| d.severity() == Severity::Error) {
1150 return true;
1151 }
1152
1153 for (_, ns) in self.namespaces() {
1155 if ns.document.has_errors() {
1156 return true;
1157 }
1158 }
1159
1160 false
1161 }
1162
1163 pub fn visit<V: crate::Visitor>(&self, diagnostics: &mut crate::Diagnostics, visitor: &mut V) {
1166 crate::visit(self, diagnostics, visitor)
1167 }
1168}