#![cfg_attr(coverage_nightly, coverage(off))]
use crate::services::complexity::ComplexityMetrics;
pub(crate) struct BraceState {
brace_count: i32,
found_first_brace: bool,
in_string: bool,
in_block_comment: bool,
in_raw_string: bool,
raw_hashes: usize,
escape_next: bool,
}
impl BraceState {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub(crate) fn new() -> Self {
Self {
brace_count: 0,
found_first_brace: false,
in_string: false,
in_block_comment: false,
in_raw_string: false,
raw_hashes: 0,
escape_next: false,
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub(crate) fn process_line(&mut self, chars: &[char], handle_raw_strings: bool) -> bool {
let len = chars.len();
let mut j = 0;
while j < len {
if self.escape_next {
self.escape_next = false;
j += 1;
continue;
}
if self.in_block_comment || self.in_string || self.in_raw_string {
j = self.advance_literal(chars, j);
continue;
}
j = self.advance_normal(chars, j, handle_raw_strings);
if self.found_first_brace && self.brace_count == 0 {
return true;
}
}
false
}
fn advance_literal(&mut self, chars: &[char], j: usize) -> usize {
let len = chars.len();
let ch = chars[j];
if self.in_block_comment {
if ch == '*' && j + 1 < len && chars[j + 1] == '/' {
self.in_block_comment = false;
return j + 2;
}
return j + 1;
}
if self.in_string {
if ch == '\\' {
self.escape_next = true;
} else if ch == '"' {
self.in_string = false;
}
return j + 1;
}
if ch == '"' {
let h = chars[j + 1..].iter().take_while(|&&c| c == '#').count();
if h >= self.raw_hashes {
self.in_raw_string = false;
return j + 1 + self.raw_hashes;
}
}
j + 1
}
fn advance_normal(&mut self, chars: &[char], j: usize, handle_raw_strings: bool) -> usize {
let len = chars.len();
let ch = chars[j];
if ch == '/' && j + 1 < len && chars[j + 1] == '/' {
return len;
}
if ch == '/' && j + 1 < len && chars[j + 1] == '*' {
self.in_block_comment = true;
return j + 2;
}
if ch == '\'' && j + 2 < len {
if chars[j + 1] == '\\' && j + 3 < len && chars[j + 3] == '\'' {
return j + 4;
}
if chars[j + 2] == '\'' {
return j + 3;
}
}
if handle_raw_strings && ch == 'r' {
let h = chars[j + 1..].iter().take_while(|&&c| c == '#').count();
let qp = j + 1 + h;
if qp < len && chars[qp] == '"' {
self.in_raw_string = true;
self.raw_hashes = h;
return qp + 1;
}
}
if ch == '"' {
self.in_string = true;
return j + 1;
}
if ch == '{' {
self.brace_count += 1;
self.found_first_brace = true;
} else if ch == '}' {
self.brace_count -= 1;
}
j + 1
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub(crate) fn find_brace_balanced_end(
lines: &[&str],
start: usize,
handle_raw_strings: bool,
) -> usize {
let mut state = BraceState::new();
for (i, line) in lines.iter().enumerate().skip(start) {
let chars: Vec<char> = line.chars().collect();
if state.process_line(&chars, handle_raw_strings) {
return i;
}
}
lines.len() - 1
}
pub(crate) struct ComplexityVisitor {
cyclomatic: u16,
cognitive: u16,
nesting: u8,
max_nesting: u8,
lines: u16,
}
impl ComplexityVisitor {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub(crate) fn new() -> Self {
Self {
cyclomatic: 1, cognitive: 0,
nesting: 0,
max_nesting: 0,
lines: 0,
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub(crate) fn analyze_lines(&mut self, lines: &[&str]) {
self.lines = lines.len() as u16;
for line in lines {
let trimmed = line.trim();
if self.is_control_flow(trimmed) {
self.cyclomatic += 1;
self.cognitive += 1 + u16::from(self.nesting);
}
if trimmed.contains("else") {
self.cyclomatic += 1;
self.cognitive += 1;
}
if trimmed.ends_with('{') || trimmed.ends_with(':') {
self.nesting += 1;
self.max_nesting = self.max_nesting.max(self.nesting);
}
if trimmed.starts_with('}') || (trimmed.is_empty() && self.nesting > 0) {
self.nesting = self.nesting.saturating_sub(1);
}
}
}
fn is_control_flow(&self, line: &str) -> bool {
line.contains("if ")
|| line.contains("while ")
|| line.contains("for ")
|| line.contains("match ")
|| line.contains("switch ")
|| line.contains("case ")
|| line.contains("elif ")
|| line.contains("except ")
|| line.contains("catch ")
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub(crate) fn into_metrics(self) -> ComplexityMetrics {
ComplexityMetrics {
cyclomatic: self.cyclomatic.min(255),
cognitive: self.cognitive.min(255),
nesting_max: self.max_nesting,
lines: self.lines,
halstead: None,
}
}
}