use constructor::ConstructorDefinition;
use contract::ContractDefinition;
use derive_more::{Display, From, FromStr, IsVariant, TryInto};
use enumeration::EnumDefinition;
use error::ErrorDefinition;
use event::EventDefinition;
use function::FunctionDefinition;
use lintspec_macros::AsToVariant;
use modifier::ModifierDefinition;
use serde::{Deserialize, Serialize};
use structure::StructDefinition;
use variable::VariableDeclaration;
use crate::{
definitions::{interface::InterfaceDefinition, library::LibraryDefinition},
error::Error,
lint::{Diagnostic, ItemDiagnostics, Validate, ValidationOptions},
textindex::TextRange,
};
pub mod constructor;
pub mod contract;
pub mod enumeration;
pub mod error;
pub mod event;
pub mod function;
pub mod interface;
pub mod library;
pub mod modifier;
pub mod structure;
pub mod variable;
pub trait SourceItem {
fn item_type(&self) -> ItemType;
fn parent(&self) -> Option<Parent>;
fn name(&self) -> String;
fn span(&self) -> TextRange;
}
#[derive(Debug, Clone, bon::Builder)]
#[builder(on(String, into))]
pub struct Identifier {
pub name: Option<String>,
pub span: TextRange,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum Visibility {
External,
#[default]
Internal,
Private,
Public,
}
#[derive(Debug, Clone, Copy, Default, bon::Builder)]
#[non_exhaustive]
pub struct Attributes {
pub visibility: Visibility,
pub r#override: bool,
}
#[derive(Debug, Clone, Display, Serialize, PartialEq, Eq)]
#[serde(untagged)]
pub enum Parent {
Contract(String),
Interface(String),
Library(String),
}
#[derive(Debug, From, TryInto, IsVariant, AsToVariant)]
pub enum Definition {
Contract(ContractDefinition),
Interface(InterfaceDefinition),
Library(LibraryDefinition),
Constructor(ConstructorDefinition),
Enumeration(EnumDefinition),
Error(ErrorDefinition),
Event(EventDefinition),
Function(FunctionDefinition),
Modifier(ModifierDefinition),
Struct(StructDefinition),
Variable(VariableDeclaration),
NatspecParsingError(Error),
}
impl PartialEq for Definition {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Contract(a), Self::Contract(b)) => a.span.start == b.span.start,
(Self::Interface(a), Self::Interface(b)) => a.span.start == b.span.start,
(Self::Library(a), Self::Library(b)) => a.span.start == b.span.start,
(Self::Constructor(a), Self::Constructor(b)) => a.span.start == b.span.start,
(Self::Enumeration(a), Self::Enumeration(b)) => a.span.start == b.span.start,
(Self::Error(a), Self::Error(b)) => a.span.start == b.span.start,
(Self::Event(a), Self::Event(b)) => a.span.start == b.span.start,
(Self::Function(a), Self::Function(b)) => a.span.start == b.span.start,
(Self::Modifier(a), Self::Modifier(b)) => a.span.start == b.span.start,
(Self::Struct(a), Self::Struct(b)) => a.span.start == b.span.start,
(Self::Variable(a), Self::Variable(b)) => a.span.start == b.span.start,
(
Self::NatspecParsingError(Error::NatspecParsingError { span: span_a, .. }),
Self::NatspecParsingError(Error::NatspecParsingError { span: span_b, .. }),
) => span_a.start == span_b.start,
(Self::NatspecParsingError(a), Self::NatspecParsingError(b)) => {
a.to_string() == b.to_string() }
_ => false,
}
}
}
impl Definition {
#[must_use]
pub fn span(&self) -> Option<TextRange> {
match self {
Definition::Contract(d) => Some(d.span()),
Definition::Interface(d) => Some(d.span()),
Definition::Library(d) => Some(d.span()),
Definition::Constructor(d) => Some(d.span()),
Definition::Enumeration(d) => Some(d.span()),
Definition::Error(d) => Some(d.span()),
Definition::Event(d) => Some(d.span()),
Definition::Function(d) => Some(d.span()),
Definition::Modifier(d) => Some(d.span()),
Definition::Struct(d) => Some(d.span()),
Definition::Variable(d) => Some(d.span()),
Definition::NatspecParsingError(Error::NatspecParsingError { span, .. }) => {
Some(span.clone())
}
Definition::NatspecParsingError(_) => None,
}
}
pub fn span_mut(&mut self) -> Option<&mut TextRange> {
match self {
Definition::Contract(d) => Some(&mut d.span),
Definition::Interface(d) => Some(&mut d.span),
Definition::Library(d) => Some(&mut d.span),
Definition::Constructor(d) => Some(&mut d.span),
Definition::Enumeration(d) => Some(&mut d.span),
Definition::Error(d) => Some(&mut d.span),
Definition::Event(d) => Some(&mut d.span),
Definition::Function(d) => Some(&mut d.span),
Definition::Modifier(d) => Some(&mut d.span),
Definition::Struct(d) => Some(&mut d.span),
Definition::Variable(d) => Some(&mut d.span),
Definition::NatspecParsingError(Error::NatspecParsingError { span, .. }) => Some(span),
Definition::NatspecParsingError(_) => None,
}
}
}
impl Validate for Definition {
fn validate(&self, options: &ValidationOptions) -> ItemDiagnostics {
match self {
Definition::NatspecParsingError(error) => {
let (parent, span, message) = match error {
Error::NatspecParsingError {
parent,
span,
message,
} => (parent.clone(), span.clone(), message.clone()),
_ => (None, TextRange::default(), error.to_string()),
};
ItemDiagnostics {
parent,
item_type: ItemType::ParsingError,
name: String::new(),
span: span.clone(),
diags: vec![Diagnostic { span, message }],
}
}
Definition::Contract(def) => def.validate(options),
Definition::Interface(def) => def.validate(options),
Definition::Library(def) => def.validate(options),
Definition::Constructor(def) => def.validate(options),
Definition::Enumeration(def) => def.validate(options),
Definition::Error(def) => def.validate(options),
Definition::Event(def) => def.validate(options),
Definition::Function(def) => def.validate(options),
Definition::Modifier(def) => def.validate(options),
Definition::Struct(def) => def.validate(options),
Definition::Variable(def) => def.validate(options),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Display, FromStr)]
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
#[serde(rename_all = "snake_case")]
#[display(rename_all = "snake_case")]
pub enum ContractType {
Contract,
Interface,
Library,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Display, FromStr)]
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
#[serde(rename_all = "snake_case")]
#[display(rename_all = "snake_case")]
#[from_str(rename_all = "kebab-case")] pub enum ItemType {
Contract,
Interface,
Library,
Constructor,
Enum,
Error,
Event,
#[display("function")]
PrivateFunction,
#[display("function")]
InternalFunction,
#[display("function")]
PublicFunction,
#[display("function")]
ExternalFunction,
Modifier,
ParsingError,
Struct,
#[display("variable")]
PrivateVariable,
#[display("variable")]
InternalVariable,
#[display("variable")]
PublicVariable,
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use super::*;
#[test]
fn test_contract_type_display() {
assert_eq!(ContractType::Contract.to_string(), "contract");
assert_eq!(ContractType::Interface.to_string(), "interface");
assert_eq!(ContractType::Library.to_string(), "library");
}
#[test]
fn test_contract_type_from_str() {
assert_eq!(
ContractType::from_str("contract").unwrap(),
ContractType::Contract
);
assert_eq!(
ContractType::from_str("interface").unwrap(),
ContractType::Interface
);
assert_eq!(
ContractType::from_str("library").unwrap(),
ContractType::Library
);
}
#[test]
fn test_contract_type_from_str_case_insensitive() {
assert_eq!(
ContractType::from_str("Contract").unwrap(),
ContractType::Contract
);
assert_eq!(
ContractType::from_str("INTERFACE").unwrap(),
ContractType::Interface
);
assert_eq!(
ContractType::from_str("Library").unwrap(),
ContractType::Library
);
}
#[test]
fn test_contract_type_from_str_invalid() {
assert!(ContractType::from_str("invalid").is_err());
assert!(ContractType::from_str("").is_err());
}
#[test]
fn test_item_type_display() {
assert_eq!(ItemType::Contract.to_string(), "contract");
assert_eq!(ItemType::Interface.to_string(), "interface");
assert_eq!(ItemType::Library.to_string(), "library");
assert_eq!(ItemType::Constructor.to_string(), "constructor");
assert_eq!(ItemType::Enum.to_string(), "enum");
assert_eq!(ItemType::Error.to_string(), "error");
assert_eq!(ItemType::Event.to_string(), "event");
assert_eq!(ItemType::Modifier.to_string(), "modifier");
assert_eq!(ItemType::ParsingError.to_string(), "parsing_error");
assert_eq!(ItemType::Struct.to_string(), "struct");
assert_eq!(ItemType::PrivateFunction.to_string(), "function");
assert_eq!(ItemType::InternalFunction.to_string(), "function");
assert_eq!(ItemType::PublicFunction.to_string(), "function");
assert_eq!(ItemType::ExternalFunction.to_string(), "function");
assert_eq!(ItemType::PrivateVariable.to_string(), "variable");
assert_eq!(ItemType::InternalVariable.to_string(), "variable");
assert_eq!(ItemType::PublicVariable.to_string(), "variable");
}
#[test]
fn test_item_type_from_str() {
assert_eq!(ItemType::from_str("contract").unwrap(), ItemType::Contract);
assert_eq!(
ItemType::from_str("interface").unwrap(),
ItemType::Interface
);
assert_eq!(ItemType::from_str("library").unwrap(), ItemType::Library);
assert_eq!(
ItemType::from_str("constructor").unwrap(),
ItemType::Constructor
);
assert_eq!(ItemType::from_str("enum").unwrap(), ItemType::Enum);
assert_eq!(ItemType::from_str("error").unwrap(), ItemType::Error);
assert_eq!(ItemType::from_str("event").unwrap(), ItemType::Event);
assert_eq!(ItemType::from_str("modifier").unwrap(), ItemType::Modifier);
assert_eq!(
ItemType::from_str("parsing-error").unwrap(),
ItemType::ParsingError
);
assert_eq!(ItemType::from_str("struct").unwrap(), ItemType::Struct);
assert_eq!(
ItemType::from_str("private-function").unwrap(),
ItemType::PrivateFunction
);
assert_eq!(
ItemType::from_str("internal-function").unwrap(),
ItemType::InternalFunction
);
assert_eq!(
ItemType::from_str("public-function").unwrap(),
ItemType::PublicFunction
);
assert_eq!(
ItemType::from_str("external-function").unwrap(),
ItemType::ExternalFunction
);
assert_eq!(
ItemType::from_str("private-variable").unwrap(),
ItemType::PrivateVariable
);
assert_eq!(
ItemType::from_str("internal-variable").unwrap(),
ItemType::InternalVariable
);
assert_eq!(
ItemType::from_str("public-variable").unwrap(),
ItemType::PublicVariable
);
}
#[test]
fn test_item_type_from_str_case_sensitive() {
assert!(ItemType::from_str("Contract").is_err());
assert!(ItemType::from_str("PRIVATE_FUNCTION").is_err());
assert!(ItemType::from_str("PrivateFunction").is_err());
}
#[test]
fn test_item_type_from_str_invalid() {
assert!(ItemType::from_str("invalid").is_err());
assert!(ItemType::from_str("function").is_err()); assert!(ItemType::from_str("variable").is_err()); assert!(ItemType::from_str("").is_err());
}
}