#![allow(unused_variables, unused_imports)]
use crate::autofix::Fixer;
use crate::Diagnostic;
use dyn_clone::DynClone;
use rslint_errors::Severity;
use rslint_parser::{SyntaxNode, SyntaxNodeExt, SyntaxToken};
use rslint_text_edit::apply_indels;
use serde::{Deserialize, Serialize};
use std::borrow::Borrow;
use std::fmt::Debug;
use std::marker::{Send, Sync};
use std::ops::{Deref, DerefMut, Drop};
use std::rc::Rc;
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Tag {
Recommended,
OnlyJS,
OnlyTS,
}
#[typetag::serde]
pub trait CstRule: Rule {
#[inline]
fn check_node(&self, node: &SyntaxNode, ctx: &mut RuleCtx) -> Option<()> {
None
}
#[inline]
fn check_token(&self, token: &SyntaxToken, ctx: &mut RuleCtx) -> Option<()> {
None
}
#[inline]
fn check_root(&self, root: &SyntaxNode, ctx: &mut RuleCtx) -> Option<()> {
None
}
}
pub trait Rule: Debug + DynClone + Send + Sync {
fn name(&self) -> &'static str;
fn group(&self) -> &'static str;
fn docs(&self) -> &'static str {
""
}
fn tags(&self) -> &'static [Tag] {
&[]
}
fn recommended(&self) -> bool {
self.tags().iter().any(|x| x == &Tag::Recommended)
}
#[cfg(feature = "schema")]
fn schema(&self) -> Option<schemars::schema::RootSchema> {
None
}
}
dyn_clone::clone_trait_object!(Rule);
dyn_clone::clone_trait_object!(CstRule);
#[typetag::serde]
pub trait Inferable: CstRule {
fn infer(&mut self, nodes: &[SyntaxNode]);
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum RuleLevel {
Warning,
Error,
}
#[derive(Debug, Clone)]
pub struct RuleCtx {
pub file_id: usize,
pub verbose: bool,
pub diagnostics: Vec<Diagnostic>,
pub fixer: Option<Fixer>,
pub src: Arc<str>,
}
impl RuleCtx {
pub fn err(&mut self, code: impl Into<String>, message: impl Into<String>) -> Diagnostic {
Diagnostic::error(self.file_id, code.into(), message.into())
}
pub fn add_err(&mut self, diagnostic: Diagnostic) {
self.diagnostics.push(diagnostic)
}
pub fn fix(&mut self) -> &mut Fixer {
let fixer = Fixer::new(self.src.clone());
self.fixer = Some(fixer);
self.fixer.as_mut().unwrap()
}
pub(crate) fn dummy_ctx() -> Self {
Self {
file_id: 0,
verbose: false,
diagnostics: vec![],
fixer: None,
src: Arc::from(String::new()),
}
}
}
#[derive(Debug, Clone)]
pub struct RuleResult {
pub diagnostics: Vec<Diagnostic>,
pub fixer: Option<Fixer>,
}
impl RuleResult {
pub fn new(diagnostics: Vec<Diagnostic>, fixer: impl Into<Option<Fixer>>) -> Self {
Self {
diagnostics,
fixer: fixer.into(),
}
}
pub fn outcome(&self) -> Outcome {
Outcome::from(&self.diagnostics)
}
pub fn merge(self, other: RuleResult) -> RuleResult {
RuleResult {
diagnostics: [self.diagnostics, other.diagnostics].concat(),
fixer: self.fixer.or(other.fixer),
}
}
pub fn fix(&self) -> Option<String> {
self.fixer.as_ref().map(|x| x.apply())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Outcome {
Failure,
Warning,
Success,
}
impl<T> From<T> for Outcome
where
T: IntoIterator,
T::Item: Borrow<Diagnostic>,
{
fn from(diagnostics: T) -> Self {
let mut outcome = Outcome::Success;
for diagnostic in diagnostics {
match diagnostic.borrow().severity {
Severity::Error => outcome = Outcome::Failure,
Severity::Warning if outcome != Outcome::Failure => outcome = Outcome::Warning,
_ => {}
}
}
outcome
}
}
impl Outcome {
pub fn merge(outcomes: impl IntoIterator<Item = impl Borrow<Outcome>>) -> Outcome {
let mut overall = Outcome::Success;
for outcome in outcomes {
match outcome.borrow() {
Outcome::Failure => overall = Outcome::Failure,
Outcome::Warning if overall != Outcome::Failure => overall = Outcome::Warning,
_ => {}
}
}
overall
}
}
#[macro_export]
#[doc(hidden)]
macro_rules! __pre_parse_docs_from_meta {
(
@$cb:tt
@[docs $($docs:tt)*]
@$other:tt
#[doc = $doc:expr]
$($rest:tt)*
) => (
$crate::__pre_parse_docs_from_meta! {
@$cb
@[docs $($docs)* $doc]
@$other
$($rest)*
}
);
(
@$cb:tt
@$docs:tt
@[others $($others:tt)*]
#[$other:meta]
$($rest:tt)*
) => (
$crate::__pre_parse_docs_from_meta! {
@$cb
@$docs
@[others $($others)* $other]
$($rest)*
}
);
(
@[cb $($cb:tt)*]
@[docs $($docs:tt)*]
@[others $($others:tt)*]
$($rest:tt)*
) => (
$($cb)* ! {
#[doc = concat!($(indoc::indoc!($docs), "\n"),*)]
$(
#[$others]
)*
$($rest)*
}
);
(
$(:: $(@ $colon:tt)?)? $($cb:ident)::+ ! {
$($input:tt)*
}
) => (
$crate::__pre_parse_docs_from_meta! {
@[cb $(:: $($colon)?)? $($cb)::+]
@[docs ]
@[others ]
$($input)*
}
);
}
#[macro_export]
#[doc(hidden)]
macro_rules! __declare_lint_inner {
(
#[doc = $doc:expr]
$(#[$outer:meta])*
// The rule struct name
$name:ident,
$group:ident,
$(
tags($($tag:ident),* $(,)?),
)?
$code:literal
$(,
// Any fields for the rule
$(
$(#[$inner:meta])*
$visibility:vis $key:ident : $val:ty
),* $(,)?
)?
) => {
use $crate::Rule;
use serde::{Deserialize, Serialize};
#[doc = $doc]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[derive(Debug, Clone, Deserialize, Serialize)]
$(#[$outer])*
#[serde(rename_all = "camelCase")]
pub struct $name {
$(
$(
$(#[$inner])*
pub $key: $val
),
*)?
}
impl $name {
pub fn new() -> Self {
Self::default()
}
}
impl Rule for $name {
fn name(&self) -> &'static str {
$code
}
fn group(&self) -> &'static str {
stringify!($group)
}
fn docs(&self) -> &'static str {
$doc
}
$(
fn tags(&self) -> &'static [$crate::Tag] {
&[$($crate::Tag::$tag),*]
}
)?
#[cfg(feature = "schema")]
fn schema(&self) -> Option<schemars::schema::RootSchema> {
Some(schemars::schema_for!($name))
}
}
};
}
#[macro_export]
macro_rules! declare_lint {
($($input:tt)*) => {
$crate::__pre_parse_docs_from_meta! {
$crate::__declare_lint_inner! { $($input)* }
}
};
}