ink_analyzer/analysis/
diagnostics.rs

1//! Diagnostic errors and warnings based on ink! semantic rules.
2//!
3//! # Note
4//! The [ink_ir crate](https://github.com/paritytech/ink/tree/v4.1.0/crates/ink/ir)
5//! is used as a reference implementation for ink!'s semantic rules.
6//!
7//! References to the source of enforced semantic rules are included
8//! either in the rustdoc for most utilities or at the call site for more generic utilities.
9//!
10//! ## Methodology for extracting ink! semantic rules
11//! 1. Start by reviewing each ink! attribute macro's definition in the
12//!    [ink_macro crate](https://github.com/paritytech/ink/blob/v4.1.0/crates/ink/macro/src/lib.rs).
13//! 2. Then for each ink! attribute macro, extract its semantic rules from its corresponding
14//!    [ink_ir](https://github.com/paritytech/ink/blob/v4.1.0/crates/ink/ir/src/lib.rs) type,
15//!    the types, utilities and modules it uses, as well as related unit tests.
16//!
17//! Using the [`#[ink::contract]` attribute macro](https://github.com/paritytech/ink/blob/v4.1.0/crates/ink/macro/src/lib.rs#L517-L520)
18//! as an example, from its
19//! [implementation in the ink_macro crate](https://github.com/paritytech/ink/blob/v4.1.0/crates/ink/macro/src/contract.rs#L20-L30),
20//! we can trace it's corresponding `ink_ir` type as the
21//! [Contract struct in the contract module of the ink_ir crate](https://github.com/paritytech/ink/blob/v4.1.0/crates/ink/ir/src/ir/contract.rs).
22//!
23//! We can then extract the semantic rules by recursively analyzing the types, utilities and modules
24//! used in the [Contract struct definition](https://github.com/paritytech/ink/blob/v4.1.0/crates/ink/ir/src/ir/contract.rs#L35-L44)
25//! and [its constructor](https://github.com/paritytech/ink/blob/v4.1.0/crates/ink/ir/src/ir/contract.rs#L61-L73)
26//! as well as related unit tests.
27
28mod common;
29mod file;
30
31mod chain_extension;
32mod contract;
33mod contract_ref;
34mod environment;
35mod error;
36mod event;
37mod ink_e2e_test;
38mod ink_test;
39mod message;
40mod storage_item;
41mod trait_definition;
42
43use ink_analyzer_ir::syntax::TextRange;
44use ink_analyzer_ir::InkFile;
45use itertools::Itertools;
46
47use crate::analysis::text_edit;
48use crate::{Action, TextEdit, Version};
49
50/// A diagnostic error or warning.
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub struct Diagnostic {
53    /// Error or warning message.
54    pub message: String,
55    /// Text range to highlight.
56    pub range: TextRange,
57    /// The severity level of the diagnostic.
58    pub severity: Severity,
59    /// Quickfixes (suggested edits/actions) for the diagnostic (if any).
60    pub quickfixes: Option<Vec<Action>>,
61}
62
63/// The severity level of a diagnostic.
64#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
65pub enum Severity {
66    /// A diagnostic error.
67    Error,
68    /// A diagnostic warning.
69    Warning,
70}
71
72/// Runs diagnostics for the source file.
73pub fn diagnostics(file: &InkFile, version: Version) -> Vec<Diagnostic> {
74    let mut results = Vec::new();
75    file::diagnostics(&mut results, file, version);
76    results
77        .into_iter()
78        // Deduplicate by range, severity and quickfix edits.
79        .unique_by(|item| {
80            let quickfix_edits: Option<Vec<TextEdit>> = item
81                .quickfixes
82                .as_ref()
83                .map(|it| it.iter().flat_map(|it| it.edits.clone()).collect());
84            (item.range, item.severity, quickfix_edits)
85        })
86        // Format edits.
87        .map(|diagnostic| Diagnostic {
88            quickfixes: diagnostic.quickfixes.map(|fixes| {
89                fixes
90                    .into_iter()
91                    .map(|action| Action {
92                        edits: text_edit::format_edits(action.edits.into_iter(), file).collect(),
93                        ..action
94                    })
95                    .collect()
96            }),
97            ..diagnostic
98        })
99        .collect()
100}