File: DiagnosticFile.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 (147 lines) | stat: -rw-r--r-- 4,607 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
137
138
139
140
141
142
143
144
145
146
147
/*
 This source file is part of the Swift.org open source project

 Copyright (c) 2023 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
import struct Markdown.SourceLocation

struct DiagnosticFile: Codable {
    var version: SemanticVersion
    var diagnostics: [Diagnostic]
    
    init(version: SemanticVersion = Self.currentVersion, problems: [Problem]) {
        self.version = version
        self.diagnostics = problems.map { .init($0) }
    }
    
    // This file format follows semantic versioning.
    // Breaking changes should increment the major version component.
    // Non breaking additions should increment the minor version.
    // Bug fixes should increment the patch version.
    static var currentVersion = SemanticVersion(major: 1, minor: 0, patch: 0, prerelease: nil, buildMetadata: nil)
    
    enum Error: Swift.Error {
        case unknownMajorVersion(found: SemanticVersion, latestKnown: SemanticVersion)
    }
    
    static func verifyIsSupported(_ version: SemanticVersion, current: SemanticVersion = Self.currentVersion) throws {
        guard version.major == current.major else {
            throw Error.unknownMajorVersion(found: version, latestKnown: current)
        }
    }
    
    struct Diagnostic: Codable {
        struct Range: Codable {
            var start: Location
            var end: Location
            struct Location: Codable {
                var line: Int
                var column: Int
            }
        }
        var source: URL?
        var range: Range?
        var severity: Severity
        var summary: String
        var explanation: String?
        var solutions: [Solution]
        struct Solution: Codable {
            var summary: String
            var replacements: [Replacement]
            struct Replacement: Codable {
                var range: Range
                var text: String
            }
        }
        var notes: [Note]
        struct Note: Codable {
            var source: URL?
            var range: Range?
            var message: String
        }
        enum Severity: String, Codable {
            case error, warning, note, remark
        }
    }
    
    enum CodingKeys: String, CodingKey {
        case version, diagnostics
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        version = try container.decode(SemanticVersion.self, forKey: .version)
        try Self.verifyIsSupported(version)
        
        diagnostics = try container.decode([Diagnostic].self, forKey: .diagnostics)
    }
}

// MARK: Initialization

extension DiagnosticFile.Diagnostic {
    init(_ problem: Problem) {
        self.source      = problem.diagnostic.source
        self.range       = problem.diagnostic.range.map { .init($0) }
        self.severity    = .init(problem.diagnostic.severity)
        self.summary     = problem.diagnostic.summary
        self.explanation = problem.diagnostic.explanation
        self.solutions   = problem.possibleSolutions.map { .init($0) }
        self.notes       = problem.diagnostic.notes.map { .init($0) }
    }
}

extension DiagnosticFile.Diagnostic.Range {
    init(_ sourceRange: Range<SourceLocation>) {
        start = .init(sourceRange.lowerBound)
        end   = .init(sourceRange.upperBound)
    }
}

extension DiagnosticFile.Diagnostic.Range.Location {
    init(_ sourceLocation: SourceLocation) {
        self.line   = sourceLocation.line
        self.column = sourceLocation.column
    }
}

extension DiagnosticFile.Diagnostic.Solution {
    init(_ solution: Solution) {
        self.summary      = solution.summary
        self.replacements = solution.replacements.map { .init($0) }
    }
}

extension DiagnosticFile.Diagnostic.Solution.Replacement {
    init(_ replacement: Replacement) {
        self.range = .init(replacement.range)
        self.text  = replacement.replacement
    }
}

extension DiagnosticFile.Diagnostic.Note {
    init(_ note: DiagnosticNote) {
        self.source  = note.source
        self.range   = .init(note.range)
        self.message = note.message
    }
}

extension DiagnosticFile.Diagnostic.Severity {
    init(_ severity: DiagnosticSeverity) {
        switch severity {
        case .error:
            self = .error
        case .warning:
            self = .warning
        case .information, .hint:
            self = .note
        }
    }
}