wdl_analysis/validation/
imports.rs

1//! Validation of imports.
2
3use wdl_ast::AstNode;
4use wdl_ast::Diagnostic;
5use wdl_ast::Span;
6use wdl_ast::v1;
7use wdl_ast::v1::StringPart;
8
9use crate::Diagnostics;
10use crate::VisitReason;
11use crate::Visitor;
12
13/// Creates an "empty import" diagnostic
14fn empty_import(span: Span) -> Diagnostic {
15    Diagnostic::error("import URI cannot be empty").with_highlight(span)
16}
17
18/// Creates a "placeholder in import" diagnostic
19fn placeholder_in_import(span: Span) -> Diagnostic {
20    Diagnostic::error("import URI cannot contain placeholders")
21        .with_highlight(span)
22        .with_fix("remove the placeholder")
23}
24
25/// Creates an "invalid import namespace" diagnostic
26fn invalid_import_namespace(span: Span) -> Diagnostic {
27    Diagnostic::error("import namespace is not a valid WDL identifier")
28        .with_label("a namespace cannot be derived from this import path", span)
29        .with_fix("add an `as` clause to the import to specify a namespace")
30}
31
32/// An AST visitor that ensures that imports are valid.
33#[derive(Debug, Default)]
34pub struct ImportsVisitor;
35
36impl Visitor for ImportsVisitor {
37    fn reset(&mut self) {
38        *self = Default::default();
39    }
40
41    fn import_statement(
42        &mut self,
43        diagnostics: &mut Diagnostics,
44        reason: VisitReason,
45        stmt: &v1::ImportStatement,
46    ) {
47        if reason == VisitReason::Exit {
48            return;
49        }
50
51        let uri = stmt.uri();
52        if uri.is_empty() {
53            diagnostics.add(empty_import(uri.span()));
54            return;
55        }
56
57        if uri.text().is_none() {
58            let span = uri
59                .parts()
60                .find_map(|p| match p {
61                    StringPart::Text(_) => None,
62                    StringPart::Placeholder(p) => Some(p.span()),
63                })
64                .expect("should have a placeholder span");
65
66            diagnostics.add(placeholder_in_import(span));
67            return;
68        }
69
70        if stmt.namespace().is_none() {
71            diagnostics.add(invalid_import_namespace(uri.span()));
72        }
73    }
74}