@@ -76,35 +76,39 @@ public struct ModuleDependency: Hashable, Sendable, SerializableCodable {
7676
7777public struct ModuleDependenciesContext : Sendable , SerializableCodable {
7878 public var validate : BooleanWarningLevel
79+ public var validateUnused : BooleanWarningLevel
7980 var moduleDependencies : [ ModuleDependency ]
8081 var fixItContext : FixItContext ?
8182
82- init ( validate: BooleanWarningLevel , moduleDependencies: [ ModuleDependency ] , fixItContext: FixItContext ? = nil ) {
83+ init ( validate: BooleanWarningLevel , validateUnused : BooleanWarningLevel , moduleDependencies: [ ModuleDependency ] , fixItContext: FixItContext ? = nil ) {
8384 self . validate = validate
85+ self . validateUnused = validateUnused
8486 self . moduleDependencies = moduleDependencies
8587 self . fixItContext = fixItContext
8688 }
8789
8890 public init ? ( settings: Settings ) {
8991 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 }
9194 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) " ) )
94104 }
95105
96106 /// Compute missing module dependencies.
97- ///
98- /// If `imports` is nil, the current toolchain does not support the features to gather imports.
99107 public func computeMissingDependencies(
100- imports: [ ( ModuleDependency , importLocations: [ Diagnostic . Location ] ) ] ? ,
108+ imports: [ ( ModuleDependency , importLocations: [ Diagnostic . Location ] ) ] ,
101109 fromSwift: Bool
102- ) -> [ ( ModuleDependency , importLocations: [ Diagnostic . Location ] ) ] ? {
110+ ) -> [ ( ModuleDependency , importLocations: [ Diagnostic . Location ] ) ] {
103111 guard validate != . no else { return [ ] }
104- guard let imports else {
105- return nil
106- }
107-
108112 return imports. filter {
109113 // 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
110114 if fromSwift && $0. importLocations. isEmpty { return false }
@@ -115,45 +119,63 @@ public struct ModuleDependenciesContext: Sendable, SerializableCodable {
115119 }
116120 }
117121
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+ }
128126
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+ }
130146
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: " " ) ) "
133148
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)
144152
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+ }
146163
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+ }
150177
151- return [ Diagnostic (
152- behavior: behavior,
153- location: location,
154- data: DiagnosticData ( message) ,
155- fixIts: fixIts,
156- childDiagnostics: importDiags) ]
178+ return missingDiagnostics + unusedDiagnostics
157179 }
158180
159181 struct FixItContext : Sendable , SerializableCodable {
@@ -169,6 +191,7 @@ public struct ModuleDependenciesContext: Sendable, SerializableCodable {
169191 guard let target = settings. target else { return nil }
170192 let thisTargetCondition = MacroCondition ( parameter: BuiltinMacros . targetNameCondition, valuePattern: target. name)
171193
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
172195 if let assignment = ( settings. globalScope. table. lookupMacro ( BuiltinMacros . MODULE_DEPENDENCIES) ? . sequence. first {
173196 $0. location != nil && ( $0. conditions? . conditions == [ thisTargetCondition] || ( $0. conditions? . conditions. isEmpty ?? true ) )
174197 } ) ,
0 commit comments