use crate::puppet_parser::range::Range;
use serde::{Deserialize, Serialize};
use crate::puppet_pp_lint::lint::{EarlyLintPass, LintError, LintPass};
#[derive(Clone, Serialize, Deserialize)]
pub struct InvalidStringEscape;
impl LintPass for InvalidStringEscape {
fn name(&self) -> &str {
"InvalidStringEscape"
}
fn description(&self) -> &str {
"Checks if only allowed characters are escaped in strings"
}
}
impl EarlyLintPass for InvalidStringEscape {
fn check_string_expression(
&self,
elt: &crate::puppet_lang::string::StringExpr<Range>,
) -> Vec<super::lint::LintError> {
let list = match &elt.data {
crate::puppet_lang::string::StringVariant::SingleQuoted(list) => list.clone(),
crate::puppet_lang::string::StringVariant::DoubleQuoted(list) => list
.clone()
.into_iter()
.filter_map(|elt| match elt {
crate::puppet_lang::string::DoubleQuotedFragment::StringFragment(elt) => Some(elt),
crate::puppet_lang::string::DoubleQuotedFragment::Expression(_) => None,
})
.collect::<Vec<_>>(),
};
let mut errors = Vec::new();
for fragment in list {
if let crate::puppet_lang::string::StringFragment::Escaped(c) = fragment {
match &elt.data {
crate::puppet_lang::string::StringVariant::SingleQuoted(_) => {
if c.data != '\'' && c.data != '\\' {
errors.push(LintError::new_with_url(
Box::new(self.clone()),
&format!("Unexpected escaped character {:?}", c.data),
"https://puppet.com/docs/puppet/7/lang_data_string.html#lang_data_string_single_quoted_strings-escape-sequences",
&c.extra,
))
}
}
crate::puppet_lang::string::StringVariant::DoubleQuoted(_) => {
if c.data != 'n'
&& c.data != 'r'
&& c.data != 't'
&& c.data != 's'
&& c.data != '$'
&& c.data != 'b'
&& c.data != 'f'
&& c.data != '\\'
&& c.data != '\"'
&& c.data != '\''
{
errors.push(LintError::new_with_url(
Box::new(self.clone()),
&format!("Unexpected escaped character {:?}", c.data),
"https://puppet.com/docs/puppet/7/lang_data_string.html#lang_data_string_double_quoted_strings-escape-sequences",
&c.extra,
))
}
}
}
}
}
errors
}
}
#[derive(Clone, Serialize, Deserialize)]
pub struct UselessDoubleQuotes;
impl LintPass for UselessDoubleQuotes {
fn name(&self) -> &str {
"UselessDoubleQuotes"
}
fn description(&self) -> &str {
"Warns if double quoted string has no interpolated expressions and no escaped single quotes"
}
}
impl EarlyLintPass for UselessDoubleQuotes {
fn check_string_expression(
&self,
elt: &crate::puppet_lang::string::StringExpr<Range>,
) -> Vec<super::lint::LintError> {
let s = match &elt.data {
crate::puppet_lang::string::StringVariant::SingleQuoted(_) => return Vec::new(),
crate::puppet_lang::string::StringVariant::DoubleQuoted(elt) => elt,
};
let mut is_useful = false;
for fragment in s {
match fragment {
crate::puppet_lang::string::DoubleQuotedFragment::StringFragment(fragment) => {
match fragment {
crate::puppet_lang::string::StringFragment::Literal(_)
| crate::puppet_lang::string::StringFragment::EscapedUTF(_) => {}
crate::puppet_lang::string::StringFragment::Escaped(c) if c.data == '\'' => {
is_useful = true
}
crate::puppet_lang::string::StringFragment::Escaped(_) => {}
}
}
crate::puppet_lang::string::DoubleQuotedFragment::Expression(_) => return Vec::new(),
}
}
if !is_useful {
return vec![LintError::new(
Box::new(self.clone()),
"Double quoted string with no interpolated values and no escaped double quotes",
&elt.extra,
)];
}
vec![]
}
}
#[derive(Clone, Serialize, Deserialize)]
pub struct ExpressionInSingleQuotes;
impl LintPass for ExpressionInSingleQuotes {
fn name(&self) -> &str {
"ExpressionInSingleQuotes"
}
fn description(&self) -> &str {
"Warns if interpolated expression found single-qouted string"
}
}
impl EarlyLintPass for ExpressionInSingleQuotes {
fn check_string_expression(
&self,
elt: &crate::puppet_lang::string::StringExpr<Range>,
) -> Vec<super::lint::LintError> {
let v = match &elt.data {
crate::puppet_lang::string::StringVariant::SingleQuoted(v) => v,
crate::puppet_lang::string::StringVariant::DoubleQuoted(_) => return vec![],
};
for fragment in v {
match fragment {
crate::puppet_lang::string::StringFragment::Literal(elt) => {
if !elt.data.contains('$') {
continue;
}
for v in elt.data.split('$') {
match v.chars().next() {
None => (),
Some(c) => {
if c.is_alphanumeric() || c == '_' {
return vec![LintError::new(
Box::new(self.clone()),
"Possibly interpolated expression in single quotes",
&elt.extra,
)];
}
}
}
}
}
crate::puppet_lang::string::StringFragment::EscapedUTF(_) => (),
crate::puppet_lang::string::StringFragment::Escaped(_) => (),
}
}
vec![]
}
}