Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalidate specs with identifiers that reference missing rule definitions #93

Merged
merged 7 commits into from
Sep 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,13 @@ docker run -it --rm -v $PWD:/cddl -w /cddl ghcr.io/anweiss/cddl-cli:<version> he
You can validate JSON documents:

```sh
cddl validate --cddl <FILE.cddl> [FILE.json]...
cddl validate --cddl <FILE.cddl> --json [FILE.json]...
```

You can validate CBOR files:

```sh
cddl validate --cddl <FILE.cddl> [FILE.cbor]...
cddl validate --cddl <FILE.cddl> --cbor [FILE.cbor]...
```

It also supports validating files from STDIN (if it detects the input as valid UTF-8, it will attempt to validate the input as JSON, otherwise it will treat it as CBOR):
Expand Down
2 changes: 1 addition & 1 deletion src/bin/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ fn main() -> Result<(), Box<dyn Error>> {
let file_content = fs::read_to_string(c)?;
cddl_from_str(&mut lexer_from_str(&file_content), &file_content, true).map(|_| ())?;

error!("{} is conformant", c);
info!("{} is conformant", c);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ use alloc::string::String;
#[cfg_attr(target_arch = "wasm32", derive(Serialize))]
#[derive(Debug, Clone)]
pub struct ErrorMsg {
short: String,
extended: Option<String>,
pub short: String,
pub extended: Option<String>,
}

impl fmt::Display for ErrorMsg {
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,13 +112,13 @@
//! You can validate JSON documents:
//!
//! ```sh
//! cddl validate --cddl <FILE.cddl> [FILE.json]...
//! cddl validate --cddl <FILE.cddl> --json [FILE.json]...
//! ```
//!
//! You can validate CBOR files:
//!
//! ```sh
//! cddl validate --cddl <FILE.cddl> [FILE.cbor]...
//! cddl validate --cddl <FILE.cddl> --cbor [FILE.cbor]...
//! ```
//!
//! It also supports validating files from STDIN (if it detects the input as
Expand Down
217 changes: 216 additions & 1 deletion src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ where
parser_position: Position,
/// Vec of collected parsing errors
pub errors: Vec<Error>,
#[cfg(feature = "ast-span")]
visited_rule_idents: Vec<(&'a str, Span)>,
#[cfg(not(feature = "ast-span"))]
visited_rule_idents: Vec<&'a str>,
current_rule_generic_param_idents: Option<Vec<&'a str>>,
}

/// Parsing error types
Expand Down Expand Up @@ -116,6 +121,8 @@ where
peek_lexer_position: Position::default(),
#[cfg(feature = "ast-span")]
parser_position: Position::default(),
visited_rule_idents: Vec::default(),
current_rule_generic_param_idents: None,
};

p.next_token()?;
Expand Down Expand Up @@ -372,6 +379,36 @@ where
}
}

#[cfg(feature = "ast-span")]
for (rule, span) in self.visited_rule_idents.iter() {
if !c.rules.iter().any(|r| r.name() == *rule) {
self.errors.push(Error::PARSER {
position: Position {
column: 0,
index: span.0,
line: span.2,
range: (span.0, span.1),
},
msg: ErrorMsg {
short: format!("missing definition for rule {}", rule),
extended: None,
},
})
}
}

#[cfg(not(feature = "ast-span"))]
for rule in self.visited_rule_idents.iter() {
if !c.rules.iter().any(|r| r.name() == *rule) {
self.errors.push(Error::PARSER {
msg: ErrorMsg {
short: format!("missing definition for rule {}", rule),
extended: None,
},
})
}
}

// TODO: implement second pass over parenthesized type rules whose contents
// are Type2::Typename, and if the identifier refers to another group rule
// per the match rules in Appendix C, refactor rule into a group rule:
Expand Down Expand Up @@ -433,7 +470,16 @@ where
let gp = if self.peek_token_is(&Token::LANGLEBRACKET) {
self.next_token()?;

Some(self.parse_genericparm()?)
let params = self.parse_genericparm()?;
let mut param_list = Vec::default();

for param in params.params.iter() {
param_list.push(param.param.ident);
}

self.current_rule_generic_param_idents = Some(param_list);

Some(params)
} else {
None
};
Expand Down Expand Up @@ -532,6 +578,8 @@ where
begin_rule_line,
);

