@@ -97,6 +97,8 @@ extension SourceRange {
9797
9898/// Represents the kind of ignore directive encountered in the source.
9999enum IgnoreDirective : CustomStringConvertible {
100+ typealias RegexExpression = Regex < ( Substring , ruleNames: Substring ? ) >
101+
100102 /// A node-level directive that disables rules for the following node and its children.
101103 case node
102104 /// A file-level directive that disables rules for the entire file.
@@ -111,10 +113,14 @@ enum IgnoreDirective: CustomStringConvertible {
111113 }
112114 }
113115
114- /// Regex pattern to match an ignore comment. This pattern supports 0 or more comma delimited rule
115- /// names. The rule name(s), when present, are in capture group #3.
116- fileprivate var pattern : String {
117- return #"^\s*\/\/\s*"# + description + #"((:\s+(([A-z0-9]+[,\s]*)+))?$|\s+$)"#
116+ /// Regex pattern to match an ignore directive comment.
117+ /// - Capture group #1 captures the rule names if `":"` is present.
118+ ///
119+ /// Note: We are using a string-based regex instead of a regex literal (`#/regex/#`)
120+ /// because Windows did not have full support for regex literals until Swift 5.10.
121+ fileprivate func makeRegex( ) -> RegexExpression {
122+ let pattern = #"^\s*\/\/\s*"# + description + #"(?:\s*:\s*(?<ruleNames>.+))?$"#
123+ return try ! Regex ( pattern)
118124 }
119125}
120126
@@ -140,10 +146,10 @@ fileprivate class RuleStatusCollectionVisitor: SyntaxVisitor {
140146 private let sourceLocationConverter : SourceLocationConverter
141147
142148 /// Cached regex object for ignoring rules at the node.
143- private let ignoreRegex : NSRegularExpression
149+ private let ignoreRegex : IgnoreDirective . RegexExpression
144150
145151 /// Cached regex object for ignoring rules at the file.
146- private let ignoreFileRegex : NSRegularExpression
152+ private let ignoreFileRegex : IgnoreDirective . RegexExpression
147153
148154 /// Stores the source ranges in which all rules are ignored.
149155 var allRulesIgnoredRanges : [ SourceRange ] = [ ]
@@ -152,8 +158,8 @@ fileprivate class RuleStatusCollectionVisitor: SyntaxVisitor {
152158 var ruleMap : [ String : [ SourceRange ] ] = [ : ]
153159
154160 init ( sourceLocationConverter: SourceLocationConverter ) {
155- ignoreRegex = try ! NSRegularExpression ( pattern : IgnoreDirective . node. pattern , options : [ ] )
156- ignoreFileRegex = try ! NSRegularExpression ( pattern : IgnoreDirective . file. pattern , options : [ ] )
161+ ignoreRegex = IgnoreDirective . node. makeRegex ( )
162+ ignoreFileRegex = IgnoreDirective . file. makeRegex ( )
157163
158164 self . sourceLocationConverter = sourceLocationConverter
159165 super. init ( viewMode: . sourceAccurate)
@@ -202,7 +208,7 @@ fileprivate class RuleStatusCollectionVisitor: SyntaxVisitor {
202208 private func appendRuleStatus(
203209 from token: TokenSyntax ,
204210 of sourceRange: SourceRange ,
205- using regex: NSRegularExpression
211+ using regex: IgnoreDirective . RegexExpression
206212 ) -> SyntaxVisitorContinueKind {
207213 let isFirstInFile = token. previousToken ( viewMode: . sourceAccurate) == nil
208214 let comments = loneLineComments ( in: token. leadingTrivia, isFirstToken: isFirstInFile)
@@ -227,18 +233,15 @@ fileprivate class RuleStatusCollectionVisitor: SyntaxVisitor {
227233 /// match, its contents (e.g. list of rule names) are returned.
228234 private func ruleStatusDirectiveMatch(
229235 in text: String ,
230- using regex: NSRegularExpression
236+ using regex: IgnoreDirective . RegexExpression
231237 ) -> RuleStatusDirectiveMatch ? {
232- let textRange = NSRange ( text. startIndex..< text. endIndex, in: text)
233- guard let match = regex. firstMatch ( in: text, options: [ ] , range: textRange) else {
238+ guard let match = text. firstMatch ( of: regex) else {
234239 return nil
235240 }
236- guard match. numberOfRanges == 5 else { return . all }
237- let matchRange = match. range ( at: 3 )
238- guard matchRange. location != NSNotFound, let ruleNamesRange = Range ( matchRange, in: text) else {
241+ guard let matchedRuleNames = match. output. ruleNames else {
239242 return . all
240243 }
241- let rules = text [ ruleNamesRange ] . split ( separator: " , " )
244+ let rules = matchedRuleNames . split ( separator: " , " )
242245 . map { $0. trimmingCharacters ( in: . whitespaces) }
243246 . filter { $0. count > 0 }
244247 return . subset( ruleNames: rules)
0 commit comments