Skip to content

Commit

Permalink
Merge pull request #283 from ronitnallagatla/match_filename
Browse files Browse the repository at this point in the history
New rule that ensures module/interface/package/program identifier matches the filename it's in
  • Loading branch information
dalance authored Jun 3, 2024
2 parents 0530ce6 + 9f8472e commit 65fffe0
Show file tree
Hide file tree
Showing 20 changed files with 630 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/target
**/*.rs.bk
/Cargo.lock
.svlint.toml
.vscode
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,4 @@ under the Developer Certificate of Origin <https://developercertificate.org/>.
- Taichi Ishitani (@taichi-ishitani)
- Sosuke Hosokawa (@so298)
- Jan Remes (@remes-codasip)
- Shantanu Sinha (@ShantanuPSinha)
189 changes: 189 additions & 0 deletions MANUAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -2179,6 +2179,53 @@ The most relevant clauses of IEEE1800-2017 are:



* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

## Syntax Rule: `interface_identifier_matches_filename`

### Hint

Ensure that the interface name matches the file name. Interface Bar should be in some/path/to/Bar.sv

### Reason

Encourages consistent file naming standards for packages and assists in searching for interfaces.

### Pass Example (1 of 1)
```systemverilog
interface syntaxrules;
endinterface
// This testcase, when executed, is called from a file named "syntaxrules.interface_identifier_matches_filename.pass.1of1"
// The rule matches all valid characters up until the first non-identifier (in this case, the period).
// The file identifier to be matched in this case becomes "syntaxrules" which matches the interface identifier
```

### Fail Example (1 of 1)
```systemverilog
interface Bar;
endinterface
```

### Explanation

Interface identifier should have the same name as the file it's in.

```interface foo;``` is allowed to live in any file of naming convention ```foo <Non-Identifier> <whatever else>```

According to Clause 5.6 of IEEE 1800-2017:

> A simple identifier shall consist of a sequence of letters, digits, dollar signs (`$`), and underscore (`_`) characters.
Any symbol defined outside this exhaustive list is considered a non-identifier.

The stopping point for string matching has to be a non-identifier character.

For example, the interface declaration ```interface foo;``` is valid in filenames such as ```foo-Bar.sv```, ```foo.debug.sv```, and ```foo-final-version.sv```. Each of these filenames begins with the interface identifier ```foo``` and is immediately followed by a non-identifier character (```-```, ```.```, or another acceptable symbol), making them compliant. A filename like ```FooBar.sv``` is invalid for the ```interface Foo;``` declaration since it does not contain a non-identifier character following the interface name.

Note that as a consequence, only one interface can be declared per file.


* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

## Syntax Rule: `interface_port_with_modport`
Expand Down Expand Up @@ -3640,6 +3687,53 @@ The most relevant clauses of IEEE1800-2017 are:



* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

## Syntax Rule: `module_identifier_matches_filename`

### Hint

Ensure that the module name matches the file name. module Bar should be in some/path/to/Bar.sv

### Reason

Encourages consistent file naming standards for packages and assists in searching for modules.

### Pass Example (1 of 1)
```systemverilog
module syntaxrules;
endmodule
// This testcase, when executed, is called from a file named "syntaxrules.module_identifier_matches_filename.pass.1of1"
// The rule matches all valid characters up until the first non-identifier (in this case, the period).
// The file identifier to be matched in this case becomes "syntaxrules" which matches the module identifier
```

### Fail Example (1 of 1)
```systemverilog
module Bar;
endmodule
```

### Explanation

Module identifier should have the same name as the file it's in.

```module foo;``` is allowed to live in any file of naming convention ```foo <Non-Identifier> <whatever else>```

According to Clause 5.6 of IEEE 1800-2017:

> A simple identifier shall consist of a sequence of letters, digits, dollar signs (`$`), and underscore (`_`) characters.
Any symbol defined outside this exhaustive list is considered a non-identifier.

The stopping point for string matching has to be a non-identifier character.

For example, the module declaration ```module foo;``` is valid in filenames such as ```foo-Bar.sv```, ```foo.debug.sv```, and ```foo-final-version.sv```. Each of these filenames begins with the module identifier ```foo``` and is immediately followed by a non-identifier character (```-```, ```.```, or another acceptable symbol), making them compliant. A filename like ```FooBar.sv``` is invalid for the ```module Foo;``` declaration since it does not contain a non-identifier character following the module name.

Note that as a consequence, only one module can be declared per file.


* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

## Syntax Rule: `module_nonansi_forbidden`
Expand Down Expand Up @@ -4429,6 +4523,54 @@ The most relevant clauses of IEEE1800-2017 are:



* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

## Syntax Rule: `package_identifier_matches_filename`

### Hint

Ensure that the package name name matches the file name. Package fooBar should be in some/path/to/fooBar.sv

### Reason

Encourages consistent file naming standards for packages and assists in searching for packages.

### Pass Example (1 of 1)
```systemverilog
package syntaxrules;
endpackage
// This testcase, when executed, is called from a file named "syntaxrules.package_identifier_matches_filename.pass.1of1"
// The rule matches all valid characters up until the first non-identifier (in this case, the period).
// The file identifier to be matched in this case becomes "syntaxrules" which matches the package identifier
```

### Fail Example (1 of 1)
```systemverilog
package fooBar;
endpackage
```

### Explanation

Package identifier should have the same name as the file it's in.

```package foo;``` is allowed to live in any file of naming convention ```foo <Non-Identifier> <whatever else>```

According to Clause 5.6 of IEEE 1800-2017:

> A simple identifier shall consist of a sequence of letters, digits, dollar signs (`$`), and underscore (`_`) characters.
Any symbol defined outside this exhaustive list is considered a non-identifier.

The stopping point for string matching has to be a non-identifier character.

For example, the package declaration ```package foo;``` is valid in filenames such as ```foo-Bar.sv```, ```foo.debug.sv```, and ```foo-final-version.sv```. Each of these filenames begins with the package identifier ```foo``` and is immediately followed by a non-identifier character (```-```, ```.```, or another acceptable symbol), making them compliant. A filename like ```FooBar.sv``` is invalid for the ```package Foo;``` declaration since it does not contain a non-identifier character following the package name.

Note that as a consequence, only one package can be declared per file.


* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

## Syntax Rule: `package_item_not_in_package`
Expand Down Expand Up @@ -4910,6 +5052,53 @@ The most relevant clauses of IEEE1800-2017 are:



* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

## Syntax Rule: `program_identifier_matches_filename`

### Hint

Ensure that the program name matches the file name. program Bar should be in some/path/to/Bar.sv

### Reason

Encourages consistent file naming standards for packages and assists in searching for programs.

### Pass Example (1 of 1)
```systemverilog
program syntaxrules;
endprogram
// This testcase, when executed, is called from a file named "syntaxrules.program_identifier_matches_filename.pass.1of1"
// The rule matches all valid characters up until the first non-identifier (in this case, the period).
// The file identifier to be matched in this case becomes "syntaxrules" which matches the program identifier
```

### Fail Example (1 of 1)
```systemverilog
program Bar;
endprogram
```

### Explanation

Program identifier should have the same name as the file it's in.

```program foo;``` is allowed to live in any file of naming convention ```foo <Non-Identifier> <whatever else>```

According to Clause 5.6 of IEEE 1800-2017:

> A simple identifier shall consist of a sequence of letters, digits, dollar signs (`$`), and underscore (`_`) characters.
Any symbol defined outside this exhaustive list is considered a non-identifier.

The stopping point for string matching has to be a non-identifier character.

For example, the program declaration ```program foo;``` is valid in filenames such as ```foo-Bar.sv```, ```foo.debug.sv```, and ```foo-final-version.sv```. Each of these filenames begins with the program identifier ```foo``` and is immediately followed by a non-identifier character (```-```, ```.```, or another acceptable symbol), making them compliant. A filename like ```FooBar.sv``` is invalid for the ```program Foo;``` declaration since it does not contain a non-identifier character following the program name.

Note that as a consequence, only one program can be declared per file.


* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

## Syntax Rule: `sequential_block_in_always_comb`
Expand Down
2 changes: 1 addition & 1 deletion build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@ fn write_test_rs(

for (t, testcase) in testcases.into_iter().enumerate().map(|(i, x)| (i + 1, x)) {
// Write subtest to its own file.
let subtest_path = Path::new(&out_dir)
let subtest_path: std::path::PathBuf = Path::new(&out_dir)
.join(format!("syntaxrules.{rulename}.{passfail}.{t}of{n_testcases}.sv"));
let mut out_subtest = File::create(&subtest_path).unwrap();
for line in testcase {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Interface identifier should have the same name as the file it's in.

```interface foo;``` is allowed to live in any file of naming convention ```foo <Non-Identifier> <whatever else>```

According to Clause 5.6 of IEEE 1800-2017:

> A simple identifier shall consist of a sequence of letters, digits, dollar signs (`$`), and underscore (`_`) characters.
Any symbol defined outside this exhaustive list is considered a non-identifier.

The stopping point for string matching has to be a non-identifier character.

For example, the interface declaration ```interface foo;``` is valid in filenames such as ```foo-Bar.sv```, ```foo.debug.sv```, and ```foo-final-version.sv```. Each of these filenames begins with the interface identifier ```foo``` and is immediately followed by a non-identifier character (```-```, ```.```, or another acceptable symbol), making them compliant. A filename like ```FooBar.sv``` is invalid for the ```interface Foo;``` declaration since it does not contain a non-identifier character following the interface name.

Note that as a consequence, only one interface can be declared per file.
15 changes: 15 additions & 0 deletions md/syntaxrules-explanation-module_identifier_matches_filename.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Module identifier should have the same name as the file it's in.

```module foo;``` is allowed to live in any file of naming convention ```foo <Non-Identifier> <whatever else>```

According to Clause 5.6 of IEEE 1800-2017:

> A simple identifier shall consist of a sequence of letters, digits, dollar signs (`$`), and underscore (`_`) characters.
Any symbol defined outside this exhaustive list is considered a non-identifier.

The stopping point for string matching has to be a non-identifier character.

For example, the module declaration ```module foo;``` is valid in filenames such as ```foo-Bar.sv```, ```foo.debug.sv```, and ```foo-final-version.sv```. Each of these filenames begins with the module identifier ```foo``` and is immediately followed by a non-identifier character (```-```, ```.```, or another acceptable symbol), making them compliant. A filename like ```FooBar.sv``` is invalid for the ```module Foo;``` declaration since it does not contain a non-identifier character following the module name.

Note that as a consequence, only one module can be declared per file.
15 changes: 15 additions & 0 deletions md/syntaxrules-explanation-package_identifier_matches_filename.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Package identifier should have the same name as the file it's in.

```package foo;``` is allowed to live in any file of naming convention ```foo <Non-Identifier> <whatever else>```

According to Clause 5.6 of IEEE 1800-2017:

> A simple identifier shall consist of a sequence of letters, digits, dollar signs (`$`), and underscore (`_`) characters.
Any symbol defined outside this exhaustive list is considered a non-identifier.

The stopping point for string matching has to be a non-identifier character.

For example, the package declaration ```package foo;``` is valid in filenames such as ```foo-Bar.sv```, ```foo.debug.sv```, and ```foo-final-version.sv```. Each of these filenames begins with the package identifier ```foo``` and is immediately followed by a non-identifier character (```-```, ```.```, or another acceptable symbol), making them compliant. A filename like ```FooBar.sv``` is invalid for the ```package Foo;``` declaration since it does not contain a non-identifier character following the package name.

Note that as a consequence, only one package can be declared per file.
15 changes: 15 additions & 0 deletions md/syntaxrules-explanation-program_identifier_matches_filename.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Program identifier should have the same name as the file it's in.

```program foo;``` is allowed to live in any file of naming convention ```foo <Non-Identifier> <whatever else>```

According to Clause 5.6 of IEEE 1800-2017:

> A simple identifier shall consist of a sequence of letters, digits, dollar signs (`$`), and underscore (`_`) characters.
Any symbol defined outside this exhaustive list is considered a non-identifier.

The stopping point for string matching has to be a non-identifier character.

For example, the program declaration ```program foo;``` is valid in filenames such as ```foo-Bar.sv```, ```foo.debug.sv```, and ```foo-final-version.sv```. Each of these filenames begins with the program identifier ```foo``` and is immediately followed by a non-identifier character (```-```, ```.```, or another acceptable symbol), making them compliant. A filename like ```FooBar.sv``` is invalid for the ```program Foo;``` declaration since it does not contain a non-identifier character following the program name.

Note that as a consequence, only one program can be declared per file.
88 changes: 88 additions & 0 deletions src/syntaxrules/interface_identifier_matches_filename.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use crate::config::ConfigOption;
use crate::linter::{SyntaxRule, SyntaxRuleResult};
use sv_parser::{unwrap_locate, NodeEvent, RefNode, SyntaxTree, unwrap_node, Locate};

#[derive(Default)]
pub struct InterfaceIdentifierMatchesFilename;
impl SyntaxRule for InterfaceIdentifierMatchesFilename {
fn check(
&mut self,
syntax_tree: &SyntaxTree,
event: &NodeEvent,
_option: &ConfigOption,
) -> SyntaxRuleResult {
let node = match event {
NodeEvent::Enter(x) => x,
NodeEvent::Leave(_) => {
return SyntaxRuleResult::Pass;
}
};

match node {
RefNode::InterfaceIdentifier(x) => {
let path_str = if let Some(x) = unwrap_locate!(node.clone()) {
if let Some((path, _)) = syntax_tree.get_origin(&x) {
path
} else {
return SyntaxRuleResult::Fail;
}
} else {
return SyntaxRuleResult::Fail;
};

let id: Option<&Locate> = match unwrap_node!(*x, SimpleIdentifier) {
Some(RefNode::SimpleIdentifier(id_)) => {
unwrap_locate!(id_)
},
_ => None,
};

if id.is_none() {
return SyntaxRuleResult::Fail;
}

let interface_name = syntax_tree.get_str(id.unwrap()).unwrap();

let path = std::path::Path::new(path_str);
if let Some(file_name_os_str) = path.file_name() {
if let Some(file_name) = file_name_os_str.to_str() {
let mut identifier_end = 0;
for (i, c) in file_name.char_indices() {
if c.is_alphanumeric() || c == '_' || c == '$' {
identifier_end = i + c.len_utf8();
} else {
// Stop at the first non-identifier character
break;
}
}

let file_ident = &file_name[..identifier_end];

// Ignoring Case
if file_ident.eq_ignore_ascii_case(interface_name) {
return SyntaxRuleResult::Pass;
}
}
}

SyntaxRuleResult::Fail
},

_ => SyntaxRuleResult::Pass,
}

}

fn name(&self) -> String {
String::from("interface_identifier_matches_filename")
}

fn hint(&self, _option: &ConfigOption) -> String {
String::from("Ensure that the interface name matches the file name. Interface Bar should be in some/path/to/Bar.sv")
}

fn reason(&self) -> String {
String::from("Encourages consistent file naming standards for packages and assists in searching for interfaces.")
}

}
Loading

0 comments on commit 65fffe0

Please sign in to comment.