-
Notifications
You must be signed in to change notification settings - Fork 246
/
Copy pathContext.swift
121 lines (101 loc) · 4.15 KB
/
Context.swift
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
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 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 Foundation
import SwiftOperators
import SwiftParser
import SwiftSyntax
/// Context contains the bits that each formatter and linter will need access to.
///
/// Specifically, it is the container for the shared configuration, diagnostic consumer, and URL of
/// the current file.
@_spi(Rules)
public final class Context {
/// Tracks whether `XCTest` has been imported so that certain logic can be modified for files that
/// are known to be tests.
public enum XCTestImportState {
/// Whether `XCTest` is imported or not has not yet been determined.
case notDetermined
/// The file is known to import `XCTest`.
case importsXCTest
/// The file is known to not import `XCTest`.
case doesNotImportXCTest
}
/// The configuration for this run of the pipeline, provided by a configuration JSON file.
let configuration: Configuration
/// The selection to process
let selection: Selection
/// Defines the operators and their precedence relationships that were used during parsing.
let operatorTable: OperatorTable
/// Emits findings to the finding consumer.
let findingEmitter: FindingEmitter
/// The URL of the file being linted or formatted.
let fileURL: URL
/// Indicates whether the file is known to import XCTest.
public var importsXCTest: XCTestImportState
/// An object that converts `AbsolutePosition` values to `SourceLocation` values.
public let sourceLocationConverter: SourceLocationConverter
/// Contains the rules have been disabled by comments for certain line numbers.
let ruleMask: RuleMask
/// Contains all the available rules' names associated to their types' object identifiers.
let ruleNameCache: [ObjectIdentifier: String]
/// Creates a new Context with the provided configuration, diagnostic engine, and file URL.
public init(
configuration: Configuration,
operatorTable: OperatorTable,
findingConsumer: ((Finding) -> Void)?,
fileURL: URL,
selection: Selection = .infinite,
sourceFileSyntax: SourceFileSyntax,
source: String? = nil,
ruleNameCache: [ObjectIdentifier: String]
) {
self.configuration = configuration
self.operatorTable = operatorTable
self.findingEmitter = FindingEmitter(consumer: findingConsumer)
self.fileURL = fileURL
self.selection = selection
self.importsXCTest = .notDetermined
let tree = source.map { Parser.parse(source: $0) } ?? sourceFileSyntax
self.sourceLocationConverter =
SourceLocationConverter(fileName: fileURL.relativePath, tree: tree)
self.ruleMask = RuleMask(
syntaxNode: Syntax(sourceFileSyntax),
sourceLocationConverter: sourceLocationConverter
)
self.ruleNameCache = ruleNameCache
}
/// Given a rule's name and the node it is examining, determine if the rule is disabled at this
/// location or not. Also makes sure the entire node is contained inside any selection.
func shouldFormat<R: Rule>(_ rule: R.Type, node: Syntax) -> Bool {
guard node.isInsideSelection(selection) else { return false }
let loc = node.startLocation(converter: self.sourceLocationConverter)
assert(
ruleNameCache[ObjectIdentifier(rule)] != nil,
"""
Missing cached rule name for '\(rule)'! \
Ensure `generate-swift-format` has been run and `ruleNameCache` was injected.
"""
)
let ruleName = ruleNameCache[ObjectIdentifier(rule)] ?? R.ruleName
switch ruleMask.ruleState(ruleName, at: loc) {
case .default:
guard let configSeverity = configuration.rules[ruleName] else { return false }
if case .disabled = configSeverity {
return false
} else {
return true
}
case .disabled:
return false
}
}
}