sqruff_lib/core/rules/
crawlers.rs

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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use enum_dispatch::enum_dispatch;
use sqruff_lib_core::dialects::syntax::{SyntaxKind, SyntaxSet};
use sqruff_lib_core::parser::segments::base::ErasedSegment;

use crate::core::rules::context::RuleContext;

#[enum_dispatch]
pub trait BaseCrawler {
    fn works_on_unparsable(&self) -> bool {
        false
    }

    fn passes_filter(&self, segment: &ErasedSegment) -> bool {
        self.works_on_unparsable() || !segment.is_type(SyntaxKind::Unparsable)
    }

    fn crawl<'a>(&self, context: RuleContext<'a>) -> Vec<RuleContext<'a>>;
}

#[enum_dispatch(BaseCrawler)]
pub enum Crawler {
    RootOnlyCrawler,
    SegmentSeekerCrawler,
    TokenSeekerCrawler,
}

/// A crawler that doesn't crawl.
///
/// This just yields one context on the root-level (topmost) segment of the
/// file.
#[derive(Debug, Default, Clone)]
pub struct RootOnlyCrawler;

impl BaseCrawler for RootOnlyCrawler {
    fn crawl<'a>(&self, context: RuleContext<'a>) -> Vec<RuleContext<'a>> {
        if self.passes_filter(&context.segment) {
            vec![context.clone()]
        } else {
            Vec::new()
        }
    }
}

pub struct SegmentSeekerCrawler {
    types: SyntaxSet,
    provide_raw_stack: bool,
    allow_recurse: bool,
}

impl SegmentSeekerCrawler {
    pub fn new(types: SyntaxSet) -> Self {
        Self {
            types,
            provide_raw_stack: false,
            allow_recurse: true,
        }
    }

    pub fn disallow_recurse(mut self) -> Self {
        self.allow_recurse = false;
        self
    }

    pub fn provide_raw_stack(mut self) -> Self {
        self.provide_raw_stack = true;
        self
    }

    fn is_self_match(&self, segment: &ErasedSegment) -> bool {
        self.types.contains(segment.get_type())
    }
}

impl BaseCrawler for SegmentSeekerCrawler {
    fn crawl<'a>(&self, mut context: RuleContext<'a>) -> Vec<RuleContext<'a>> {
        let mut acc = Vec::new();
        let mut self_match = false;

        if self.is_self_match(&context.segment) {
            self_match = true;
            acc.push(context.clone());
        }

        if context.segment.segments().is_empty() || (self_match && !self.allow_recurse) {
            return acc;
        }

        if !self.types.intersects(context.segment.descendant_type_set()) {
            if self.provide_raw_stack {
                context
                    .raw_stack
                    .append(&mut context.segment.get_raw_segments());
            }

            return acc;
        }

        context.parent_stack.push(context.segment.clone());
        let segment = context.segment;
        for (idx, child) in segment.segments().iter().enumerate() {
            context.segment = child.clone();
            context.segment_idx = idx;

            acc.extend(self.crawl(context.clone()));
        }

        acc
    }
}

pub struct TokenSeekerCrawler;

impl BaseCrawler for TokenSeekerCrawler {
    fn crawl<'a>(&self, mut context: RuleContext<'a>) -> Vec<RuleContext<'a>> {
        let mut acc = Vec::new();

        if context.segment.segments().is_empty() {
            acc.push(context.clone());
        }

        context.parent_stack.push(context.segment.clone());
        let segment = context.segment;
        for (idx, child) in segment.segments().iter().enumerate() {
            context.segment = child.clone();
            context.segment_idx = idx;

            acc.extend(self.crawl(context.clone()));
        }

        acc
    }
}