Skip to content

Commit

Permalink
feat: double_quotes (#45)
Browse files Browse the repository at this point in the history
Co-authored-by: Andrew Thrasher <adthrasher@gmail.com>
  • Loading branch information
simojoe and adthrasher committed May 30, 2024
1 parent d474775 commit 285cffa
Show file tree
Hide file tree
Showing 6 changed files with 236 additions and 1 deletion.
32 changes: 31 additions & 1 deletion Arena.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ commit_hash = "7dd6c4c7c8c9fc6dcd4eb5ff8f3262cd26d5e7cc"

[repositories."stjudecloud/workflows"]
identifier = "stjudecloud/workflows"
commit_hash = "511a7358402c4ce24864cf837156df400fc94da2"
commit_hash = "08e3d49c7fb10086fe516feb2f400c1d65ef6100"

[[concerns]]
kind = "LintWarning"
Expand All @@ -37,6 +37,36 @@ kind = "LintWarning"
document = "ENCODE-DCC/chip-seq-pipeline2:/dev/test/test_task/compare_md5sum.wdl"
message = "[v1::W003::[Completeness]::Medium] missing parameter meta within task: ref_files (6:6-6:27)"

[[concerns]]
kind = "LintWarning"
document = "ENCODE-DCC/chip-seq-pipeline2:/dev/test/test_task/compare_md5sum.wdl"
message = "[v1::W012::[Style, Clarity]::Low] string defined with single quotes (84:45-84:56)"

[[concerns]]
kind = "LintWarning"
document = "ENCODE-DCC/chip-seq-pipeline2:/dev/test/test_task/compare_md5sum.wdl"
message = "[v1::W012::[Style, Clarity]::Low] string defined with single quotes (85:46-85:65)"

[[concerns]]
kind = "LintWarning"
document = "ENCODE-DCC/chip-seq-pipeline2:/dev/test/test_task/compare_md5sum.wdl"
message = "[v1::W012::[Style, Clarity]::Low] string defined with single quotes (86:26-86:39)"

[[concerns]]
kind = "LintWarning"
document = "ENCODE-DCC/chip-seq-pipeline2:/dev/test/test_task/compare_md5sum.wdl"
message = "[v1::W012::[Style, Clarity]::Low] string defined with single quotes (87:39-87:52)"

[[concerns]]
kind = "LintWarning"
document = "ENCODE-DCC/chip-seq-pipeline2:/dev/test/test_task/compare_md5sum.wdl"
message = "[v1::W012::[Style, Clarity]::Low] string defined with single quotes (91:18-91:27)"

[[concerns]]
kind = "LintWarning"
document = "ENCODE-DCC/chip-seq-pipeline2:/dev/test/test_task/compare_md5sum.wdl"
message = "[v1::W012::[Style, Clarity]::Low] string defined with single quotes (93:17-93:36)"

[[concerns]]
kind = "LintWarning"
document = "getwilds/ww-fastq-to-cram:/ww-fastq-to-cram.wdl"
Expand Down
1 change: 1 addition & 0 deletions RULES.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ repository. Note that the information may be out of sync with released packages.
| `document_preamble` | `v1::W009` | Spacing | [`wdl-grammar`][wdl-grammar-lints] |
| `preamble_comment` | `v1::W010` | Style | [`wdl-grammar`][wdl-grammar-lints] |
| `one_empty_line` | `v1::W011` | Spacing | [`wdl-grammar`][wdl-grammar-lints] |
| `double_quotes` | `v1::W012` | Naming | [`wdl-grammar`][wdl-grammar-lints] |

[wdl-ast-lints]: https://docs.rs/wdl-ast/latest/wdl_ast/v1/index.html#lint-rules
[wdl-ast-validation]: https://docs.rs/wdl-ast/latest/wdl_ast/v1/index.html#validation-rules
Expand Down
2 changes: 2 additions & 0 deletions wdl-grammar/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
contributed by @simojoe).
* Adds the `one_empty_line` rule that ensures no excess of empty lines
(#33, contributed by @simojoe).
* Adds the `double_quotes` rule for quote styling in string declarations
(contributed by @simojoe).

### Changed

Expand Down
1 change: 1 addition & 0 deletions wdl-grammar/src/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
//! | `document_preamble` | `v1::W009` | Spacing | [Link](lint::DocumentPreamble) |
//! | `preamble_comment` | `v1::W010` | Style | [Link](lint::PreambleComment) |
//! | `one_empty_line` | `v1::W011` | Spacing | [Link](lint::OneEmptyLine) |
//! | `double_quotes ` | `v1::W012` | Naming | [Link](lint::DoubleQuotes) |

use pest::iterators::Pair;
use pest::Parser as _;
Expand Down
4 changes: 4 additions & 0 deletions wdl-grammar/src/v1/lint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use pest::iterators::Pair;

mod document_preamble;
mod double_quotes;
mod missing_runtime_block;
mod mixed_indentation;
mod newline_eof;
Expand All @@ -13,6 +14,7 @@ mod snake_case;
mod whitespace;

pub use document_preamble::DocumentPreamble;
pub use double_quotes::DoubleQuotes;
pub use missing_runtime_block::MissingRuntimeBlock;
pub use mixed_indentation::MixedIndentation;
pub use newline_eof::NewlineEOF;
Expand Down Expand Up @@ -43,5 +45,7 @@ pub fn rules<'a>() -> Vec<Box<dyn wdl_core::concern::lint::Rule<&'a Pair<'a, cra
Box::new(PreambleComment),
// v1::W011
Box::new(OneEmptyLine),
// v1::W012
Box::new(DoubleQuotes),
]
}
197 changes: 197 additions & 0 deletions wdl-grammar/src/v1/lint/double_quotes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
//! All quotes should be double quotes.

