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 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
//! Diagnostic errors and warnings based on ink! semantic rules.
//!
//! # Note
//! The [ink_ir crate](https://github.com/paritytech/ink/tree/v4.1.0/crates/ink/ir)
//! is used as a reference implementation for ink!'s semantic rules.
//!
//! References to the source of enforced semantic rules are included
//! either in the rustdoc for most utilities or at the call site for more generic utilities.
//!
//! ## Methodology for extracting ink! semantic rules
//! 1. Start by reviewing each ink! attribute macro's definition in the
//! [ink_macro crate](https://github.com/paritytech/ink/blob/v4.1.0/crates/ink/macro/src/lib.rs).
//! 2. Then for each ink! attribute macro, extract its semantic rules from its corresponding
//! [ink_ir](https://github.com/paritytech/ink/blob/v4.1.0/crates/ink/ir/src/lib.rs) type,
//! the types, utilities and modules it uses, as well as related unit tests.
//!
//! Using the [`#[ink::contract]` attribute macro](https://github.com/paritytech/ink/blob/v4.1.0/crates/ink/macro/src/lib.rs#L517-L520)
//! as an example, from its
//! [implementation in the ink_macro crate](https://github.com/paritytech/ink/blob/v4.1.0/crates/ink/macro/src/contract.rs#L20-L30),
//! we can trace it's corresponding `ink_ir` type as the
//! [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).
//!
//! We can then extract the semantic rules by recursively analyzing the types, utilities and modules
//! used in the [Contract struct definition](https://github.com/paritytech/ink/blob/v4.1.0/crates/ink/ir/src/ir/contract.rs#L35-L44)
//! and [its constructor](https://github.com/paritytech/ink/blob/v4.1.0/crates/ink/ir/src/ir/contract.rs#L61-L73)
//! as well as related unit tests.
mod file;
mod utils;
mod chain_extension;
mod constructor;
mod contract;
mod environment;
mod event;
mod extension_fn;
mod ink_e2e_test;
mod ink_impl;
mod ink_test;
mod message;
mod storage;
mod storage_item;
mod topic;
mod trait_definition;
use ink_analyzer_ir::syntax::TextRange;
use ink_analyzer_ir::InkFile;
use itertools::Itertools;
use crate::analysis::text_edit;
use crate::{Action, TextEdit, Version};
/// A diagnostic error or warning.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Diagnostic {
/// Error or warning message.
pub message: String,
/// Text range to highlight.
pub range: TextRange,
/// The severity level of the diagnostic.
pub severity: Severity,
/// Quickfixes (suggested edits/actions) for the diagnostic (if any).
pub quickfixes: Option<Vec<Action>>,
}
/// The severity level of a diagnostic.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Severity {
/// A diagnostic error.
Error,
/// A diagnostic warning.
Warning,
}
/// Runs diagnostics for the source file.
pub fn diagnostics(file: &InkFile, version: Version) -> Vec<Diagnostic> {
let mut results = Vec::new();
file::diagnostics(&mut results, file, version);
results
.into_iter()
// Deduplicate by range, severity and quickfix edits.
.unique_by(|item| {
let quickfix_edits: Option<Vec<TextEdit>> = item
.quickfixes
.as_ref()
.map(|it| it.iter().flat_map(|it| it.edits.clone()).collect());
(item.range, item.severity, quickfix_edits)
})
// Format edits.
.map(|diagnostic| Diagnostic {
quickfixes: diagnostic.quickfixes.map(|fixes| {
fixes
.into_iter()
.map(|action| Action {
edits: text_edit::format_edits(action.edits.into_iter(), file).collect(),
..action
})
.collect()
}),
..diagnostic
})
.collect()
}