File: DiagnosticEngine.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 (136 lines) | stat: -rw-r--r-- 5,484 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
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
133
134
135
136
/*
 This source file is part of the Swift.org open source project

 Copyright (c) 2021-2022 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 Swift project authors
*/

import Foundation

/// A type that collects and dispatches diagnostics during compilation.
public final class DiagnosticEngine {

    /// The queue on which diagnostics are dispatched to consumers.
    private let workQueue = DispatchQueue(label: "org.swift.docc.DiagnosticsEngine.work-queue")

    /// The diagnostic consumers currently subscribed to this engine.
    let consumers: Synchronized<[ObjectIdentifier: DiagnosticConsumer]> = .init([:])
    /// The diagnostics encountered by this engine.
    let diagnostics: Synchronized<[Problem]> = .init([])
    /// A flag that indicates whether this engine has emitted a diagnostics with a severity level of ``DiagnosticSeverity/error``.
    var didEncounterError: Synchronized<Bool> = .init(false)

    /// Determines which problems will be emitted to consumers.
    /// 
    /// This filter level is inclusive, i.e. if a level of ``DiagnosticSeverity/information`` is specified,
    /// diagnostics with a severity up to and including `.information` will be printed.
    public var filterLevel: DiagnosticSeverity {
        didSet {
            self.filter = { $0.diagnostic.severity.rawValue <= self.filterLevel.rawValue }
        }
    }
    
    /// Returns a Boolean value indicating whether the engine contains a consumer that satisfies the given predicate.
    /// - Parameter predicate: A closure that takes one of the engine's consumers as its argument and returns a Boolean value that indicates whether the passed consumer represents a match.
    /// - Returns: `true` if the engine contains a consumer that satisfies predicate; otherwise, `false`.
    public func hasConsumer(matching predicate: (DiagnosticConsumer) throws -> Bool) rethrows -> Bool {
        try consumers.sync {
            try $0.values.contains(where: predicate)
        }
    }
    
    /// Determines whether warnings will be treated as errors.
    private let treatWarningsAsErrors: Bool

    /// Determines which problems should be emitted.
    private var filter: (Problem) -> Bool

    /// A convenience accessor for retrieving all of the diagnostics this engine currently holds.
    public var problems: [Problem] {
        return diagnostics.sync { $0 }
    }

    /// Creates a new diagnostic engine instance with no consumers.
    public init(filterLevel: DiagnosticSeverity = .warning, treatWarningsAsErrors: Bool = false) {
        self.filterLevel = filterLevel
        self.treatWarningsAsErrors = treatWarningsAsErrors
        self.filter = { $0.diagnostic.severity.rawValue <= filterLevel.rawValue }
    }

    /// Removes all of the encountered diagnostics from this engine.
    public func clearDiagnostics() {
        diagnostics.sync {
            $0.removeAll()
        }
        didEncounterError.sync { $0 = false }
    }

    /// Dispatches a diagnostic to all subscribed consumers.
    /// - Parameter problem: The diagnostic to dispatch to this engine's currently subscribed consumers.
    public func emit(_ problem: Problem) {
        emit([problem])
    }

    /// Dispatches multiple diagnostics to consumers.
    /// - Parameter problems: The array of diagnostics to dispatch to this engine's currently subscribed consumers.
    /// > Note: Diagnostics are dispatched asynchronously.
    public func emit(_ problems: [Problem]) {
        let mappedProblems = problems.map { problem -> Problem in
            var problem = problem
            if treatWarningsAsErrors, problem.diagnostic.severity == .warning {
                problem.diagnostic.severity = .error
            }
            return problem
        }
        let filteredProblems = mappedProblems.filter(filter)
        guard !filteredProblems.isEmpty else { return }

        if filteredProblems.containsErrors {
            didEncounterError.sync { $0 = true }
        }
        
        diagnostics.sync {
            $0.append(contentsOf: filteredProblems)
        }

        workQueue.async { [weak self] in
            // If the engine isn't around then return early
            guard let self else { return }
            for consumer in self.consumers.sync({ $0.values }) {
                consumer.receive(filteredProblems)
            }
        }
    }
    
    @available(*, deprecated, renamed: "flush()", message: "Use 'flush()' instead. This deprecated API will be removed after 5.11 is released")
    public func finalize() {
        flush()
    }
    
    public func flush() {
        workQueue.sync {
            for consumer in self.consumers.sync({ $0.values }) {
                try? consumer.flush()
            }
        }
    }

    /// Subscribes a given consumer to the diagnostics emitted by this engine.
    /// - Parameter consumer: The consumer to subscribe to this engine.
    public func add(_ consumer: DiagnosticConsumer) {
        consumers.sync {
            $0[ObjectIdentifier(consumer)] = consumer
        }
    }

    /// Unsubscribes a given consumer
    /// - Parameter consumer: The consumer to remove from this engine.
    public func remove(_ consumer: DiagnosticConsumer) {
        consumers.sync {
            $0.removeValue(forKey: ObjectIdentifier(consumer))
        }
    }
}