self.current_rule_generic_param_idents = None;

Ok(Rule::Group {
rule: Box::from(GroupRule {
name: ident,
Expand Down Expand Up @@ -571,6 +619,8 @@ where
#[cfg(not(feature = "ast-comments"))]
self.advance_newline()?;

self.current_rule_generic_param_idents = None;

Ok(Rule::Type {
rule: TypeRule {
name: ident,
Expand Down Expand Up @@ -647,6 +697,8 @@ where
end_rule_range = self.parser_position.range.1;
}

self.current_rule_generic_param_idents = None;

return Ok(Rule::Type {
rule: TypeRule {
name: ident,
Expand All @@ -672,6 +724,8 @@ where
}
}

self.current_rule_generic_param_idents = None;

Ok(Rule::Group {
rule: Box::from(GroupRule {
name: ident,
Expand Down Expand Up @@ -736,6 +790,8 @@ where
begin_rule_line,
);

self.current_rule_generic_param_idents = None;

Ok(Rule::Type {
rule: TypeRule {
name: ident,
Expand Down Expand Up @@ -1835,6 +1891,7 @@ where
&& !self.peek_token_is(&Token::COLON)
&& !self.peek_token_is(&Token::ARROWMAP)
&& !self.cur_token_is(Token::EOF)
&& !matches!(self.cur_token, Token::IDENT(_))
{
#[cfg(feature = "ast-span")]
{
Expand Down Expand Up @@ -1977,6 +2034,20 @@ where

#[cfg(feature = "ast-span")]
if let Some((name, generic_args, _)) = entry_type.groupname_entry() {
if name.socket.is_none()
&& token::lookup_ident(name.ident)
.in_standard_prelude()
.is_none()
{
if let Some(params) = &self.current_rule_generic_param_idents {
if !params.iter().any(|&p| p == name.ident) {
self.visited_rule_idents.push((name.ident, name.span));
}
} else {
self.visited_rule_idents.push((name.ident, name.span));
}
}

return Ok(GroupEntry::TypeGroupname {
ge: TypeGroupnameEntry {
occur,
Expand All @@ -1993,6 +2064,20 @@ where

#[cfg(not(feature = "ast-span"))]
if let Some((name, generic_args)) = entry_type.groupname_entry() {
if name.socket.is_none()
&& token::lookup_ident(name.ident)
.in_standard_prelude()
.is_none()
{
if let Some(params) = &self.current_rule_generic_param_idents {
if !params.iter().any(|&p| p == name.ident) {
self.visited_rule_idents.push(name.ident);
}
} else {
self.visited_rule_idents.push(name.ident);
}
}

return Ok(GroupEntry::TypeGroupname {
ge: TypeGroupnameEntry {
occur,
Expand Down Expand Up @@ -2020,6 +2105,40 @@ where
comments_after_type_or_group
};

#[cfg(feature = "ast-span")]
if let Some((ident, _, _)) = entry_type.groupname_entry() {
if ident.socket.is_none()
&& token::lookup_ident(ident.ident)
.in_standard_prelude()
.is_none()
{
if let Some(params) = &self.current_rule_generic_param_idents {
if !params.iter().any(|&p| p == ident.ident) {
self.visited_rule_idents.push((ident.ident, ident.span));
}
} else {
self.visited_rule_idents.push((ident.ident, ident.span));
}
}
}

#[cfg(not(feature = "ast-span"))]
if let Some((ident, _)) = entry_type.groupname_entry() {
if ident.socket.is_none()
&& token::lookup_ident(ident.ident)
.in_standard_prelude()
.is_none()
{
if let Some(params) = &self.current_rule_generic_param_idents {
if !params.iter().any(|&p| p == ident.ident) {
self.visited_rule_idents.push(ident.ident);
}
} else {
self.visited_rule_idents.push(ident.ident);
}
}
}

Ok(GroupEntry::ValueMemberKey {
ge: Box::from(ValueMemberKeyEntry {
occur,
Expand Down Expand Up @@ -2076,6 +2195,40 @@ where
span.1 = self.lexer_position.range.1;
}

#[cfg(feature = "ast-span")]
if let Some((ident, _, _)) = entry_type.groupname_entry() {
if ident.socket.is_none()
&& token::lookup_ident(ident.ident)
.in_standard_prelude()
.is_none()
{
if let Some(params) = &self.current_rule_generic_param_idents {
if !params.iter().any(|&p| p == ident.ident) {
self.visited_rule_idents.push((ident.ident, ident.span));
}
} else {
self.visited_rule_idents.push((ident.ident, ident.span));
}
}
}

#[cfg(not(feature = "ast-span"))]
if let Some((ident, _)) = entry_type.groupname_entry() {
if ident.socket.is_none()
&& token::lookup_ident(ident.ident)
.in_standard_prelude()
.is_none()
{
if let Some(params) = &self.current_rule_generic_param_idents {
if !params.iter().any(|&p| p == ident.ident) {
self.visited_rule_idents.push(ident.ident);
}
} else {
self.visited_rule_idents.push(ident.ident);
}
}
}

Ok(GroupEntry::ValueMemberKey {
ge: Box::from(ValueMemberKeyEntry {
occur,
Expand Down Expand Up @@ -2125,6 +2278,20 @@ where
self.next_token()?;
}

if name.socket.is_none()
&& token::lookup_ident(name.ident)
.in_standard_prelude()
.is_none()
{
if let Some(params) = &self.current_rule_generic_param_idents {
if !params.iter().any(|&p| p == name.ident) {
self.visited_rule_idents.push((name.ident, name.span));
}
} else {
self.visited_rule_idents.push((name.ident, name.span));
}
}

return Ok(GroupEntry::TypeGroupname {
ge: TypeGroupnameEntry {
occur,
Expand All @@ -2149,6 +2316,20 @@ where
self.next_token()?;
}

if name.socket.is_none()
&& token::lookup_ident(name.ident)
.in_standard_prelude()
.is_none()
{
if let Some(params) = &self.current_rule_generic_param_idents {
if !params.iter().any(|&p| p == name.ident) {
self.visited_rule_idents.push(name.ident);
}
} else {
self.visited_rule_idents.push(name.ident);
}
}

return Ok(GroupEntry::TypeGroupname {
ge: TypeGroupnameEntry {
occur,
Expand All @@ -2162,6 +2343,40 @@ where
});
}

#[cfg(feature = "ast-span")]
if let Some((ident, _, _)) = entry_type.groupname_entry() {
if ident.socket.is_none()
&& token::lookup_ident(ident.ident)
.in_standard_prelude()
.is_none()
{
if let Some(params) = &self.current_rule_generic_param_idents {
if !params.iter().any(|&p| p == ident.ident) {
self.visited_rule_idents.push((ident.ident, ident.span));
}
} else {
self.visited_rule_idents.push((ident.ident, ident.span));
}
}
}

#[cfg(not(feature = "ast-span"))]
if let Some((ident, _)) = entry_type.groupname_entry() {
if ident.socket.is_none()
&& token::lookup_ident(ident.ident)
.in_standard_prelude()
.is_none()
{
if let Some(params) = &self.current_rule_generic_param_idents {
if !params.iter().any(|&p| p == ident.ident) {
self.visited_rule_idents.push(ident.ident);
}
} else {
self.visited_rule_idents.push(ident.ident);
}
}
}

Ok(GroupEntry::ValueMemberKey {
ge: Box::from(ValueMemberKeyEntry {
occur,
Expand Down
1 change: 1 addition & 0 deletions src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,7 @@ pub fn lookup_ident(ident: &str) -> Token {
"number" => Token::NUMBER,
"biguint" => Token::BIGUINT,
"bignint" => Token::BIGNINT,
"bigint" => Token::BIGINT,
"integer" => Token::INTEGER,
"unsigned" => Token::UNSIGNED,
"decfrac" => Token::DECFRAC,
Expand Down
Loading