1010
1111//! Read a section of the config file and generate diagnostics from it.
1212
13+ use elp_ide_db:: DiagnosticCode ;
14+ use elp_ide_db:: RootDatabase ;
1315use elp_ide_db:: elp_base_db:: FileId ;
16+ use elp_ide_ssr:: SsrRule ;
17+ use elp_ide_ssr:: SsrSearchScope ;
18+ use elp_ide_ssr:: match_pattern;
1419use hir:: Semantic ;
20+ use hir:: Strategy ;
21+ use hir:: fold:: MacroStrategy ;
22+ use hir:: fold:: ParenStrategy ;
1523use serde:: Deserialize ;
1624use serde:: Serialize ;
1725
1826use super :: Diagnostic ;
27+ use super :: Severity ;
1928use super :: TypeReplacement ;
2029use super :: replace_call;
2130use super :: replace_call:: Replacement ;
@@ -41,13 +50,15 @@ impl LintsFromConfig {
4150pub enum Lint {
4251 ReplaceCall ( ReplaceCall ) ,
4352 ReplaceInSpec ( ReplaceInSpec ) ,
53+ LintMatchSsr ( MatchSsr ) ,
4454}
4555
4656impl Lint {
4757 pub fn get_diagnostics ( & self , acc : & mut Vec < Diagnostic > , sema : & Semantic , file_id : FileId ) {
4858 match self {
4959 Lint :: ReplaceCall ( l) => l. get_diagnostics ( acc, sema, file_id) ,
5060 Lint :: ReplaceInSpec ( l) => l. get_diagnostics ( acc, sema, file_id) ,
61+ Lint :: LintMatchSsr ( l) => l. get_diagnostics ( acc, sema, file_id) ,
5162 }
5263 }
5364}
@@ -115,12 +126,82 @@ impl ReplaceInSpec {
115126
116127// ---------------------------------------------------------------------
117128
129+ #[ derive( Debug , Clone , PartialEq , Eq , Serialize ) ]
130+ pub struct MatchSsr {
131+ pub ssr_pattern : String ,
132+ #[ serde( default ) ]
133+ pub message : Option < String > ,
134+ }
135+
136+ impl < ' de > Deserialize < ' de > for MatchSsr {
137+ fn deserialize < D > ( deserializer : D ) -> Result < Self , D :: Error >
138+ where
139+ D : serde:: Deserializer < ' de > ,
140+ {
141+ use serde:: de:: Error ;
142+
143+ #[ derive( Deserialize ) ]
144+ struct MatchSsrHelper {
145+ ssr_pattern : String ,
146+ #[ serde( default ) ]
147+ message : Option < String > ,
148+ }
149+
150+ let helper = MatchSsrHelper :: deserialize ( deserializer) ?;
151+
152+ // Validate the SSR pattern by trying to parse it
153+ // Use a minimal database for validation
154+ let db = RootDatabase :: default ( ) ;
155+ SsrRule :: parse_str ( & db, & helper. ssr_pattern ) . map_err ( |e| {
156+ D :: Error :: custom ( format ! (
157+ "invalid SSR pattern '{}': {}" ,
158+ helper. ssr_pattern, e
159+ ) )
160+ } ) ?;
161+
162+ Ok ( MatchSsr {
163+ ssr_pattern : helper. ssr_pattern ,
164+ message : helper. message ,
165+ } )
166+ }
167+ }
168+
169+ impl MatchSsr {
170+ pub fn get_diagnostics ( & self , acc : & mut Vec < Diagnostic > , sema : & Semantic , file_id : FileId ) {
171+ let strategy = Strategy {
172+ macros : MacroStrategy :: Expand ,
173+ parens : ParenStrategy :: InvisibleParens ,
174+ } ;
175+
176+ let scope = SsrSearchScope :: WholeFile ( file_id) ;
177+ let matches = match_pattern ( sema, strategy, & self . ssr_pattern , scope) ;
178+
179+ for matched in matches. matches {
180+ let message = self
181+ . message
182+ . clone ( )
183+ . unwrap_or_else ( || format ! ( "SSR pattern matched: {}" , self . ssr_pattern) ) ;
184+
185+ let diag = Diagnostic :: new (
186+ DiagnosticCode :: AdHoc ( "ssr-match" . to_string ( ) ) ,
187+ message,
188+ matched. range . range ,
189+ )
190+ . with_severity ( Severity :: WeakWarning ) ;
191+ acc. push ( diag) ;
192+ }
193+ }
194+ }
195+
196+ // ---------------------------------------------------------------------
197+
118198#[ cfg( test) ]
119199mod tests {
120200 use expect_test:: expect;
121201
122202 use super :: Lint ;
123203 use super :: LintsFromConfig ;
204+ use super :: MatchSsr ;
124205 use super :: ReplaceCall ;
125206 use super :: ReplaceCallAction ;
126207 use super :: ReplaceInSpec ;
@@ -492,4 +573,57 @@ mod tests {
492573 "# ] ]
493574 . assert_eq ( & result) ;
494575 }
576+
577+ #[ test]
578+ fn serde_serialize_match_ssr ( ) {
579+ let result = toml:: to_string :: < MatchSsr > ( & MatchSsr {
580+ ssr_pattern : "ssr: _@A = 10." . to_string ( ) ,
581+ message : Some ( "Found pattern" . to_string ( ) ) ,
582+ } )
583+ . unwrap ( ) ;
584+ expect ! [ [ r#"
585+ ssr_pattern = "ssr: _@A = 10."
586+ message = "Found pattern"
587+ "# ] ]
588+ . assert_eq ( & result) ;
589+ }
590+
591+ #[ test]
592+ fn serde_deserialize_match_ssr ( ) {
593+ let match_ssr: MatchSsr = toml:: from_str (
594+ r#"
595+ ssr_pattern = "ssr: _@A = 10."
596+ message = "Found pattern"
597+ "# ,
598+ )
599+ . unwrap ( ) ;
600+
601+ expect ! [ [ r#"
602+ MatchSsr {
603+ ssr_pattern: "ssr: _@A = 10.",
604+ message: Some(
605+ "Found pattern",
606+ ),
607+ }
608+ "# ] ]
609+ . assert_debug_eq ( & match_ssr) ;
610+ }
611+
612+ #[ test]
613+ fn serde_serialize_lint_match_ssr ( ) {
614+ let result = toml:: to_string :: < LintsFromConfig > ( & LintsFromConfig {
615+ lints : vec ! [ Lint :: LintMatchSsr ( MatchSsr {
616+ ssr_pattern: "ssr: _@A = 10." . to_string( ) ,
617+ message: Some ( "Found pattern" . to_string( ) ) ,
618+ } ) ] ,
619+ } )
620+ . unwrap ( ) ;
621+ expect ! [ [ r#"
622+ [[lints]]
623+ type = "LintMatchSsr"
624+ ssr_pattern = "ssr: _@A = 10."
625+ message = "Found pattern"
626+ "# ] ]
627+ . assert_eq ( & result) ;
628+ }
495629}
0 commit comments