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}