1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
use std::convert::Infallible;

use codespan_reporting::diagnostic::Severity;
use itertools::Itertools;

use kodept_ast::ExpressionBlock;
use kodept_ast::visitor::TraversingResult;
use kodept_ast::visitor::visit_side::{RefVisitGuard, VisitSide};
use kodept_core::impl_named;
use kodept_core::structure::{Located, rlt};
use kodept_core::structure::rlt::Variable;

use crate::analyzer::Analyzer;
use crate::error::report::ReportMessage;
use crate::traits::Context;

#[derive(Debug)]
pub struct VariableUniquenessAnalyzer;

impl_named!(VariableUniquenessAnalyzer);

pub struct DuplicatedVariable(String);

impl From<DuplicatedVariable> for ReportMessage {
    fn from(value: DuplicatedVariable) -> Self {
        Self::new(
            Severity::Error,
            "SE002",
            format!("Variable `{}` has duplicates in one block", value.0),
        )
    }
}

impl Analyzer for VariableUniquenessAnalyzer {
    type Error = Infallible;
    type Node<'n> = &'n ExpressionBlock;

    fn analyze<'n, 'c, C: Context<'c>>(
        &self,
        guard: RefVisitGuard<Self::Node<'n>>,
        context: &mut C,
    ) -> TraversingResult<Self::Error> {
        let (node, token) = guard.allow_only(VisitSide::Exiting)?;
        let tree = context.tree();
        let variables = node
            .items(&tree, &token)
            .into_iter()
            .filter_map(|it| it.as_init_var())
            .group_by(|it| &it.variable(&tree, &token).name);

        for (name, variables) in variables.into_iter() {
            let variables = variables.collect_vec();
            if variables.len() > 1 {
                context.add_report(
                    variables
                        .into_iter()
                        .filter_map(|it| context.access(it))
                        .map(|it: &rlt::InitializedVariable| match &it.variable {
                            Variable::Immutable { id, .. } | Variable::Mutable { id, .. } => {
                                id.location()
                            }
                        })
                        .collect(),
                    DuplicatedVariable(name.clone()),
                )
            }
        }

        Ok(())
    }
}