use std::collections::VecDeque;

use nonempty::NonEmpty;
use pest::iterators::Pair;
use wdl_core::concern::code;
use wdl_core::concern::lint;
use wdl_core::concern::lint::Rule;
use wdl_core::concern::lint::TagSet;
use wdl_core::concern::Code;
use wdl_core::file::Location;
use wdl_core::Version;

use crate::v1;

/// Detects strings that are not defined with double quotes.
#[derive(Debug)]
pub struct DoubleQuotes;

impl<'a> DoubleQuotes {
/// Creates a warning for strings defined using single-quotes
fn single_quote_string(&self, location: Location) -> lint::Warning
where
Self: Rule<&'a Pair<'a, v1::Rule>>,
{
// SAFETY: this error is written so that it will always unwrap.
lint::warning::Builder::default()
.code(self.code())
.level(lint::Level::Low)
.tags(self.tags())
.push_location(location)
.subject("string defined with single quotes")
.body("All strings should be defined using double quotes.")
.fix("Change the single quotes to double quotes.")
.try_build()
.unwrap()
}
}

impl<'a> Rule<&'a Pair<'a, v1::Rule>> for DoubleQuotes {
fn code(&self) -> Code {
// SAFETY: this manually crafted to unwrap successfully every time.
Code::try_new(code::Kind::Warning, Version::V1, 12).unwrap()
}

fn tags(&self) -> lint::TagSet {
TagSet::new(&[lint::Tag::Clarity, lint::Tag::Style])
}

fn check(&self, tree: &'a Pair<'_, v1::Rule>) -> lint::Result {
let mut warnings = VecDeque::new();

for node in tree.clone().into_inner().flatten() {
if node.as_rule() == v1::Rule::string && node.as_str().starts_with('\'') {
let location = Location::try_from(node.as_span()).map_err(lint::Error::Location)?;
warnings.push_back(self.single_quote_string(location));
}
}

match warnings.pop_front() {
Some(front) => {
let mut results = NonEmpty::new(front);
results.extend(warnings);
Ok(Some(results))
}
None => Ok(None),
}
}
}

#[cfg(test)]
mod tests {

use pest::Parser as _;
use wdl_core::concern::lint::Rule as _;

use super::*;
use crate::v1::parse::Parser;
use crate::v1::Rule;

#[test]
fn it_ignores_a_correctly_formatted_import() -> Result<(), Box<dyn std::error::Error>> {
let tree = Parser::parse(Rule::import, r#"import "wdl-common/wdl/structs.wdl""#)?
.next()
.unwrap();

assert!(DoubleQuotes.check(&tree)?.is_none());
Ok(())
}

#[test]
fn it_catches_a_single_quote_import() -> Result<(), Box<dyn std::error::Error>> {
let tree = Parser::parse(Rule::import, r#"import 'wdl-common/wdl/structs.wdl'"#)?
.next()
.unwrap();
let warnings = DoubleQuotes.check(&tree)?.unwrap();

assert_eq!(warnings.len(), 1);
assert_eq!(
warnings.first().to_string(),
"[v1::W012::[Style, Clarity]::Low] string defined with single quotes (1:8-1:36)"
);

Ok(())
}

#[test]
fn it_catches_a_single_quote_bound_declaration() -> Result<(), Box<dyn std::error::Error>> {
let tree = Parser::parse(Rule::bound_declaration, r#"String bad_string = 'bad'"#)?
.next()
.unwrap();
let warnings = DoubleQuotes.check(&tree)?.unwrap();

assert_eq!(warnings.len(), 1);
assert_eq!(
warnings.first().to_string(),
"[v1::W012::[Style, Clarity]::Low] string defined with single quotes (1:21-1:26)"
);

Ok(())
}

#[test]
fn it_catches_a_single_quote_task_metadata() -> Result<(), Box<dyn std::error::Error>> {
let tree = Parser::parse(
Rule::task,
r#"task sort {
meta {
description: 'Sorts'
}
command <<< >>>
}"#,
)?
.next()
.unwrap();
let warnings = DoubleQuotes.check(&tree)?.unwrap();

assert_eq!(warnings.len(), 1);
assert_eq!(
warnings.first().to_string(),
"[v1::W012::[Style, Clarity]::Low] string defined with single quotes (3:22-3:29)"
);

Ok(())
}

#[test]
fn it_catches_placeholder_string() -> Result<(), Box<dyn std::error::Error>> {
let tree = Parser::parse(
Rule::bound_declaration,
r#"String nested = "Hello ~{if alien then 'world' else planet}!""#,
)?
.next()
.unwrap();
let warnings = DoubleQuotes.check(&tree)?.unwrap();

assert_eq!(warnings.len(), 1);
assert_eq!(
warnings.first().to_string(),
"[v1::W012::[Style, Clarity]::Low] string defined with single quotes (1:40-1:47)"
);

Ok(())
}

#[test]
fn it_catches_placeholder_string2() -> Result<(), Box<dyn std::error::Error>> {
let tree = Parser::parse(
Rule::document,
r#"version 1.1
task foo {
command <<<
echo 'this Bash string should be ignored'
echo "~{if foo then 'this should be flagged' else 'this one too'}"
>>>
}
"#,
)?
.next()
.unwrap();
let warnings = DoubleQuotes.check(&tree)?.unwrap();

assert_eq!(warnings.len(), 2);
assert_eq!(
warnings.first().to_string(),
"[v1::W012::[Style, Clarity]::Low] string defined with single quotes (5:41-5:65)"
);
assert_eq!(
warnings.last().to_string(),
"[v1::W012::[Style, Clarity]::Low] string defined with single quotes (5:71-5:85)"
);

Ok(())
}
}

0 comments on commit 285cffa

Please sign in to comment.