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::Context;
25use crate::diagnostics::no_common_type;
26use crate::diagnostics::unused_import;
27use crate::graph::DocumentGraph;
28use crate::graph::ParseState;
29use crate::types::CallType;
30use crate::types::Optional;
31use crate::types::Type;
32
33pub mod v1;
34
35pub const TASK_VAR_NAME: &str = "task";
38
39#[derive(Debug)]
41pub struct Namespace {
42 span: Span,
44 source: Arc<Url>,
46 document: Document,
48 used: bool,
50 excepted: bool,
53}
54
55impl Namespace {
56 pub fn span(&self) -> Span {
58 self.span
59 }
60
61 pub fn source(&self) -> &Arc<Url> {
63 &self.source
64 }
65
66 pub fn document(&self) -> &Document {
68 &self.document
69 }
70}
71
72#[derive(Debug, Clone)]
74pub struct Struct {
75 name: String,
77 name_span: Span,
82 offset: usize,
87 node: rowan::GreenNode,
91 namespace: Option<String>,
95 ty: Option<Type>,
99}
100
101impl Struct {
102 pub fn name(&self) -> &str {
104 &self.name
105 }
106
107 pub fn name_span(&self) -> Span {
109 self.name_span
110 }
111
112 pub fn offset(&self) -> usize {
114 self.offset
115 }
116
117 pub fn node(&self) -> &rowan::GreenNode {
119 &self.node
120 }
121
122 pub fn namespace(&self) -> Option<&str> {
127 self.namespace.as_deref()
128 }
129
130 pub fn ty(&self) -> Option<&Type> {
135 self.ty.as_ref()
136 }
137}
138
139#[derive(Debug, Clone)]
141pub struct Enum {
142 name: String,
144 name_span: Span,
149 offset: usize,
154 node: rowan::GreenNode,
159 namespace: Option<String>,
163 ty: Option<Type>,
167}
168
169impl Enum {
170 pub fn name(&self) -> &str {
172 &self.name
173 }
174
175 pub fn name_span(&self) -> Span {
177 self.name_span
178 }
179
180 pub fn offset(&self) -> usize {
182 self.offset
183 }
184
185 pub fn node(&self) -> &rowan::GreenNode {
187 &self.node
188 }
189
190 pub fn definition(&self) -> wdl_ast::v1::EnumDefinition {
194 wdl_ast::v1::EnumDefinition::cast(wdl_ast::SyntaxNode::new_root(self.node.clone()))
195 .expect("stored node should be a valid enum definition")
196 }
197
198 pub fn namespace(&self) -> Option<&str> {
200 self.namespace.as_deref()
201 }
202
203 pub fn ty(&self) -> Option<&Type> {
205 self.ty.as_ref()
206 }
207}
208
209#[derive(Debug, Clone)]
211pub struct Name {
212 span: Span,
214 ty: Type,
216}
217
218impl Name {
219 pub fn span(&self) -> Span {
221 self.span
222 }
223
224 pub fn ty(&self) -> &Type {
226 &self.ty
227 }
228}
229
230#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
232pub struct ScopeIndex(usize);
233
234#[derive(Debug)]
236pub struct Scope {
237 parent: Option<ScopeIndex>,
241 span: Span,
243 names: IndexMap<String, Name>,
245}
246
247impl Scope {
248 fn new(parent: Option<ScopeIndex>, span: Span) -> Self {
250 Self {
251 parent,
252 span,
253 names: Default::default(),
254 }
255 }
256
257 pub fn insert(&mut self, name: impl Into<String>, span: Span, ty: Type) {
259 self.names.insert(name.into(), Name { span, ty });
260 }
261}
262
263#[derive(Debug, Clone, Copy)]
265pub struct ScopeRef<'a> {
266 scopes: &'a [Scope],
268 index: ScopeIndex,
270}
271
272impl<'a> ScopeRef<'a> {
273 fn new(scopes: &'a [Scope], index: ScopeIndex) -> Self {
275 Self { scopes, index }
276 }
277
278 pub fn span(&self) -> Span {
280 self.scopes[self.index.0].span
281 }
282
283 pub fn parent(&self) -> Option<Self> {
287 self.scopes[self.index.0].parent.map(|p| Self {
288 scopes: self.scopes,
289 index: p,
290 })
291 }
292
293 pub fn names(&self) -> impl Iterator<Item = (&str, &Name)> + use<'_> {
295 self.scopes[self.index.0]
296 .names
297 .iter()
298 .map(|(name, n)| (name.as_str(), n))
299 }
300
301 pub fn local(&self, name: &str) -> Option<&Name> {
305 self.scopes[self.index.0].names.get(name)
306 }
307
308 pub fn lookup(&self, name: &str) -> Option<&Name> {
312 let mut current = Some(self.index);
313
314 while let Some(index) = current {
315 if let Some(name) = self.scopes[index.0].names.get(name) {
316 return Some(name);
317 }
318
319 current = self.scopes[index.0].parent;
320 }
321
322 None
323 }
324}
325
326#[derive(Debug)]
328struct ScopeRefMut<'a> {
329 scopes: &'a mut [Scope],
331 index: ScopeIndex,
333}
334
335impl<'a> ScopeRefMut<'a> {
336 fn new(scopes: &'a mut [Scope], index: ScopeIndex) -> Self {
338 Self { scopes, index }
339 }
340
341 pub fn lookup(&self, name: &str) -> Option<&Name> {
345 let mut current = Some(self.index);
346
347 while let Some(index) = current {
348 if let Some(name) = self.scopes[index.0].names.get(name) {
349 return Some(name);
350 }
351
352 current = self.scopes[index.0].parent;
353 }
354
355 None
356 }
357
358 pub fn insert(&mut self, name: impl Into<String>, span: Span, ty: Type) {
360 self.scopes[self.index.0]
361 .names
362 .insert(name.into(), Name { span, ty });
363 }
364
365 pub fn as_scope_ref(&'a self) -> ScopeRef<'a> {
367 ScopeRef {
368 scopes: self.scopes,
369 index: self.index,
370 }
371 }
372}
373
374#[derive(Debug)]
379pub struct ScopeUnion<'a> {
380 scope_refs: Vec<(ScopeRef<'a>, bool)>,
382}
383
384impl<'a> ScopeUnion<'a> {
385 pub fn new() -> Self {
387 Self {
388 scope_refs: Vec::new(),
389 }
390 }
391
392 pub fn insert(&mut self, scope_ref: ScopeRef<'a>, exhaustive: bool) {
394 self.scope_refs.push((scope_ref, exhaustive));
395 }
396
397 pub fn resolve(self) -> Result<HashMap<String, Name>, Vec<Diagnostic>> {
402 let mut errors = Vec::new();
403 let mut ignored: HashSet<String> = HashSet::new();
404
405 let mut names: HashMap<String, Name> = HashMap::new();
407 for (scope_ref, _) in &self.scope_refs {
408 for (name, info) in scope_ref.names() {
409 if ignored.contains(name) {
410 continue;
411 }
412
413 match names.entry(name.to_string()) {
414 Entry::Vacant(entry) => {
415 entry.insert(info.clone());
416 }
417 Entry::Occupied(mut entry) => {
418 let Some(ty) = entry.get().ty.common_type(&info.ty) else {
419 errors.push(no_common_type(
420 &entry.get().ty,
421 entry.get().span,
422 &info.ty,
423 info.span,
424 ));
425 names.remove(name);
426 ignored.insert(name.to_string());
427 continue;
428 };
429
430 entry.get_mut().ty = ty;
431 }
432 }
433 }
434 }
435
436 for (scope_ref, _) in &self.scope_refs {
438 for (name, info) in &mut names {
439 if ignored.contains(name) {
440 continue;
441 }
442
443 if scope_ref.local(name).is_none() {
445 info.ty = info.ty.optional();
446 }
447 }
448 }
449
450 let has_exhaustive = self.scope_refs.iter().any(|(_, exhaustive)| *exhaustive);
452 if !has_exhaustive {
453 for info in names.values_mut() {
454 info.ty = info.ty.optional();
455 }
456 }
457
458 if !errors.is_empty() {
459 return Err(errors);
460 }
461
462 Ok(names)
463 }
464}
465
466#[derive(Debug, Clone, PartialEq, Eq)]
468pub struct Input {
469 ty: Type,
471 required: bool,
476}
477
478impl Input {
479 pub fn ty(&self) -> &Type {
481 &self.ty
482 }
483
484 pub fn required(&self) -> bool {
486 self.required
487 }
488}
489
490#[derive(Debug, Clone, PartialEq, Eq)]
492pub struct Output {
493 ty: Type,
495 name_span: Span,
497}
498
499impl Output {
500 pub(crate) fn new(ty: Type, name_span: Span) -> Self {
502 Self { ty, name_span }
503 }
504
505 pub fn ty(&self) -> &Type {
507 &self.ty
508 }
509
510 pub fn name_span(&self) -> Span {
512 self.name_span
513 }
514}
515
516#[derive(Debug)]
518pub struct Task {
519 name_span: Span,
521 name: String,
523 scopes: Vec<Scope>,
529 inputs: Arc<IndexMap<String, Input>>,
531 outputs: Arc<IndexMap<String, Output>>,
533}
534
535impl Task {
536 pub fn name(&self) -> &str {
538 &self.name
539 }
540
541 pub fn name_span(&self) -> Span {
543 self.name_span
544 }
545
546 pub fn scope(&self) -> ScopeRef<'_> {
548 ScopeRef::new(&self.scopes, ScopeIndex(0))
549 }
550
551 pub fn inputs(&self) -> &IndexMap<String, Input> {
553 &self.inputs
554 }
555
556 pub fn outputs(&self) -> &IndexMap<String, Output> {
558 &self.outputs
559 }
560}
561
562#[derive(Debug)]
564pub struct Workflow {
565 name_span: Span,
567 name: String,
569 scopes: Vec<Scope>,
575 inputs: Arc<IndexMap<String, Input>>,
577 outputs: Arc<IndexMap<String, Output>>,
579 calls: HashMap<String, CallType>,
581 allows_nested_inputs: bool,
583}
584
585impl Workflow {
586 pub fn name(&self) -> &str {
588 &self.name
589 }
590
591 pub fn name_span(&self) -> Span {
593 self.name_span
594 }
595
596 pub fn scope(&self) -> ScopeRef<'_> {
598 ScopeRef::new(&self.scopes, ScopeIndex(0))
599 }
600
601 pub fn inputs(&self) -> &IndexMap<String, Input> {
603 &self.inputs
604 }
605
606 pub fn outputs(&self) -> &IndexMap<String, Output> {
608 &self.outputs
609 }
610
611 pub fn calls(&self) -> &HashMap<String, CallType> {
613 &self.calls
614 }
615
616 pub fn allows_nested_inputs(&self) -> bool {
618 self.allows_nested_inputs
619 }
620}
621
622#[derive(Debug)]
624pub(crate) struct DocumentData {
625 config: Config,
627 root: Option<GreenNode>,
631 id: Arc<String>,
635 uri: Arc<Url>,
637 version: Option<SupportedVersion>,
639 namespaces: IndexMap<String, Namespace>,
641 tasks: IndexMap<String, Task>,
643 workflow: Option<Workflow>,
645 structs: IndexMap<String, Struct>,
647 enums: IndexMap<String, Enum>,
649 parse_diagnostics: Vec<Diagnostic>,
651 analysis_diagnostics: Vec<Diagnostic>,
653}
654
655impl DocumentData {
656 fn new(
658 config: Config,
659 uri: Arc<Url>,
660 root: Option<GreenNode>,
661 version: Option<SupportedVersion>,
662 diagnostics: Vec<Diagnostic>,
663 ) -> Self {
664 Self {
665 config,
666 root,
667 id: Uuid::new_v4().to_string().into(),
668 uri,
669 version,
670 namespaces: Default::default(),
671 tasks: Default::default(),
672 workflow: Default::default(),
673 structs: Default::default(),
674 enums: Default::default(),
675 parse_diagnostics: diagnostics,
676 analysis_diagnostics: Default::default(),
677 }
678 }
679
680 pub fn context(&self, name: &str) -> Option<Context> {
686 if let Some(ns) = self.namespaces.get(name) {
688 Some(Context::Namespace(ns.span()))
689 } else if let Some(task) = self.tasks.get(name) {
690 Some(Context::Task(task.name_span()))
691 } else if let Some(wf) = &self.workflow
692 && wf.name == name
693 {
694 Some(Context::Workflow(wf.name_span()))
695 } else if let Some(s) = self.structs.get(name) {
696 Some(Context::Struct(s.name_span()))
697 } else {
698 self.enums.get(name).map(|e| Context::Enum(e.name_span()))
700 }
701 }
702}
703
704#[derive(Debug, Clone)]
708pub struct Document {
709 data: Arc<DocumentData>,
711}
712
713impl Document {
714 pub(crate) fn default_from_uri(uri: Arc<Url>) -> Self {
716 Self {
717 data: Arc::new(DocumentData::new(
718 Default::default(),
719 uri,
720 None,
721 None,
722 Default::default(),
723 )),
724 }
725 }
726
727 pub(crate) fn from_graph_node(
729 config: &Config,
730 graph: &DocumentGraph,
731 index: NodeIndex,
732 ) -> Self {
733 let node = graph.get(index);
734
735 let (wdl_version, diagnostics) = match node.parse_state() {
736 ParseState::NotParsed => panic!("node should have been parsed"),
737 ParseState::Error(_) => return Self::default_from_uri(node.uri().clone()),
738 ParseState::Parsed {
739 wdl_version,
740 diagnostics,
741 ..
742 } => (wdl_version, diagnostics),
743 };
744
745 let root = node.root().expect("node should have been parsed");
746 let (config, wdl_version) = match (root.version_statement(), wdl_version) {
747 (Some(stmt), Some(wdl_version)) => (
748 config.with_diagnostics_config(
749 config.diagnostics_config().excepted_for_node(stmt.inner()),
750 ),
751 *wdl_version,
752 ),
753 _ => {
754 return Self {
757 data: Arc::new(DocumentData::new(
758 config.clone(),
759 node.uri().clone(),
760 Some(root.inner().green().into()),
761 None,
762 diagnostics.to_vec(),
763 )),
764 };
765 }
766 };
767
768 let mut data = DocumentData::new(
769 config.clone(),
770 node.uri().clone(),
771 Some(root.inner().green().into()),
772 Some(wdl_version),
773 diagnostics.to_vec(),
774 );
775 match root.ast_with_version_fallback(config.fallback_version()) {
776 Ast::Unsupported => {}
777 Ast::V1(ast) => v1::populate_document(&mut data, &config, graph, index, &ast),
778 }
779
780 if let Some(severity) = config.diagnostics_config().unused_import {
782 let DocumentData {
783 namespaces,
784 analysis_diagnostics,
785 ..
786 } = &mut data;
787
788 analysis_diagnostics.extend(
789 namespaces
790 .iter()
791 .filter(|(_, ns)| !ns.used && !ns.excepted)
792 .map(|(name, ns)| unused_import(name, ns.span()).with_severity(severity)),
793 );
794 }
795
796 Self {
797 data: Arc::new(data),
798 }
799 }
800
801 pub fn config(&self) -> &Config {
803 &self.data.config
804 }
805
806 pub fn root(&self) -> wdl_ast::Document {
812 wdl_ast::Document::cast(SyntaxNode::new_root(
813 self.data.root.clone().expect("should have a root"),
814 ))
815 .expect("should cast")
816 }
817
818 pub fn id(&self) -> &Arc<String> {
822 &self.data.id
823 }
824
825 pub fn uri(&self) -> &Arc<Url> {
827 &self.data.uri
828 }
829
830 pub fn path(&self) -> Cow<'_, str> {
837 if let Ok(path) = self.data.uri.to_file_path() {
838 if let Some(path) = std::env::current_dir()
839 .ok()
840 .and_then(|cwd| path.strip_prefix(cwd).ok().and_then(Path::to_str))
841 {
842 return path.to_string().into();
843 }
844
845 if let Ok(path) = path.into_os_string().into_string() {
846 return path.into();
847 }
848 }
849
850 self.data.uri.as_str().into()
851 }
852
853 pub fn version(&self) -> Option<SupportedVersion> {
858 self.data.version
859 }
860
861 pub fn namespaces(&self) -> impl Iterator<Item = (&str, &Namespace)> {
863 self.data.namespaces.iter().map(|(n, ns)| (n.as_str(), ns))
864 }
865
866 pub fn namespace(&self, name: &str) -> Option<&Namespace> {
868 self.data.namespaces.get(name)
869 }
870
871 pub fn tasks(&self) -> impl Iterator<Item = &Task> {
873 self.data.tasks.iter().map(|(_, t)| t)
874 }
875
876 pub fn task_by_name(&self, name: &str) -> Option<&Task> {
878 self.data.tasks.get(name)
879 }
880
881 pub fn workflow(&self) -> Option<&Workflow> {
885 self.data.workflow.as_ref()
886 }
887
888 pub fn structs(&self) -> impl Iterator<Item = (&str, &Struct)> {
890 self.data.structs.iter().map(|(n, s)| (n.as_str(), s))
891 }
892
893 pub fn struct_by_name(&self, name: &str) -> Option<&Struct> {
895 self.data.structs.get(name)
896 }
897
898 pub fn enums(&self) -> impl Iterator<Item = (&str, &Enum)> {
900 self.data.enums.iter().map(|(n, e)| (n.as_str(), e))
901 }
902
903 pub fn enum_by_name(&self, name: &str) -> Option<&Enum> {
905 self.data.enums.get(name)
906 }
907
908 pub fn get_custom_type(&self, name: &str) -> Option<Type> {
910 if let Some(s) = self.struct_by_name(name) {
911 return s.ty().cloned();
912 }
913
914 if let Some(s) = self.enum_by_name(name) {
915 return s.ty().cloned();
916 }
917
918 None
919 }
920
921 pub fn get_variant_cache_key(
923 &self,
924 name: &str,
925 variant: &str,
926 ) -> Option<crate::types::EnumVariantCacheKey> {
927 let (enum_index, _, r#enum) = self.data.enums.get_full(name)?;
928 let enum_ty = r#enum.ty()?.as_enum()?;
929 let variant_index = enum_ty.variants().iter().position(|v| v == variant)?;
930 Some(crate::types::EnumVariantCacheKey::new(
931 enum_index,
932 variant_index,
933 ))
934 }
935
936 pub fn parse_diagnostics(&self) -> &[Diagnostic] {
938 &self.data.parse_diagnostics
939 }
940
941 pub fn analysis_diagnostics(&self) -> &[Diagnostic] {
943 &self.data.analysis_diagnostics
944 }
945
946 pub fn diagnostics(&self) -> impl Iterator<Item = &Diagnostic> {
948 self.data
949 .parse_diagnostics
950 .iter()
951 .chain(self.data.analysis_diagnostics.iter())
952 }
953
954 pub fn sort_diagnostics(&mut self) -> Self {
960 let data = &mut self.data;
961 let inner = Arc::get_mut(data).expect("should only have one reference");
962 inner.parse_diagnostics.sort();
963 inner.analysis_diagnostics.sort();
964 Self { data: data.clone() }
965 }
966
967 pub fn extend_diagnostics(&mut self, diagnostics: Vec<Diagnostic>) -> Self {
973 let data = &mut self.data;
974 let inner = Arc::get_mut(data).expect("should only have one reference");
975 inner.analysis_diagnostics.extend(diagnostics);
976 Self { data: data.clone() }
977 }
978
979 pub fn find_scope_by_position(&self, position: usize) -> Option<ScopeRef<'_>> {
981 fn find_scope(scopes: &[Scope], position: usize) -> Option<ScopeRef<'_>> {
983 let mut index = match scopes.binary_search_by_key(&position, |s| s.span.start()) {
984 Ok(index) => index,
985 Err(index) => {
986 if index == 0 {
989 return None;
990 }
991
992 index - 1
993 }
994 };
995
996 loop {
999 let scope = &scopes[index];
1000 if scope.span.contains(position) {
1001 return Some(ScopeRef::new(scopes, ScopeIndex(index)));
1002 }
1003
1004 if index == 0 {
1005 return None;
1006 }
1007
1008 index -= 1;
1009 }
1010 }
1011
1012 if let Some(workflow) = &self.data.workflow
1014 && workflow.scope().span().contains(position)
1015 {
1016 return find_scope(&workflow.scopes, position);
1017 }
1018
1019 let task = match self
1021 .data
1022 .tasks
1023 .binary_search_by_key(&position, |_, t| t.scope().span().start())
1024 {
1025 Ok(index) => &self.data.tasks[index],
1026 Err(index) => {
1027 if index == 0 {
1030 return None;
1031 }
1032
1033 &self.data.tasks[index - 1]
1034 }
1035 };
1036
1037 if task.scope().span().contains(position) {
1038 return find_scope(&task.scopes, position);
1039 }
1040
1041 None
1042 }
1043
1044 pub fn has_errors(&self) -> bool {
1053 if self.diagnostics().any(|d| d.severity() == Severity::Error) {
1055 return true;
1056 }
1057
1058 for (_, ns) in self.namespaces() {
1060 if ns.document.has_errors() {
1061 return true;
1062 }
1063 }
1064
1065 false
1066 }
1067
1068 pub fn visit<V: crate::Visitor>(&self, diagnostics: &mut crate::Diagnostics, visitor: &mut V) {
1071 crate::visit(self, diagnostics, visitor)
1072 }
1073}