@@ -76,35 +76,39 @@ public struct ModuleDependency: Hashable, Sendable, SerializableCodable {
76
76
77
77
public struct ModuleDependenciesContext : Sendable , SerializableCodable {
78
78
public var validate : BooleanWarningLevel
79
+ public var validateUnused : BooleanWarningLevel
79
80
var moduleDependencies : [ ModuleDependency ]
80
81
var fixItContext : FixItContext ?
81
82
82
- init ( validate: BooleanWarningLevel , moduleDependencies: [ ModuleDependency ] , fixItContext: FixItContext ? = nil ) {
83
+ init ( validate: BooleanWarningLevel , validateUnused : BooleanWarningLevel , moduleDependencies: [ ModuleDependency ] , fixItContext: FixItContext ? = nil ) {
83
84
self . validate = validate
85
+ self . validateUnused = validateUnused
84
86
self . moduleDependencies = moduleDependencies
85
87
self . fixItContext = fixItContext
86
88
}
87
89
88
90
public init ? ( settings: Settings ) {
89
91
let validate = settings. globalScope. evaluate ( BuiltinMacros . VALIDATE_MODULE_DEPENDENCIES)
90
- guard validate != . no else { return nil }
92
+ let validateUnused = settings. globalScope. evaluate ( BuiltinMacros . VALIDATE_UNUSED_MODULE_DEPENDENCIES)
93
+ guard validate != . no || validateUnused != . no else { return nil }
91
94
let downgrade = settings. globalScope. evaluate ( BuiltinMacros . VALIDATE_DEPENDENCIES_DOWNGRADE_ERRORS)
92
- let fixItContext = ModuleDependenciesContext . FixItContext ( settings: settings)
93
- self . init ( validate: downgrade ? . yes : validate, moduleDependencies: settings. moduleDependencies, fixItContext: fixItContext)
95
+ let fixItContext = validate != . no ? ModuleDependenciesContext . FixItContext ( settings: settings) : nil
96
+ self . init ( validate: downgrade ? . yes : validate, validateUnused: validateUnused, moduleDependencies: settings. moduleDependencies, fixItContext: fixItContext)
97
+ }
98
+
99
+ public func makeUnsupportedToolchainDiagnostic( ) -> Diagnostic {
100
+ Diagnostic (
101
+ behavior: . warning,
102
+ location: . unknown,
103
+ data: DiagnosticData ( " The current toolchain does not support \( BuiltinMacros . VALIDATE_MODULE_DEPENDENCIES. name) " ) )
94
104
}
95
105
96
106
/// Compute missing module dependencies.
97
- ///
98
- /// If `imports` is nil, the current toolchain does not support the features to gather imports.
99
107
public func computeMissingDependencies(
100
- imports: [ ( ModuleDependency , importLocations: [ Diagnostic . Location ] ) ] ? ,
108
+ imports: [ ( ModuleDependency , importLocations: [ Diagnostic . Location ] ) ] ,
101
109
fromSwift: Bool
102
- ) -> [ ( ModuleDependency , importLocations: [ Diagnostic . Location ] ) ] ? {
110
+ ) -> [ ( ModuleDependency , importLocations: [ Diagnostic . Location ] ) ] {
103
111
guard validate != . no else { return [ ] }
104
- guard let imports else {
105
- return nil
106
- }
107
-
108
112
return imports. filter {
109
113
// ignore module deps without source locations, these are inserted by swift / swift-build and we should treat them as implementation details which we can track without needing the user to declare them
110
114
if fromSwift && $0. importLocations. isEmpty { return false }
@@ -115,45 +119,63 @@ public struct ModuleDependenciesContext: Sendable, SerializableCodable {
115
119
}
116
120
}
117
121
118
- /// Make diagnostics for missing module dependencies.
119
- public func makeDiagnostics( missingDependencies: [ ( ModuleDependency , importLocations: [ Diagnostic . Location ] ) ] ? ) -> [ Diagnostic ] {
120
- guard let missingDependencies else {
121
- return [ Diagnostic (
122
- behavior: . warning,
123
- location: . unknown,
124
- data: DiagnosticData ( " The current toolchain does not support \( BuiltinMacros . VALIDATE_MODULE_DEPENDENCIES. name) " ) ) ]
125
- }
126
-
127
- guard !missingDependencies. isEmpty else { return [ ] }
122
+ public func computeUnusedDependencies( usedModuleNames: Set < String > ) -> [ ModuleDependency ] {
123
+ guard validateUnused != . no else { return [ ] }
124
+ return moduleDependencies. filter { !usedModuleNames. contains ( $0. name) }
125
+ }
128
126
129
- let behavior : Diagnostic . Behavior = validate == . yesError ? . error : . warning
127
+ /// Make diagnostics for missing module dependencies.
128
+ public func makeDiagnostics( missingDependencies: [ ( ModuleDependency , importLocations: [ Diagnostic . Location ] ) ] , unusedDependencies: [ ModuleDependency ] ) -> [ Diagnostic ] {
129
+ let missingDiagnostics : [ Diagnostic ]
130
+ if !missingDependencies. isEmpty {
131
+ let behavior : Diagnostic . Behavior = validate == . yesError ? . error : . warning
132
+
133
+ let fixIt = fixItContext? . makeFixIt ( newModules: missingDependencies. map { $0. 0 } )
134
+ let fixIts = fixIt. map { [ $0] } ?? [ ]
135
+
136
+ let importDiags : [ Diagnostic ] = missingDependencies
137
+ . flatMap { dep in
138
+ dep. 1 . map {
139
+ return Diagnostic (
140
+ behavior: behavior,
141
+ location: $0,
142
+ data: DiagnosticData ( " Missing entry in \( BuiltinMacros . MODULE_DEPENDENCIES. name) : \( dep. 0 . asBuildSettingEntryQuotedIfNeeded) " ) ,
143
+ fixIts: fixIts)
144
+ }
145
+ }
130
146
131
- let fixIt = fixItContext? . makeFixIt ( newModules: missingDependencies. map { $0. 0 } )
132
- let fixIts = fixIt. map { [ $0] } ?? [ ]
147
+ let message = " Missing entries in \( BuiltinMacros . MODULE_DEPENDENCIES. name) : \( missingDependencies. map { $0. 0 . asBuildSettingEntryQuotedIfNeeded } . sorted ( ) . joined ( separator: " " ) ) "
133
148
134
- let importDiags : [ Diagnostic ] = missingDependencies
135
- . flatMap { dep in
136
- dep. 1 . map {
137
- return Diagnostic (
138
- behavior: behavior,
139
- location: $0,
140
- data: DiagnosticData ( " Missing entry in \( BuiltinMacros . MODULE_DEPENDENCIES. name) : \( dep. 0 . asBuildSettingEntryQuotedIfNeeded) " ) ,
141
- fixIts: fixIts)
142
- }
143
- }
149
+ let location : Diagnostic . Location = fixIt. map {
150
+ Diagnostic . Location. path ( $0. sourceRange. path, line: $0. sourceRange. endLine, column: $0. sourceRange. endColumn)
151
+ } ?? Diagnostic . Location. buildSetting ( BuiltinMacros . MODULE_DEPENDENCIES)
144
152
145
- let message = " Missing entries in \( BuiltinMacros . MODULE_DEPENDENCIES. name) : \( missingDependencies. map { $0. 0 . asBuildSettingEntryQuotedIfNeeded } . sorted ( ) . joined ( separator: " " ) ) "
153
+ missingDiagnostics = [ Diagnostic (
154
+ behavior: behavior,
155
+ location: location,
156
+ data: DiagnosticData ( message) ,
157
+ fixIts: fixIts,
158
+ childDiagnostics: importDiags) ]
159
+ }
160
+ else {
161
+ missingDiagnostics = [ ]
162
+ }
146
163
147
- let location : Diagnostic . Location = fixIt. map {
148
- Diagnostic . Location. path ( $0. sourceRange. path, line: $0. sourceRange. endLine, column: $0. sourceRange. endColumn)
149
- } ?? Diagnostic . Location. buildSetting ( BuiltinMacros . MODULE_DEPENDENCIES)
164
+ let unusedDiagnostics : [ Diagnostic ]
165
+ if !unusedDependencies. isEmpty {
166
+ let message = " Unused entries in \( BuiltinMacros . MODULE_DEPENDENCIES. name) : \( unusedDependencies. map { $0. name } . sorted ( ) . joined ( separator: " " ) ) "
167
+ // TODO location & fixit
168
+ unusedDiagnostics = [ Diagnostic (
169
+ behavior: validateUnused == . yesError ? . error : . warning,
170
+ location: . unknown,
171
+ data: DiagnosticData ( message) ,
172
+ fixIts: [ ] ) ]
173
+ }
174
+ else {
175
+ unusedDiagnostics = [ ]
176
+ }
150
177
151
- return [ Diagnostic (
152
- behavior: behavior,
153
- location: location,
154
- data: DiagnosticData ( message) ,
155
- fixIts: fixIts,
156
- childDiagnostics: importDiags) ]
178
+ return missingDiagnostics + unusedDiagnostics
157
179
}
158
180
159
181
struct FixItContext : Sendable , SerializableCodable {
@@ -169,6 +191,7 @@ public struct ModuleDependenciesContext: Sendable, SerializableCodable {
169
191
guard let target = settings. target else { return nil }
170
192
let thisTargetCondition = MacroCondition ( parameter: BuiltinMacros . targetNameCondition, valuePattern: target. name)
171
193
194
+ // TODO: if you have an assignment in a project-xcconfig and another assignment in target-settings, this would find the project-xcconfig assignment, but updating that might have no effect depending on the target-settings assignment
172
195
if let assignment = ( settings. globalScope. table. lookupMacro ( BuiltinMacros . MODULE_DEPENDENCIES) ? . sequence. first {
173
196
$0. location != nil && ( $0. conditions? . conditions == [ thisTargetCondition] || ( $0. conditions? . conditions. isEmpty ?? true ) )
174
197
} ) ,
0 commit comments