File: LintPipeline.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (94 lines) | stat: -rw-r--r-- 4,322 bytes parent folder | download
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
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import SwiftSyntax

/// A syntax visitor that delegates to individual rules for linting.
///
/// This file will be extended with `visit` methods in Pipelines+Generated.swift.
extension LintPipeline {
  /// Calls the `visit` method of a rule for the given node if that rule is enabled for the node.
  ///
  /// - Parameters:
  ///   - visitor: A reference to the `visit` method on the *type* of a `SyntaxLintRule` subclass.
  ///     The type of the rule in question is inferred from the signature of the method reference.
  ///   - context: The formatter context that contains information about which rules are enabled or
  ///     disabled.
  ///   - node: The syntax node on which the rule will be applied. This lets us check whether the
  ///     rule is enabled for the particular source range where the node occurs.
  func visitIfEnabled<Rule: SyntaxLintRule, Node: SyntaxProtocol>(
    _ visitor: (Rule) -> (Node) -> SyntaxVisitorContinueKind, for node: Node
  ) {
    guard context.shouldFormat(Rule.self, node: Syntax(node)) else { return }
    let ruleId = ObjectIdentifier(Rule.self)
    guard self.shouldSkipChildren[ruleId] == nil else { return }
    let rule = self.rule(Rule.self)
    let continueKind = visitor(rule)(node)
    if case .skipChildren = continueKind {
      self.shouldSkipChildren[ruleId] = node
    }
  }

  /// Calls the `visit` method of a rule for the given node if that rule is enabled for the node.
  ///
  /// - Parameters:
  ///   - visitor: A reference to the `visit` method on the *type* of a `SyntaxFormatRule` subclass.
  ///     The type of the rule in question is inferred from the signature of the method reference.
  ///   - context: The formatter context that contains information about which rules are enabled or
  ///     disabled.
  ///   - node: The syntax node on which the rule will be applied. This lets us check whether the
  ///     rule is enabled for the particular source range where the node occurs.
  func visitIfEnabled<Rule: SyntaxFormatRule, Node: SyntaxProtocol>(
    _ visitor: (Rule) -> (Node) -> Any, for node: Node
  ) {
    // Note that visitor function type is expressed as `Any` because we ignore the return value, but
    // more importantly because the `visit` methods return protocol refinements of `Syntax` that
    // cannot currently be expressed as constraints without duplicating this function for each of
    // them individually.
    guard context.shouldFormat(Rule.self, node: Syntax(node)) else { return }
    guard self.shouldSkipChildren[ObjectIdentifier(Rule.self)] == nil else { return }
    let rule = self.rule(Rule.self)
    _ = visitor(rule)(node)
  }

  /// Cleans up any state associated with `rule` when we leave syntax node `node`
  ///
  /// - Parameters:
  ///   - rule: The type of the syntax rule we're cleaning up.
  ///   - node: The syntax node htat our traversal has left.
  func onVisitPost<R: Rule, Node: SyntaxProtocol>(
    rule: R.Type, for node: Node
  ) {
    let rule = ObjectIdentifier(rule)
    if case .some(let skipNode) = self.shouldSkipChildren[rule] {
      if node.id == skipNode.id {
        self.shouldSkipChildren.removeValue(forKey: rule)
      }
    }
  }

  /// Retrieves an instance of a lint or format rule based on its type.
  ///
  /// There is at most 1 instance of each rule allocated per `LintPipeline`. This method will
  /// create that instance as needed, using `ruleCache` to cache rules.
  /// - Parameter type: The type of the rule to retrieve.
  /// - Returns: An instance of the given type.
  private func rule<R: Rule>(_ type: R.Type) -> R {
    let identifier = ObjectIdentifier(type)
    if let cachedRule = ruleCache[identifier] {
      return cachedRule as! R
    }
    let rule = R(context: context)
    ruleCache[identifier] = rule
    return rule
  }
}