From 14e04987c58c3a1ea8ea4982b5f487c882dd9d80 Mon Sep 17 00:00:00 2001 From: ileitch Date: Sat, 25 Mar 2023 19:02:21 +0100 Subject: [PATCH] Add experimental unused import analysis. --- .periphery.linux.yml | 1 + .periphery.yml | 1 + CHANGELOG.md | 2 +- Sources/Frontend/Commands/ScanCommand.swift | 4 + Sources/Frontend/main.swift | 1 - .../PeripheryKit/Indexer/Declaration.swift | 25 ++++++ Sources/PeripheryKit/Indexer/SourceFile.swift | 2 - .../PeripheryKit/Indexer/SwiftIndexer.swift | 12 +++ Sources/PeripheryKit/Indexer/XibParser.swift | 1 - .../PeripheryKit/SPM/SPMProjectDriver.swift | 5 +- Sources/PeripheryKit/ScanResultBuilder.swift | 5 +- ...antExplicitPublicAccessibilityMarker.swift | 4 +- .../Mutators/UnusedImportMarker.swift | 82 +++++++++++++++++++ .../SourceGraph/SourceGraph.swift | 55 +++++++++++-- .../SourceGraphMutatorRunner.swift | 5 +- .../Syntax/ImportSyntaxVisitor.swift | 26 ++++-- Sources/Shared/Configuration.swift | 10 +++ Sources/XcodeSupport/XcodeTarget.swift | 1 - Sources/XcodeSupport/Xcodebuild.swift | 1 - .../RedundantPublicAccessibilityTest.swift | 1 - .../CrossModuleRetentionTest.swift | 1 - .../ObjcAccessibleRetentionTest.swift | 1 - .../ObjcAnnotatedRetentionTest.swift | 1 - Tests/SPMTests/SPMProjectTest.swift | 1 - Tests/XcodeTests/SwiftUIProjectTest.swift | 2 - Tests/XcodeTests/UIKitProjectTest.swift | 2 - Tests/XcodeTests/XcodebuildTest.swift | 1 - 27 files changed, 217 insertions(+), 36 deletions(-) create mode 100644 Sources/PeripheryKit/SourceGraph/Mutators/UnusedImportMarker.swift diff --git a/.periphery.linux.yml b/.periphery.linux.yml index be38f475f..b4a24eeb0 100644 --- a/.periphery.linux.yml +++ b/.periphery.linux.yml @@ -6,3 +6,4 @@ targets: - PeripheryTests - SPMTests - AccessibilityTests +enable_unused_import_analysis: true diff --git a/.periphery.yml b/.periphery.yml index d881e1e07..01dd780bb 100644 --- a/.periphery.yml +++ b/.periphery.yml @@ -8,3 +8,4 @@ targets: - XcodeTests - SPMTests - AccessibilityTests +enable_unused_import_analysis: true diff --git a/CHANGELOG.md b/CHANGELOG.md index bc40e7bd6..bb9206e2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ ##### Enhancements -- None. +- Add experimental unused import analysis. ##### Bug Fixes diff --git a/Sources/Frontend/Commands/ScanCommand.swift b/Sources/Frontend/Commands/ScanCommand.swift index dc75019a4..1bc2c121d 100644 --- a/Sources/Frontend/Commands/ScanCommand.swift +++ b/Sources/Frontend/Commands/ScanCommand.swift @@ -54,6 +54,9 @@ struct ScanCommand: FrontendCommand { @Flag(help: "Disable identification of redundant public accessibility") var disableRedundantPublicAnalysis: Bool = defaultConfiguration.$disableRedundantPublicAnalysis.defaultValue + @Flag(help: "Enable identification of unused imports (experimental)") + var enableUnusedImportAnalysis: Bool = defaultConfiguration.$enableUnusedImportsAnalysis.defaultValue + @Flag(help: "Retain properties that are assigned, but never used") var retainAssignOnlyProperties: Bool = defaultConfiguration.$retainAssignOnlyProperties.defaultValue @@ -127,6 +130,7 @@ struct ScanCommand: FrontendCommand { configuration.apply(\.$retainUnusedProtocolFuncParams, retainUnusedProtocolFuncParams) configuration.apply(\.$retainSwiftUIPreviews, retainSwiftUIPreviews) configuration.apply(\.$disableRedundantPublicAnalysis, disableRedundantPublicAnalysis) + configuration.apply(\.$enableUnusedImportsAnalysis, enableUnusedImportAnalysis) configuration.apply(\.$externalEncodableProtocols, externalEncodableProtocols) configuration.apply(\.$externalTestCaseClasses, externalTestCaseClasses) configuration.apply(\.$verbose, verbose) diff --git a/Sources/Frontend/main.swift b/Sources/Frontend/main.swift index 6543c2a44..eec43590d 100644 --- a/Sources/Frontend/main.swift +++ b/Sources/Frontend/main.swift @@ -1,6 +1,5 @@ import Foundation import ArgumentParser -import PeripheryKit import Shared Logger.configureBuffering() diff --git a/Sources/PeripheryKit/Indexer/Declaration.swift b/Sources/PeripheryKit/Indexer/Declaration.swift index 60ed7d71d..7f0212e58 100644 --- a/Sources/PeripheryKit/Indexer/Declaration.swift +++ b/Sources/PeripheryKit/Indexer/Declaration.swift @@ -109,10 +109,29 @@ final class Declaration { } } + var extensionKind: Kind? { + switch self { + case .class: + return .extensionClass + case .struct: + return .extensionStruct + case .enum: + return .extensionEnum + case .protocol: + return .extensionProtocol + default: + return nil + } + } + var isExtensionKind: Bool { rawValue.hasPrefix("extension") } + var isExtendableKind: Bool { + isConcreteTypeDeclarableKind + } + var isConformableKind: Bool { isDiscreteConformableKind || isExtensionKind } @@ -125,6 +144,10 @@ final class Declaration { return [.class, .struct, .enum] } + var isConcreteTypeDeclarableKind: Bool { + Self.concreteTypeDeclarableKinds.contains(self) + } + static var concreteTypeDeclarableKinds: Set { return [.class, .struct, .enum, .typealias] } @@ -147,6 +170,8 @@ final class Declaration { var displayName: String? { switch self { + case .module: + return "imported module" case .class: return "class" case .protocol: diff --git a/Sources/PeripheryKit/Indexer/SourceFile.swift b/Sources/PeripheryKit/Indexer/SourceFile.swift index e74d6c6e0..dea3db26d 100644 --- a/Sources/PeripheryKit/Indexer/SourceFile.swift +++ b/Sources/PeripheryKit/Indexer/SourceFile.swift @@ -2,8 +2,6 @@ import Foundation import SystemPackage class SourceFile { - typealias ImportStatement = (parts: [String], isTestable: Bool) - let path: FilePath let modules: Set var importStatements: [ImportStatement] = [] diff --git a/Sources/PeripheryKit/Indexer/SwiftIndexer.swift b/Sources/PeripheryKit/Indexer/SwiftIndexer.swift index c91809ba2..665805997 100644 --- a/Sources/PeripheryKit/Indexer/SwiftIndexer.swift +++ b/Sources/PeripheryKit/Indexer/SwiftIndexer.swift @@ -240,6 +240,14 @@ public final class SwiftIndexer: Indexer { multiplexingSyntaxVisitor.visit() sourceFile.importStatements = importSyntaxVisitor.importStatements + + if configuration.enableUnusedImportsAnalysis { + for stmt in sourceFile.importStatements { + if stmt.isExported { + graph.addExportedModule(stmt.module, exportedBy: sourceFile.modules) + } + } + } associateLatentReferences() associateDanglingReferences() @@ -266,6 +274,10 @@ public final class SwiftIndexer: Indexer { } } + if configuration.enableUnusedImportsAnalysis { + graph.addIndexedModules(modules) + } + let sourceFile = SourceFile(path: file, modules: modules) self.sourceFile = sourceFile return sourceFile diff --git a/Sources/PeripheryKit/Indexer/XibParser.swift b/Sources/PeripheryKit/Indexer/XibParser.swift index 12888308e..da66a33de 100644 --- a/Sources/PeripheryKit/Indexer/XibParser.swift +++ b/Sources/PeripheryKit/Indexer/XibParser.swift @@ -1,7 +1,6 @@ import Foundation import AEXML import SystemPackage -import Shared final class XibParser { private let path: FilePath diff --git a/Sources/PeripheryKit/SPM/SPMProjectDriver.swift b/Sources/PeripheryKit/SPM/SPMProjectDriver.swift index 7e7fa1aa8..ef0c27c81 100644 --- a/Sources/PeripheryKit/SPM/SPMProjectDriver.swift +++ b/Sources/PeripheryKit/SPM/SPMProjectDriver.swift @@ -74,7 +74,10 @@ extension SPMProjectDriver: ProjectDriver { storePaths = [FilePath(package.path).appending(".build/debug/index/store")] } - try SwiftIndexer(sourceFiles: sourceFiles, graph: graph, indexStorePaths: storePaths).perform() + try SwiftIndexer( + sourceFiles: sourceFiles, + graph: graph, + indexStorePaths: storePaths).perform() graph.indexingComplete() } diff --git a/Sources/PeripheryKit/ScanResultBuilder.swift b/Sources/PeripheryKit/ScanResultBuilder.swift index 67d9bdf63..391fb7992 100644 --- a/Sources/PeripheryKit/ScanResultBuilder.swift +++ b/Sources/PeripheryKit/ScanResultBuilder.swift @@ -1,10 +1,11 @@ import Foundation -import Shared public struct ScanResultBuilder { public static func build(for graph: SourceGraph) -> [ScanResult] { let assignOnlyProperties = graph.assignOnlyProperties - let removableDeclarations = graph.unusedDeclarations.subtracting(assignOnlyProperties) + let removableDeclarations = graph.unusedDeclarations + .subtracting(assignOnlyProperties) + .union(graph.unusedModuleImports) let redundantProtocols = graph.redundantProtocols.filter { !removableDeclarations.contains($0.0) } let redundantPublicAccessibility = graph.redundantPublicAccessibility.filter { !removableDeclarations.contains($0.0) } diff --git a/Sources/PeripheryKit/SourceGraph/Mutators/RedundantExplicitPublicAccessibilityMarker.swift b/Sources/PeripheryKit/SourceGraph/Mutators/RedundantExplicitPublicAccessibilityMarker.swift index 9e6853683..7e3cc0a7c 100644 --- a/Sources/PeripheryKit/SourceGraph/Mutators/RedundantExplicitPublicAccessibilityMarker.swift +++ b/Sources/PeripheryKit/SourceGraph/Mutators/RedundantExplicitPublicAccessibilityMarker.swift @@ -159,8 +159,8 @@ final class RedundantExplicitPublicAccessibilityMarker: SourceGraphMutator { let referenceFiles = graph.references(to: decl).map { $0.location.file } let referenceModules = referenceFiles.flatMapSet { file -> Set in - let importsDeclModuleTestable = file.importStatements.contains(where: { (parts, isTestable) in - isTestable && !Set(parts).isDisjoint(with: decl.location.file.modules) + let importsDeclModuleTestable = file.importStatements.contains(where: { + $0.isTestable && decl.location.file.modules.contains($0.module) }) if !importsDeclModuleTestable { diff --git a/Sources/PeripheryKit/SourceGraph/Mutators/UnusedImportMarker.swift b/Sources/PeripheryKit/SourceGraph/Mutators/UnusedImportMarker.swift new file mode 100644 index 000000000..0d3fc2d99 --- /dev/null +++ b/Sources/PeripheryKit/SourceGraph/Mutators/UnusedImportMarker.swift @@ -0,0 +1,82 @@ +import Foundation +import Shared + +/// Marks unused import statements (experimental). +/// +/// A module import is unused when the source file contains no references to it, and no other +/// imported modules either export it, or extend declarations declared by it. +/// +/// Testing TODO: +/// * Exports, including nested exports +/// * Public declaration extended by another module +final class UnusedImportMarker: SourceGraphMutator { + private let graph: SourceGraph + private let configuration: Configuration + + required init(graph: SourceGraph, configuration: Configuration) { + self.graph = graph + self.configuration = configuration + } + + func mutate() throws { + guard configuration.enableUnusedImportsAnalysis else { return } + + var referencedModulesByFile = [SourceFile: Set]() + + // Build a mapping of source files and the modules they reference. + for ref in graph.allReferences { + guard let decl = graph.explicitDeclaration(withUsr: ref.usr) else { continue } + // Record directly referenced modules and also identify any modules that extended + // the declaration. These extensions may provide members/conformances that aren't + // referenced directly but which are still required. + let referencedModules = decl.location.file.modules.union(modulesExtending(decl)) + referencedModulesByFile[ref.location.file, default: []].formUnion(referencedModules) + } + + // For each source file, determine whether its imports are unused. + for (file, referencedModules) in referencedModulesByFile { + let unreferencedImports = file.importStatements + .filter { + // Only consider modules that have been indexed as we need to see which modules + // they export. + graph.indexedModules.contains($0.module) && + !referencedModules.contains($0.module) + } + + for unreferencedImport in unreferencedImports { + // In the simple case, a module is unused if it's not referenced. However, it's + // possible the module exports other referenced modules. + guard !referencedModules.contains(where: { + graph.isModule($0, exportedBy: unreferencedImport.module) + }) else { continue } + + graph.markUnusedModuleImport(unreferencedImport) + } + } + } + + // MARK: - Private + + private var extendedDeclCache: [Declaration: Set] = [:] + + /// Identifies any modules that extend the given declaration. + private func modulesExtending(_ decl: Declaration) -> Set { + guard decl.kind.isExtendableKind else { return [] } + + if let modules = extendedDeclCache[decl] { + return modules + } + + let modules: Set = graph.references(to: decl) + .flatMapSet { + guard let parent = $0.parent, + parent.kind == decl.kind.extensionKind, + parent.name == decl.name + else { return [] } + + return parent.location.file.modules + } + extendedDeclCache[decl] = modules + return modules + } +} diff --git a/Sources/PeripheryKit/SourceGraph/SourceGraph.swift b/Sources/PeripheryKit/SourceGraph/SourceGraph.swift index 5194ba33c..53d8f9a9e 100644 --- a/Sources/PeripheryKit/SourceGraph/SourceGraph.swift +++ b/Sources/PeripheryKit/SourceGraph/SourceGraph.swift @@ -17,10 +17,13 @@ public final class SourceGraph { private(set) var assetReferences: Set = [] private(set) var mainAttributedDeclarations: Set = [] private(set) var allReferencesByUsr: [String: Set] = [:] + private(set) var indexedModules: Set = [] + private(set) var unusedModuleImports: Set = [] private var potentialAssignOnlyProperties: Set = [] private var allDeclarationsByKind: [Declaration.Kind: Set] = [:] private var allExplicitDeclarationsByUsr: [String: Declaration] = [:] + private var moduleToExportingModules: [String: Set] = [:] private let lock = UnfairLock() @@ -150,12 +153,6 @@ public final class SourceGraph { declaration.usrs.forEach { allExplicitDeclarationsByUsr.removeValue(forKey: $0) } } - func add(_ reference: Reference) { - withLock { - addUnsafe(reference) - } - } - func addUnsafe(_ reference: Reference) { _ = allReferences.insert(reference) allReferencesByUsr[reference.usr, default: []].insert(reference) @@ -173,9 +170,9 @@ public final class SourceGraph { } else { _ = declaration.references.insert(reference) } - } - add(reference) + addUnsafe(reference) + } } func remove(_ reference: Reference) { @@ -215,6 +212,48 @@ public final class SourceGraph { explicitDeclaration(withUsr: reference.usr) == nil } + func addIndexedModules(_ modules: Set) { + withLock { + indexedModules.formUnion(modules) + } + } + + func addExportedModule(_ module: String, exportedBy exportingModules: Set) { + withLock { + moduleToExportingModules[module, default: []].formUnion(exportingModules) + } + } + + func isModule(_ module: String, exportedBy exportingModule: String) -> Bool { + withLock { + isModuleUnsafe(module, exportedBy: exportingModule) + } + } + + private func isModuleUnsafe(_ module: String, exportedBy exportingModule: String) -> Bool { + let exportingModules = moduleToExportingModules[module, default: []] + + if exportingModules.contains(exportingModule) { + // The module is exported directly. + return true + } + + // Recursively check if the module is exported transitively. + return exportingModules.contains { nestedExportingModule in + return isModuleUnsafe(nestedExportingModule, exportedBy: exportingModule) && + isModuleUnsafe(module, exportedBy: nestedExportingModule) + } + } + + func markUnusedModuleImport(_ statement: ImportStatement) { + withLock { + let usr = "\(statement.location.description)-\(statement.module)" + let decl = Declaration(kind: .module, usrs: [usr], location: statement.location) + decl.name = statement.module + unusedModuleImports.insert(decl) + } + } + func inheritedTypeReferences(of decl: Declaration, seenDeclarations: Set = []) -> [Reference] { var references: [Reference] = [] diff --git a/Sources/PeripheryKit/SourceGraph/SourceGraphMutatorRunner.swift b/Sources/PeripheryKit/SourceGraph/SourceGraphMutatorRunner.swift index 6a4eee336..559674fed 100644 --- a/Sources/PeripheryKit/SourceGraph/SourceGraphMutatorRunner.swift +++ b/Sources/PeripheryKit/SourceGraph/SourceGraphMutatorRunner.swift @@ -7,6 +7,9 @@ public final class SourceGraphMutatorRunner { } private let mutators: [SourceGraphMutator.Type] = [ + // Must come before all others as we need to observe all references prior to any mutations. + UnusedImportMarker.self, + // Must come before ExtensionReferenceBuilder. AccessibilityCascader.self, ObjCAccessibleRetainer.self, @@ -44,7 +47,7 @@ public final class SourceGraphMutatorRunner { AssignOnlyPropertyReferenceEliminator.self, UsedDeclarationMarker.self, - RedundantProtocolMarker.self + RedundantProtocolMarker.self, ] private let graph: SourceGraph diff --git a/Sources/PeripheryKit/Syntax/ImportSyntaxVisitor.swift b/Sources/PeripheryKit/Syntax/ImportSyntaxVisitor.swift index 5ff471c19..190b01a57 100644 --- a/Sources/PeripheryKit/Syntax/ImportSyntaxVisitor.swift +++ b/Sources/PeripheryKit/Syntax/ImportSyntaxVisitor.swift @@ -1,16 +1,32 @@ import Foundation import SwiftSyntax -final class ImportSyntaxVisitor: PeripherySyntaxVisitor { - typealias ImportStatement = (parts: [String], isTestable: Bool) +struct ImportStatement { + let module: String + let isTestable: Bool + let isExported: Bool + let location: SourceLocation +} +final class ImportSyntaxVisitor: PeripherySyntaxVisitor { var importStatements: [ImportStatement] = [] - init(sourceLocationBuilder: SourceLocationBuilder) {} + private let sourceLocationBuilder: SourceLocationBuilder + + init(sourceLocationBuilder: SourceLocationBuilder) { + self.sourceLocationBuilder = sourceLocationBuilder + } func visit(_ node: ImportDeclSyntax) { let parts = node.path.map { $0.name.text } - let attributes = node.attributes.compactMap { $0.as(AttributeSyntax.self)?.attributeName.trimmedDescription } - importStatements.append((parts, attributes.contains("testable"))) + let module = parts.first ?? "" + let attributes = node.attributes.compactMap { $0.as(AttributeSyntax.self)?.attributeName.trimmedDescription } + let location = sourceLocationBuilder.location(at: node.positionAfterSkippingLeadingTrivia) + let statement = ImportStatement( + module: module, + isTestable: attributes.contains("testable"), + isExported: attributes.contains("_exported"), + location: location) + importStatements.append(statement) } } diff --git a/Sources/Shared/Configuration.swift b/Sources/Shared/Configuration.swift index 4dc55f446..bdae3aac5 100644 --- a/Sources/Shared/Configuration.swift +++ b/Sources/Shared/Configuration.swift @@ -71,6 +71,9 @@ public final class Configuration { @Setting(key: "disable_redundant_public_analysis", defaultValue: false) public var disableRedundantPublicAnalysis: Bool + @Setting(key: "enable_unused_import_analysis", defaultValue: false) + public var enableUnusedImportsAnalysis: Bool + @Setting(key: "verbose", defaultValue: false) public var verbose: Bool @@ -180,6 +183,10 @@ public final class Configuration { config[$disableRedundantPublicAnalysis.key] = disableRedundantPublicAnalysis } + if $enableUnusedImportsAnalysis.hasNonDefaultValue { + config[$enableUnusedImportsAnalysis.key] = enableUnusedImportsAnalysis + } + if $verbose.hasNonDefaultValue { config[$verbose.key] = verbose } @@ -270,6 +277,8 @@ public final class Configuration { $retainSwiftUIPreviews.assign(value) case $disableRedundantPublicAnalysis.key: $disableRedundantPublicAnalysis.assign(value) + case $enableUnusedImportsAnalysis.key: + $enableUnusedImportsAnalysis.assign(value) case $verbose.key: $verbose.assign(value) case $quiet.key: @@ -312,6 +321,7 @@ public final class Configuration { $retainUnusedProtocolFuncParams.reset() $retainSwiftUIPreviews.reset() $disableRedundantPublicAnalysis.reset() + $enableUnusedImportsAnalysis.reset() $externalEncodableProtocols.reset() $externalTestCaseClasses.reset() $verbose.reset() diff --git a/Sources/XcodeSupport/XcodeTarget.swift b/Sources/XcodeSupport/XcodeTarget.swift index f40049050..cb2b6ce11 100644 --- a/Sources/XcodeSupport/XcodeTarget.swift +++ b/Sources/XcodeSupport/XcodeTarget.swift @@ -2,7 +2,6 @@ import Foundation import XcodeProj import SystemPackage import PeripheryKit -import Shared final class XcodeTarget { let project: XcodeProject diff --git a/Sources/XcodeSupport/Xcodebuild.swift b/Sources/XcodeSupport/Xcodebuild.swift index e084c1617..588202e94 100644 --- a/Sources/XcodeSupport/Xcodebuild.swift +++ b/Sources/XcodeSupport/Xcodebuild.swift @@ -1,6 +1,5 @@ import Foundation import SystemPackage -import PeripheryKit import Shared public final class Xcodebuild { diff --git a/Tests/AccessibilityTests/RedundantPublicAccessibilityTest.swift b/Tests/AccessibilityTests/RedundantPublicAccessibilityTest.swift index 1ce19fbd0..0267eabb3 100644 --- a/Tests/AccessibilityTests/RedundantPublicAccessibilityTest.swift +++ b/Tests/AccessibilityTests/RedundantPublicAccessibilityTest.swift @@ -1,5 +1,4 @@ import XCTest -import Shared @testable import TestShared @testable import PeripheryKit diff --git a/Tests/PeripheryTests/CrossModuleRetentionTest.swift b/Tests/PeripheryTests/CrossModuleRetentionTest.swift index 4dc359bdc..e79be95b2 100644 --- a/Tests/PeripheryTests/CrossModuleRetentionTest.swift +++ b/Tests/PeripheryTests/CrossModuleRetentionTest.swift @@ -1,6 +1,5 @@ import XCTest import SystemPackage -import Shared @testable import TestShared @testable import PeripheryKit diff --git a/Tests/PeripheryTests/ObjcAccessibleRetentionTest.swift b/Tests/PeripheryTests/ObjcAccessibleRetentionTest.swift index ede5037d9..743d32ba7 100644 --- a/Tests/PeripheryTests/ObjcAccessibleRetentionTest.swift +++ b/Tests/PeripheryTests/ObjcAccessibleRetentionTest.swift @@ -1,6 +1,5 @@ import XCTest import SystemPackage -import Shared @testable import TestShared @testable import PeripheryKit diff --git a/Tests/PeripheryTests/ObjcAnnotatedRetentionTest.swift b/Tests/PeripheryTests/ObjcAnnotatedRetentionTest.swift index 896e06dea..3fa5e0e7c 100644 --- a/Tests/PeripheryTests/ObjcAnnotatedRetentionTest.swift +++ b/Tests/PeripheryTests/ObjcAnnotatedRetentionTest.swift @@ -1,6 +1,5 @@ import XCTest import SystemPackage -import Shared @testable import TestShared @testable import PeripheryKit diff --git a/Tests/SPMTests/SPMProjectTest.swift b/Tests/SPMTests/SPMProjectTest.swift index 8ed887142..7e08b1a37 100644 --- a/Tests/SPMTests/SPMProjectTest.swift +++ b/Tests/SPMTests/SPMProjectTest.swift @@ -1,5 +1,4 @@ import XCTest -import Shared @testable import TestShared @testable import PeripheryKit diff --git a/Tests/XcodeTests/SwiftUIProjectTest.swift b/Tests/XcodeTests/SwiftUIProjectTest.swift index 0390db3f9..812a0b921 100644 --- a/Tests/XcodeTests/SwiftUIProjectTest.swift +++ b/Tests/XcodeTests/SwiftUIProjectTest.swift @@ -1,8 +1,6 @@ import XCTest -import Shared @testable import TestShared @testable import XcodeSupport -@testable import PeripheryKit class SwiftUIProjectTest: SourceGraphTestCase { override static func setUp() { diff --git a/Tests/XcodeTests/UIKitProjectTest.swift b/Tests/XcodeTests/UIKitProjectTest.swift index 307142441..7e72bbbf9 100644 --- a/Tests/XcodeTests/UIKitProjectTest.swift +++ b/Tests/XcodeTests/UIKitProjectTest.swift @@ -1,8 +1,6 @@ import XCTest -import Shared @testable import TestShared @testable import XcodeSupport -@testable import PeripheryKit class UIKitProjectTest: SourceGraphTestCase { override static func setUp() { diff --git a/Tests/XcodeTests/XcodebuildTest.swift b/Tests/XcodeTests/XcodebuildTest.swift index 466167613..fff8655df 100644 --- a/Tests/XcodeTests/XcodebuildTest.swift +++ b/Tests/XcodeTests/XcodebuildTest.swift @@ -2,7 +2,6 @@ import Foundation import XCTest import Shared @testable import XcodeSupport -@testable import PeripheryKit class XcodebuildBuildProjectTest: XCTestCase { var xcodebuild: Xcodebuild!