From 1fccca3ee9bd871a0e57832d9acc8f527fb0137d Mon Sep 17 00:00:00 2001 From: Ian Leitch Date: Sat, 3 Aug 2024 15:53:30 +0200 Subject: [PATCH] Automatic target detection (#767) --- .github/workflows/test.yml | 2 +- .gitignore | 1 + .periphery.linux.yml | 11 - .periphery.yml | 13 - Package.resolved | 20 +- Package.swift | 47 +-- Sources/Frontend/Commands/ScanBehavior.swift | 18 - Sources/Frontend/Commands/ScanCommand.swift | 17 +- Sources/Frontend/CommonSetupGuide.swift | 4 +- Sources/Frontend/GuidedSetup.swift | 11 +- Sources/Frontend/Project.swift | 2 +- Sources/Frontend/SPMProjectSetupGuide.swift | 39 +- Sources/Frontend/Scan.swift | 5 +- Sources/Indexer/IndexTarget.swift | 9 - Sources/Indexer/Indexer.swift | 5 + Sources/Indexer/JobPool.swift | 10 +- Sources/Indexer/SourceFileCollector.swift | 60 +++ Sources/Indexer/SwiftIndexer.swift | 121 ++---- .../Generic/GenericProjectDriver.swift | 42 +- Sources/PeripheryKit/ProjectDriver.swift | 9 +- Sources/PeripheryKit/SPM/SPM.swift | 83 +--- .../PeripheryKit/SPM/SPMProjectDriver.swift | 64 ++- Sources/Shared/Configuration.swift | 379 ++++-------------- Sources/Shared/OutputFormat.swift | 7 +- Sources/Shared/PeripheryError.swift | 10 - Sources/Shared/PropertyTypeSanitizer.swift | 5 +- .../Elements/ProjectFileKind.swift | 3 - Sources/XcodeSupport/XcodeProject.swift | 26 -- Sources/XcodeSupport/XcodeProjectDriver.swift | 122 ++---- .../XcodeSupport/XcodeProjectSetupGuide.swift | 38 +- Sources/XcodeSupport/XcodeProjectlike.swift | 2 - Sources/XcodeSupport/XcodeTarget.swift | 5 - Sources/XcodeSupport/XcodeWorkspace.swift | 6 - .../AccessibilityProject/Package.swift | 3 +- .../RedundantPublicAccessibilityTest.swift | 2 - Tests/Fixtures/Package.swift | 53 +++ ...stCrossModuleInheritanceWithSameName.swift | 0 .../CrossModuleSuperclass.swift | 0 .../FunctionFixture.swift | 0 .../PropertyFixture.swift | 0 .../ExternalAssociatedType.swift | 0 .../ExternalTestCase.swift | 0 ...NotRetainMembersOfObjcAnnotatedClass.swift | 0 ...sNotRetainObjcAnnotatedWithoutOption.swift | 0 ...tObjcMembersAnnotationRetainsMembers.swift | 0 ...RetainsImplicitlyObjcAccessibleClass.swift | 0 .../testRetainsObjcAnnotatedClass.swift | 0 .../testRetainsObjcAnnotatedMembers.swift | 0 .../testRetainsOptionalProtocolMethod.swift | 0 ...lProtocolMethodImplementedInSubclass.swift | 0 ...etainsAnnotatedExtensionDeclarations.swift | 0 ...larationsOnObjcMembersAnnotatedClass.swift | 0 ...nsObjcProtocolConformingDeclarations.swift | 0 .../testRetainsObjcProtocolMembers.swift | 0 .../RetentionFixtures/testAccessibility.swift | 0 ...ysisDoesNotApplyToProtocolProperties.swift | 0 .../testCircularTypeInheritance.swift | 0 ...lassRetainedByUnusedInstanceVariable.swift | 0 .../RetentionFixtures/testCodingKeyEnum.swift | 0 ...formanceToExternalProtocolIsRetained.swift | 0 ...otocolReferencedByNonReferencedClass.swift | 0 .../testCrossReferencedClasses.swift | 0 .../testCustomConstructorWithLiteral.swift | 0 .../testDeeplyNestedClassReferences.swift | 0 ...RetainDescendantsOfUnusedDeclaration.swift | 0 .../testDoesNotRetainLazyProperty.swift | 0 ...ocolMembersImplementedByExternalType.swift | 0 ...dInSubclassWithDefaultImplementation.swift | 0 ...tocolMethodWithDefaultImplementation.swift | 0 .../RetentionFixtures/testEnumCases.swift | 0 .../testExternalXCTestCaseClass.swift | 0 ...estFunctionAccessorsRetainReferences.swift | 0 ...lyNamedVarsInStaticAndInstanceScopes.swift | 0 .../testIgnoreAllComment.swift | 0 .../testIgnoreComments.swift | 0 ...estIgnoreUnusedParamInUnusedFunction.swift | 0 .../testInstanceVarReferencedInClosure.swift | 0 .../testIsolatedCyclicRootReferences.swift | 0 .../testMainActorAnnotation.swift | 0 .../testNestedDeclarations.swift | 0 .../testNonReferencedClass.swift | 0 .../testNonReferencedFreeFunction.swift | 0 .../testNonReferencedMethod.swift | 0 ...tNonReferencedMethodInClassExtension.swift | 0 .../testNonReferencedProperty.swift | 0 .../testOverriddenMethodRetainedBySuper.swift | 0 ...estPropertyReferencedByComputedValue.swift | 0 ...formedByStaticMethodOutsideExtension.swift | 0 ...ProtocolConformingMembersAreRetained.swift | 0 ...ProtocolImplementInClassAndExtension.swift | 0 ...CalledIndirectlyByProtocolIsRetained.swift | 0 ...colMethodsImplementedOnlyInExtension.swift | 0 .../testProtocolUsedAsExistentialType.swift | 0 ...ProtocolMethodInSameClassIsRedundant.swift | 0 ...ocolMethodImplementedOnlyInExtension.swift | 0 ...dundantProtocolThatInheritsAnyObject.swift | 0 ...tProtocolThatInheritsForeignProtocol.swift | 0 ...ntProtocolThatInheritsOtherProtocols.swift | 0 .../testRequiredInitInSubclass.swift | 0 .../testRetainImplicitDeclarations.swift | 0 .../testRetainOverridingMethod.swift | 0 .../testRetainPublicMembers.swift | 0 .../testRetainUnusedProtocolFuncParams.swift | 0 ...NotRetainImplementationInUnusedClass.swift | 0 ...inedProtocolDoesNotRetainUnusedClass.swift | 0 .../testRetainsAssignOnlyPropertyTypes.swift | 0 .../testRetainsAssociatedTypeTypeAlias.swift | 0 .../testRetainsCallAsFunction.swift | 0 .../testRetainsCodableProperties.swift | 0 ...nsConstructorOfGenericClassAndStruct.swift | 0 .../testRetainsDefaultConstructor.swift | 0 .../testRetainsDestructor.swift | 0 ...tRetainsDynamicMemberLookupSubscript.swift | 0 .../testRetainsEncodableProperties.swift | 0 ...testRetainsExtendedExternalTypeAlias.swift | 0 ...testRetainsExtendedProtocolTypeAlias.swift | 0 .../testRetainsExtendedTypeAlias.swift | 0 ...tainsExternalAssociatedTypeTypeAlias.swift | 0 .../testRetainsExternalTypeExtension.swift | 0 .../testRetainsFilesOption.swift | 0 ...testRetainsForeignProtocolParameters.swift | 0 ...sForeignProtocolParametersInSubclass.swift | 0 ...ocolMembersImplementedByExternalType.swift | 0 ...metersOnUnimplementedProtocolMembers.swift | 0 ...tainsGenericProtocolExtensionMembers.swift | 0 .../testRetainsGenericType.swift | 0 .../testRetainsInferredAssociatedType.swift | 0 .../testRetainsInheritedClass.swift | 0 ...tRetainsInitializerCalledOnTypeAlias.swift | 0 ...thodDefinedInExtensionOnStandardType.swift | 0 ...ocolMethodDefinedInProtocolExtension.swift | 0 .../testRetainsOpenClassParameters.swift | 0 ...stRetainsParamUsedInOverriddenMethod.swift | 0 .../testRetainsPropertyWrappers.swift | 0 .../testRetainsProtocolExtension.swift | 0 ...ProtocolMethodImplementedInExtension.swift | 0 ...tocolMethodsImplementedInSuperclasss.swift | 0 .../testRetainsProtocolParameters.swift | 0 ...etainsProtocolsViaCompositeTypealias.swift | 0 .../testRetainsPublicEnumCases.swift | 0 ...stRetainsReferencedMethodViaReceiver.swift | 0 .../testRetainsResultBuilderMethods.swift | 0 ...tainsSelfReferencedMethodViaReceiver.swift | 0 ...ringInterpolationAppendInterpolation.swift | 0 ...dProtocolThatInheritsForeignProtocol.swift | 0 .../testSelfReferencedClass.swift | 0 .../testSelfReferencedConstructor.swift | 0 .../testSelfReferencedProperty.swift | 0 .../testSelfReferencedRecursiveMethod.swift | 0 ...SimpleAssignOnlyPropertyNameConflict.swift | 0 ...stSimplePropertyAssignedButNeverRead.swift | 0 .../testSimpleRedundantProtocol.swift | 0 .../testStaticMemberUsedAsSubscriptKey.swift | 0 ...aredWithCompositeValuesIsNotRetained.swift | 0 .../testStructImplicitInitializer.swift | 0 .../testUnusedAssociatedType.swift | 0 .../testUnusedOverriddenMethod.swift | 0 .../testUnusedProtocolWithExtension.swift | 0 .../testUnusedTypealias.swift | 0 ...TestCaseClassesAndMethodsAreRetained.swift | 0 .../TypeSyntaxInspectorFixture.swift | 0 .../testBackquote.swift | 0 .../testBlockParameter.swift | 0 .../testFatalErrorFunction.swift | 0 .../testFunctionCallWithNamedParameter.swift | 0 .../testIBActionAnnotatedFunction.swift | 0 .../testIgnoreProtocolDeclaration.swift | 0 .../testIgnoredParameter.swift | 0 .../testInitializer.swift | 0 .../testInitializerPosition.swift | 0 .../testLocalVarDeclaredInBlock.swift | 0 .../testLocalVariableAssignment.swift | 0 .../testMultiLineParameterPosition.swift | 0 .../testNestedFunction.swift | 0 .../testNestedVariable.swift | 0 .../testParamForGenericSpecialization.swift | 0 .../testParameterPosition.swift | 0 .../UnusedParameterFixtures/testReturn.swift | 0 .../testShadowed.swift | 0 .../testShadowedAfterUse.swift | 0 .../testShadowedByBlockParameter.swift | 0 .../testSimpleFunctionCall.swift | 0 .../testSimpleUnused.swift | 0 .../testStringInterpolation.swift | 0 .../testSubscriptArgument.swift | 0 .../testUsedInInitializerCall.swift | 0 .../CrossModuleRetentionTest.swift | 3 +- .../ObjcAccessibleRetentionTest.swift | 9 - .../ObjcAnnotatedRetentionTest.swift | 9 - Tests/PeripheryTests/RetentionTest.swift | 9 - .../Syntax/FunctionVisitTest.swift | 2 +- .../Syntax/PropertyVisitTest.swift | 2 +- .../Syntax/TypeSyntaxInspectorTest.swift | 2 +- Tests/SPMTests/SPMProject/Package.swift | 13 +- Tests/SPMTests/SPMProjectTest.swift | 2 - Tests/Shared/FixtureSourceGraphTestCase.swift | 25 +- Tests/Shared/Helper.swift | 4 + Tests/Shared/SourceGraphTestCase.swift | 12 +- Tests/Shared/XCTestCase+Extensions.swift | 2 +- Tests/XcodeTests/SwiftUIProjectTest.swift | 1 - .../LocalPackages/LocalPackage/Package.swift | 11 +- .../UIKitProject.xcodeproj/project.pbxproj | 8 +- .../FileInGroupWithoutFolder.swift | 1 - .../UIKitProject/FileInGroupWithoutFolder.xib | 10 + Tests/XcodeTests/UIKitProjectTest.swift | 2 - Tests/XcodeTests/XcodeTargetTest.swift | 4 +- 206 files changed, 431 insertions(+), 954 deletions(-) delete mode 100644 .periphery.linux.yml delete mode 100644 .periphery.yml delete mode 100644 Sources/Indexer/IndexTarget.swift create mode 100644 Sources/Indexer/SourceFileCollector.swift create mode 100644 Tests/Fixtures/Package.swift rename Tests/Fixtures/{ => Sources}/CrossModuleRetentionFixtures/testCrossModuleInheritanceWithSameName.swift (100%) rename Tests/Fixtures/{ => Sources}/CrossModuleRetentionSupportFixtures/CrossModuleSuperclass.swift (100%) rename Tests/Fixtures/{ => Sources}/DeclarationVisitorFixtures/FunctionFixture.swift (100%) rename Tests/Fixtures/{ => Sources}/DeclarationVisitorFixtures/PropertyFixture.swift (100%) rename Tests/Fixtures/{ => Sources}/ExternalModuleFixtures/ExternalAssociatedType.swift (100%) rename Tests/Fixtures/{ => Sources}/ExternalModuleFixtures/ExternalTestCase.swift (100%) rename Tests/Fixtures/{ => Sources}/ObjcAccessibleRetentionFixtures/testDoesNotRetainMembersOfObjcAnnotatedClass.swift (100%) rename Tests/Fixtures/{ => Sources}/ObjcAccessibleRetentionFixtures/testDoesNotRetainObjcAnnotatedWithoutOption.swift (100%) rename Tests/Fixtures/{ => Sources}/ObjcAccessibleRetentionFixtures/testObjcMembersAnnotationRetainsMembers.swift (100%) rename Tests/Fixtures/{ => Sources}/ObjcAccessibleRetentionFixtures/testRetainsImplicitlyObjcAccessibleClass.swift (100%) rename Tests/Fixtures/{ => Sources}/ObjcAccessibleRetentionFixtures/testRetainsObjcAnnotatedClass.swift (100%) rename Tests/Fixtures/{ => Sources}/ObjcAccessibleRetentionFixtures/testRetainsObjcAnnotatedMembers.swift (100%) rename Tests/Fixtures/{ => Sources}/ObjcAccessibleRetentionFixtures/testRetainsOptionalProtocolMethod.swift (100%) rename Tests/Fixtures/{ => Sources}/ObjcAccessibleRetentionFixtures/testRetainsOptionalProtocolMethodImplementedInSubclass.swift (100%) rename Tests/Fixtures/{ => Sources}/ObjcAnnotatedRetentionFixtures/testRetainsAnnotatedExtensionDeclarations.swift (100%) rename Tests/Fixtures/{ => Sources}/ObjcAnnotatedRetentionFixtures/testRetainsExtensionDeclarationsOnObjcMembersAnnotatedClass.swift (100%) rename Tests/Fixtures/{ => Sources}/ObjcAnnotatedRetentionFixtures/testRetainsObjcProtocolConformingDeclarations.swift (100%) rename Tests/Fixtures/{ => Sources}/ObjcAnnotatedRetentionFixtures/testRetainsObjcProtocolMembers.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testAccessibility.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testAssignOnlyPropertyAnalysisDoesNotApplyToProtocolProperties.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testCircularTypeInheritance.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testClassRetainedByUnusedInstanceVariable.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testCodingKeyEnum.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testConformanceToExternalProtocolIsRetained.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testConformingProtocolReferencedByNonReferencedClass.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testCrossReferencedClasses.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testCustomConstructorWithLiteral.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testDeeplyNestedClassReferences.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testDoesNotRetainDescendantsOfUnusedDeclaration.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testDoesNotRetainLazyProperty.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testDoesNotRetainProtocolMembersImplementedByExternalType.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testDoesNotRetainProtocolMethodInSubclassWithDefaultImplementation.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testDoesNotRetainUnusedProtocolMethodWithDefaultImplementation.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testEnumCases.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testExternalXCTestCaseClass.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testFunctionAccessorsRetainReferences.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testIdenticallyNamedVarsInStaticAndInstanceScopes.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testIgnoreAllComment.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testIgnoreComments.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testIgnoreUnusedParamInUnusedFunction.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testInstanceVarReferencedInClosure.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testIsolatedCyclicRootReferences.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testMainActorAnnotation.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testNestedDeclarations.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testNonReferencedClass.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testNonReferencedFreeFunction.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testNonReferencedMethod.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testNonReferencedMethodInClassExtension.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testNonReferencedProperty.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testOverriddenMethodRetainedBySuper.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testPropertyReferencedByComputedValue.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testProtocolConformedByStaticMethodOutsideExtension.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testProtocolConformingMembersAreRetained.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testProtocolImplementInClassAndExtension.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testProtocolMethodCalledIndirectlyByProtocolIsRetained.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testProtocolMethodsImplementedOnlyInExtension.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testProtocolUsedAsExistentialType.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testProtocolVarReferencedByProtocolMethodInSameClassIsRedundant.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testPublicProtocolMethodImplementedOnlyInExtension.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRedundantProtocolThatInheritsAnyObject.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRedundantProtocolThatInheritsForeignProtocol.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRedundantProtocolThatInheritsOtherProtocols.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRequiredInitInSubclass.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainImplicitDeclarations.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainOverridingMethod.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainPublicMembers.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainUnusedProtocolFuncParams.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainedProtocolDoesNotRetainImplementationInUnusedClass.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainedProtocolDoesNotRetainUnusedClass.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsAssignOnlyPropertyTypes.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsAssociatedTypeTypeAlias.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsCallAsFunction.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsCodableProperties.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsConstructorOfGenericClassAndStruct.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsDefaultConstructor.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsDestructor.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsDynamicMemberLookupSubscript.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsEncodableProperties.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsExtendedExternalTypeAlias.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsExtendedProtocolTypeAlias.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsExtendedTypeAlias.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsExternalAssociatedTypeTypeAlias.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsExternalTypeExtension.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsFilesOption.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsForeignProtocolParameters.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsForeignProtocolParametersInSubclass.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsFunctionParametersOnProtocolMembersImplementedByExternalType.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsFunctionParametersOnUnimplementedProtocolMembers.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsGenericProtocolExtensionMembers.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsGenericType.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsInferredAssociatedType.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsInheritedClass.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsInitializerCalledOnTypeAlias.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsMethodDefinedInExtensionOnStandardType.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsNonProtocolMethodDefinedInProtocolExtension.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsOpenClassParameters.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsParamUsedInOverriddenMethod.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsPropertyWrappers.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsProtocolExtension.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsProtocolMethodImplementedInExtension.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsProtocolMethodsImplementedInSuperclasss.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsProtocolParameters.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsProtocolsViaCompositeTypealias.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsPublicEnumCases.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsReferencedMethodViaReceiver.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsResultBuilderMethods.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsSelfReferencedMethodViaReceiver.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsStringInterpolationAppendInterpolation.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testRetainsUsedProtocolThatInheritsForeignProtocol.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testSelfReferencedClass.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testSelfReferencedConstructor.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testSelfReferencedProperty.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testSelfReferencedRecursiveMethod.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testSimpleAssignOnlyPropertyNameConflict.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testSimplePropertyAssignedButNeverRead.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testSimpleRedundantProtocol.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testStaticMemberUsedAsSubscriptKey.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testStaticPropertyDeclaredWithCompositeValuesIsNotRetained.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testStructImplicitInitializer.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testUnusedAssociatedType.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testUnusedOverriddenMethod.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testUnusedProtocolWithExtension.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testUnusedTypealias.swift (100%) rename Tests/Fixtures/{ => Sources}/RetentionFixtures/testXCTestCaseClassesAndMethodsAreRetained.swift (100%) rename Tests/Fixtures/{ => Sources}/TypeSyntaxInspectorFixtures/TypeSyntaxInspectorFixture.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testBackquote.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testBlockParameter.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testFatalErrorFunction.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testFunctionCallWithNamedParameter.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testIBActionAnnotatedFunction.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testIgnoreProtocolDeclaration.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testIgnoredParameter.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testInitializer.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testInitializerPosition.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testLocalVarDeclaredInBlock.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testLocalVariableAssignment.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testMultiLineParameterPosition.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testNestedFunction.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testNestedVariable.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testParamForGenericSpecialization.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testParameterPosition.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testReturn.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testShadowed.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testShadowedAfterUse.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testShadowedByBlockParameter.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testSimpleFunctionCall.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testSimpleUnused.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testStringInterpolation.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testSubscriptArgument.swift (100%) rename Tests/Fixtures/{ => Sources}/UnusedParameterFixtures/testUsedInInitializerCall.swift (100%) delete mode 100644 Tests/XcodeTests/UIKitProject/UIKitProject/FileInGroupWithoutFolder.swift create mode 100644 Tests/XcodeTests/UIKitProject/UIKitProject/FileInGroupWithoutFolder.xib diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7b92bd10b1..c17d6f5ddc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -91,4 +91,4 @@ jobs: - name: Test run: ${{ env.swift_test }} - name: Scan - run: ${{ env.periphery_scan }} --config .periphery.linux.yml + run: ${{ env.periphery_scan }} diff --git a/.gitignore b/.gitignore index 4b986841a0..55c8033166 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ DerivedData *.gcda *.gcno .swiftpm +Tests/Fixtures/.build/ # VSCode .vscode/* \ No newline at end of file diff --git a/.periphery.linux.yml b/.periphery.linux.yml deleted file mode 100644 index 2029e94580..0000000000 --- a/.periphery.linux.yml +++ /dev/null @@ -1,11 +0,0 @@ -targets: -- Frontend -- Shared -- PeripheryKit -- TestShared -- PeripheryTests -- SPMTests -- AccessibilityTests -- SourceGraph -- SyntaxAnalysis -- Indexer diff --git a/.periphery.yml b/.periphery.yml deleted file mode 100644 index 584a8c668f..0000000000 --- a/.periphery.yml +++ /dev/null @@ -1,13 +0,0 @@ -targets: -- Frontend -- Shared -- PeripheryKit -- XcodeSupport -- TestShared -- PeripheryTests -- XcodeTests -- SPMTests -- AccessibilityTests -- SourceGraph -- SyntaxAnalysis -- Indexer diff --git a/Package.resolved b/Package.resolved index 739c218075..ca5042b6f7 100644 --- a/Package.resolved +++ b/Package.resolved @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-argument-parser", "state" : { - "revision" : "0fbc8848e389af3bb55c182bc19ca9d5dc2f255b", - "version" : "1.4.0" + "revision" : "41982a3656a71c768319979febd796c6fd111d5c", + "version" : "1.5.0" } }, { @@ -59,8 +59,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-syntax", "state" : { - "revision" : "303e5c5c36d6a558407d364878df131c3546fad8", - "version" : "510.0.2" + "revision" : "2bc86522d115234d1f588efe2bcb4ce4be8f8b82", + "version" : "510.0.3" } }, { @@ -68,8 +68,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-system", "state" : { - "revision" : "6a9e38e7bd22a3b8ba80bddf395623cf68f57807", - "version" : "1.3.1" + "revision" : "d2ba781702a1d8285419c15ee62fd734a9437ff5", + "version" : "1.3.2" } }, { @@ -77,8 +77,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/tuist/xcodeproj", "state" : { - "revision" : "20d5803c70e4ac5d67151ea6f5624a8136ab8fe0", - "version" : "8.21.0" + "revision" : "7713589d4d1bceedd02899d9c44b8e57be05ea35", + "version" : "8.22.0" } }, { @@ -86,8 +86,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/jpsim/Yams", "state" : { - "revision" : "9234124cff5e22e178988c18d8b95a8ae8007f76", - "version" : "5.1.2" + "revision" : "3036ba9d69cf1fd04d433527bc339dc0dc75433d", + "version" : "5.1.3" } } ], diff --git a/Package.swift b/Package.swift index 2594193335..5612755a32 100644 --- a/Package.swift +++ b/Package.swift @@ -7,7 +7,7 @@ var dependencies: [Package.Dependency] = [ .package(url: "https://github.com/tadija/AEXML", from: "4.0.0"), .package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0"), .package(url: "https://github.com/ileitch/swift-indexstore", from: "9.0.4"), - .package(url: "https://github.com/apple/swift-syntax", from: "510.0.2"), + .package(url: "https://github.com/apple/swift-syntax", from: "510.0.3"), .package(url: "https://github.com/ileitch/swift-filename-matcher", from: "0.0.0") ] @@ -89,43 +89,6 @@ var targets: [PackageDescription.Target] = [ ], path: "Tests/Shared" ), - .target( - name: "ExternalModuleFixtures", - path: "Tests/Fixtures/ExternalModuleFixtures" - ), - .target( - name: "CrossModuleRetentionFixtures", - dependencies: [ - .target(name: "CrossModuleRetentionSupportFixtures") - ], - path: "Tests/Fixtures/CrossModuleRetentionFixtures" - ), - .target( - name: "CrossModuleRetentionSupportFixtures", - path: "Tests/Fixtures/CrossModuleRetentionSupportFixtures" - ), - .target( - name: "RetentionFixtures", - dependencies: [ - .target(name: "ExternalModuleFixtures") - ], - path: "Tests/Fixtures/RetentionFixtures" - ), - .target( - name: "UnusedParameterFixtures", - path: "Tests/Fixtures/UnusedParameterFixtures", - swiftSettings: [ - .unsafeFlags(["-suppress-warnings"]) // Suppress warnings from testLocalVariableAssignment - ] - ), - .target( - name: "TypeSyntaxInspectorFixtures", - path: "Tests/Fixtures/TypeSyntaxInspectorFixtures" - ), - .target( - name: "DeclarationVisitorFixtures", - path: "Tests/Fixtures/DeclarationVisitorFixtures" - ), .testTarget( name: "PeripheryTests", dependencies: [ @@ -162,14 +125,6 @@ targets.append(contentsOf: [ .product(name: "XcodeProj", package: "XcodeProj") ] ), - .target( - name: "ObjcAccessibleRetentionFixtures", - path: "Tests/Fixtures/ObjcAccessibleRetentionFixtures" - ), - .target( - name: "ObjcAnnotatedRetentionFixtures", - path: "Tests/Fixtures/ObjcAnnotatedRetentionFixtures" - ), .testTarget( name: "XcodeTests", dependencies: [ diff --git a/Sources/Frontend/Commands/ScanBehavior.swift b/Sources/Frontend/Commands/ScanBehavior.swift index cefb2dfc84..0b1f7443ed 100644 --- a/Sources/Frontend/Commands/ScanBehavior.swift +++ b/Sources/Frontend/Commands/ScanBehavior.swift @@ -90,24 +90,6 @@ final class ScanBehavior { logger.info(output, canQuiet: false) logger.endInterval(interval) - if filteredResults.count > 0, - configuration.outputFormat.supportsAuxiliaryOutput { - logger.info( - colorize("\n* ", .boldGreen) + - colorize("Seeing false positives?", .bold) + - - colorize("\n - ", .boldGreen) + - "Periphery only analyzes files that are members of the targets you specify." + - "\n References to declarations identified as unused may reside in files that are members of other targets, e.g test targets." + - - colorize("\n - ", .boldGreen) + - "Periphery is a very precise tool, false positives often turn out to be correct after further investigation." + - - colorize("\n - ", .boldGreen) + - "If it really is a false positive, please report it - https://github.com/peripheryapp/periphery/issues." - ) - } - updateChecker.notifyIfAvailable() if !filteredResults.isEmpty && configuration.strict { diff --git a/Sources/Frontend/Commands/ScanCommand.swift b/Sources/Frontend/Commands/ScanCommand.swift index 7a03ebba53..c8c289628c 100644 --- a/Sources/Frontend/Commands/ScanCommand.swift +++ b/Sources/Frontend/Commands/ScanCommand.swift @@ -18,21 +18,15 @@ struct ScanCommand: FrontendCommand { @Option(help: "Path to configuration file. By default Periphery will look for .periphery.yml in the current directory") var config: String? - @Option(help: "Path to your project's .xcworkspace. Xcode projects only") - var workspace: String? - - @Option(help: "Path to your project's .xcodeproj - supply this option if your project doesn't have an .xcworkspace. Xcode projects only") + @Option(help: "Path to your project's .xcodeproj or .xcworkspace") var project: String? @Option(parsing: .upToNextOption, help: "File target mapping configuration file paths. For use with third-party build systems") var fileTargetsPath: [FilePath] = defaultConfiguration.$fileTargetsPath.defaultValue - @Option(parsing: .upToNextOption, help: "Schemes that must be built in order to produce the targets passed to the --targets option. Xcode projects only") + @Option(parsing: .upToNextOption, help: "Schemes to build. All targets built by these schemes will be scanned") var schemes: [String] = defaultConfiguration.$schemes.defaultValue - @Option(parsing: .upToNextOption, help: "Target names to scan. Required for Xcode projects. Optional for Swift Package Manager projects, default behavior is to scan all targets defined in Package.swift") - var targets: [String] = defaultConfiguration.$targets.defaultValue - @Option(help: "Output format (allowed: \(OutputFormat.allValueStrings.joined(separator: ", ")))") var format: OutputFormat = defaultConfiguration.$outputFormat.defaultValue @@ -117,9 +111,6 @@ struct ScanCommand: FrontendCommand { @Flag(help: "Only output results") var quiet: Bool = defaultConfiguration.$quiet.defaultValue - @Option(help: "JSON package manifest path (obtained using `swift package describe --type json` or manually)") - var jsonPackageManifestPath: String? - @Option(help: "Baseline file path used to filter results") var baseline: FilePath? @@ -137,11 +128,9 @@ struct ScanCommand: FrontendCommand { let configuration = Configuration.shared configuration.guidedSetup = setup - configuration.apply(\.$workspace, workspace) configuration.apply(\.$project, project) configuration.apply(\.$fileTargetsPath, fileTargetsPath) configuration.apply(\.$schemes, schemes) - configuration.apply(\.$targets, targets) configuration.apply(\.$indexExclude, indexExclude) configuration.apply(\.$reportExclude, reportExclude) configuration.apply(\.$reportInclude, reportInclude) @@ -171,10 +160,8 @@ struct ScanCommand: FrontendCommand { configuration.apply(\.$relativeResults, relativeResults) configuration.apply(\.$retainCodableProperties, retainCodableProperties) configuration.apply(\.$retainEncodableProperties, retainEncodableProperties) - configuration.apply(\.$jsonPackageManifestPath, jsonPackageManifestPath) configuration.apply(\.$baseline, baseline) configuration.apply(\.$writeBaseline, writeBaseline) - try scanBehavior.main { project in try Scan().perform(project: project) }.get() diff --git a/Sources/Frontend/CommonSetupGuide.swift b/Sources/Frontend/CommonSetupGuide.swift index b16bfcaf0a..850e8e7294 100644 --- a/Sources/Frontend/CommonSetupGuide.swift +++ b/Sources/Frontend/CommonSetupGuide.swift @@ -10,8 +10,8 @@ final class CommonSetupGuide: SetupGuideHelpers, SetupGuide { } func perform() throws { - print(colorize("\nAssume all 'public' declarations are in use?", .bold)) - print(colorize("?", .boldYellow) + " You should choose 'Yes' here if your public interfaces are not used by any selected build target, as may be the case for a framework/library project.") + print(colorize("Assume all 'public' declarations are in use?", .bold)) + print(colorize("?", .boldYellow) + " Choose 'Yes' if your project is a framework/library without a main application target.") configuration.retainPublic = selectBoolean() } diff --git a/Sources/Frontend/GuidedSetup.swift b/Sources/Frontend/GuidedSetup.swift index 674fe05a47..871d92a1f4 100644 --- a/Sources/Frontend/GuidedSetup.swift +++ b/Sources/Frontend/GuidedSetup.swift @@ -46,12 +46,15 @@ final class GuidedSetup: SetupGuideHelpers { let guides: [SetupGuide] = [projectGuide, commonGuide] try guides.forEach { try $0.perform() } let options = Array(guides.map { $0.commandLineOptions }.joined()) + var shouldSave = false - print(colorize("\nSave configuration to \(Configuration.defaultConfigurationFile)?", .bold)) - let shouldSave = selectBoolean() + if configuration.hasNonDefaultValues { + print(colorize("\nSave configuration to \(Configuration.defaultConfigurationFile)?", .bold)) + shouldSave = selectBoolean() - if shouldSave { - try configuration.save() + if shouldSave { + try configuration.save() + } } print(colorize("\n*", .boldGreen) + " Executing command:") diff --git a/Sources/Frontend/Project.swift b/Sources/Frontend/Project.swift index 4a4fd9ef63..98b46ccfe1 100644 --- a/Sources/Frontend/Project.swift +++ b/Sources/Frontend/Project.swift @@ -11,7 +11,7 @@ final class Project { static func identify() -> Self { let configuration = Configuration.shared - if configuration.workspace != nil || configuration.project != nil { + if configuration.project != nil { return self.init(kind: .xcode) } else if !configuration.fileTargetsPath.isEmpty { return self.init(kind: .generic) diff --git a/Sources/Frontend/SPMProjectSetupGuide.swift b/Sources/Frontend/SPMProjectSetupGuide.swift index e9d8b4b549..6d14f1afee 100644 --- a/Sources/Frontend/SPMProjectSetupGuide.swift +++ b/Sources/Frontend/SPMProjectSetupGuide.swift @@ -4,13 +4,6 @@ import PeripheryKit import Shared final class SPMProjectSetupGuide: SetupGuideHelpers, ProjectSetupGuide { - private let configuration: Configuration - - init(configuration: Configuration = .shared) { - self.configuration = configuration - super.init() - } - var projectKind: ProjectKind { .spm } @@ -19,37 +12,9 @@ final class SPMProjectSetupGuide: SetupGuideHelpers, ProjectSetupGuide { SPM.isSupported } - func perform() throws { - let package = try SPM.Package.load() - let selection = try selectTargets(in: package) - - if case let .some(targets) = selection { - configuration.targets = targets - } - } + func perform() {} var commandLineOptions: [String] { - var options: [String] = [] - - if !configuration.targets.isEmpty { - options.append("--targets " + configuration.targets.map { "\"\($0)\"" }.joined(separator: ",")) - } - - return options + [] } - - // MARK: - Private - - private func selectTargets(in package: SPM.Package) throws -> SetupSelection { - let targets = package.swiftTargets - - guard !targets.isEmpty else { - throw PeripheryError.guidedSetupError(message: "Failed to identify any targets in package \(package.name)") - } - - print(colorize("Select build targets to analyze:", .bold)) - let targetNames = targets.map { $0.name }.sorted() - return select(multiple: targetNames, allowAll: true) - } - } diff --git a/Sources/Frontend/Scan.swift b/Sources/Frontend/Scan.swift index 1d4da9985a..6d011b12b7 100644 --- a/Sources/Frontend/Scan.swift +++ b/Sources/Frontend/Scan.swift @@ -45,8 +45,11 @@ final class Scan { logger.info("\(asterisk) Indexing...") } + let indexLogger = logger.contextualized(with: "index") + let sourceFiles = try driver.collect(logger: indexLogger) + let graph = SourceGraph.shared - try driver.index(graph: graph) + try driver.index(sourceFiles: sourceFiles, graph: graph, logger: indexLogger) logger.endInterval(indexInterval) let analyzeInterval = logger.beginInterval("analyze") diff --git a/Sources/Indexer/IndexTarget.swift b/Sources/Indexer/IndexTarget.swift deleted file mode 100644 index bbee5404e1..0000000000 --- a/Sources/Indexer/IndexTarget.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Foundation - -public struct IndexTarget: Hashable { - public let name: String - - public init(name: String) { - self.name = name - } -} diff --git a/Sources/Indexer/Indexer.swift b/Sources/Indexer/Indexer.swift index 686ef3a2db..4ad94a20c3 100644 --- a/Sources/Indexer/Indexer.swift +++ b/Sources/Indexer/Indexer.swift @@ -1,6 +1,7 @@ import Foundation import SystemPackage import Shared +import SourceGraph import FilenameMatcher public class Indexer { @@ -16,4 +17,8 @@ public class Indexer { let included = files.filter { !configuration.indexExcludeMatchers.anyMatch(filename: $0.string) } return (included, files.subtracting(included)) } + + func isRetained(_ file: SourceFile) -> Bool { + configuration.retainFilesMatchers.anyMatch(filename: file.path.string) + } } diff --git a/Sources/Indexer/JobPool.swift b/Sources/Indexer/JobPool.swift index acc213779b..64ee994418 100644 --- a/Sources/Indexer/JobPool.swift +++ b/Sources/Indexer/JobPool.swift @@ -1,10 +1,10 @@ import Foundation import Shared -struct JobPool { - let jobs: [T] +struct JobPool { + let jobs: [Job] - func forEach(_ block: @escaping (T) throws -> Void) throws { + func forEach(_ block: @escaping (Job) throws -> Void) throws { var error: Error? DispatchQueue.concurrentPerform(iterations: jobs.count) { idx in @@ -23,9 +23,9 @@ struct JobPool { } } - func flatMap(_ block: @escaping (T) throws -> [R]) throws -> [R] { + func flatMap(_ block: @escaping (Job) throws -> [Result]) throws -> [Result] { var error: Error? - var results: [R] = [] + var results: [Result] = [] let lock = UnfairLock() DispatchQueue.concurrentPerform(iterations: jobs.count) { idx in diff --git a/Sources/Indexer/SourceFileCollector.swift b/Sources/Indexer/SourceFileCollector.swift new file mode 100644 index 0000000000..d37d371268 --- /dev/null +++ b/Sources/Indexer/SourceFileCollector.swift @@ -0,0 +1,60 @@ +import Foundation +import SystemPackage +import SwiftIndexStore +import Shared +import SourceGraph + +public struct SourceFileCollector { + let indexStorePaths: [FilePath] + let logger: ContextualLogger + let configuration: Configuration + + public init(indexStorePaths: [FilePath], logger: ContextualLogger, configuration: Configuration = .shared) { + self.indexStorePaths = indexStorePaths + self.logger = logger + self.configuration = configuration + } + + public func collect() throws -> [SourceFile: [IndexUnit]] { + let currentFilePath = FilePath.current + + return try JobPool(jobs: indexStorePaths) + .flatMap { indexStorePath in + logger.debug("Reading \(indexStorePath)") + let indexStore = try IndexStore.open(store: URL(fileURLWithPath: indexStorePath.string), lib: .open()) + let units = indexStore.units(includeSystem: false) + + return try units.compactMap { unit -> (FilePath, IndexStore, IndexStoreUnit, String?)? in + guard let filePath = try indexStore.mainFilePath(for: unit), !filePath.isEmpty else { return nil } + + let file = FilePath.makeAbsolute(filePath, relativeTo: currentFilePath) + + if file.exists { + if !self.isExcluded(file) { + let module = try indexStore.moduleName(for: unit) + return (file, indexStore, unit, module) + } + } + + return nil + } + } + .reduce(into: [FilePath: [(IndexStore, IndexStoreUnit, String?)]](), { result, tuple in + let (file, indexStore, unit, module) = tuple + result[file, default: []].append((indexStore, unit, module)) + }) + .reduce(into: [SourceFile: [IndexUnit]](), { result, pair in + let (file, tuples) = pair + let modules = tuples.compactMapSet { $0.2 } + let sourceFile = SourceFile(path: file, modules: modules) + let units = tuples.map { IndexUnit(store:$0.0, unit: $0.1) } + result[sourceFile] = units + }) + } + + // MARK: - Private + + private func isExcluded(_ file: FilePath) -> Bool { + configuration.indexExcludeMatchers.anyMatch(filename: file.string) + } +} diff --git a/Sources/Indexer/SwiftIndexer.swift b/Sources/Indexer/SwiftIndexer.swift index 83c3429829..1fe8f2b9c5 100644 --- a/Sources/Indexer/SwiftIndexer.swift +++ b/Sources/Indexer/SwiftIndexer.swift @@ -5,77 +5,36 @@ import Shared import SourceGraph import SyntaxAnalysis +public struct IndexUnit { + let store: IndexStore + let unit: IndexStoreUnit +} + public final class SwiftIndexer: Indexer { - private let sourceFiles: [FilePath: Set] + private let sourceFiles: [SourceFile: [IndexUnit]] private let graph: SourceGraph private let logger: ContextualLogger private let configuration: Configuration - private let indexStorePaths: [FilePath] - private let currentFilePath = FilePath.current public required init( - sourceFiles: [FilePath: Set], + sourceFiles: [SourceFile: [IndexUnit]], graph: SourceGraph, - indexStorePaths: [FilePath], - logger: Logger = .init(), + logger: ContextualLogger, configuration: Configuration = .shared ) { self.sourceFiles = sourceFiles self.graph = graph - self.indexStorePaths = indexStorePaths - self.logger = logger.contextualized(with: "index:swift") + self.logger = logger.contextualized(with: "swift") self.configuration = configuration super.init(configuration: configuration) } public func perform() throws { - let allSourceFiles = Set(sourceFiles.keys) - let (includedFiles, excludedFiles) = filterIndexExcluded(from: allSourceFiles) - excludedFiles.forEach { self.logger.debug("Excluding \($0.string)") } - - let unitsByFile = try JobPool(jobs: indexStorePaths) - .flatMap { [logger, currentFilePath] indexStorePath in - logger.debug("Reading \(indexStorePath)") - let indexStore = try IndexStore.open(store: URL(fileURLWithPath: indexStorePath.string), lib: .open()) - let units = indexStore.units(includeSystem: false) - - return try units.compactMap { unit -> (FilePath, IndexStore, IndexStoreUnit)? in - guard let filePath = try indexStore.mainFilePath(for: unit) else { return nil } - - let file = FilePath.makeAbsolute(filePath, relativeTo: currentFilePath) - - if includedFiles.contains(file) { - return (file, indexStore, unit) - } - - return nil - } - } - .reduce(into: [FilePath: [(IndexStore, IndexStoreUnit)]](), { result, tuple in - let (file, indexStore, unit) = tuple - result[file, default: []].append((indexStore, unit)) - }) - - let indexedFiles = Set(unitsByFile.keys) - let unindexedFiles = allSourceFiles.subtracting(excludedFiles).subtracting(indexedFiles) - - if !unindexedFiles.isEmpty { - unindexedFiles.forEach { logger.debug("Source file not indexed: \($0)") } - let targets = unindexedFiles.flatMapSet { sourceFiles[$0] ?? [] }.mapSet { $0.name } - throw PeripheryError.unindexedTargetsError(targets: targets, indexStorePaths: indexStorePaths) - } - - var retainedFiles: Set = [] - - if !configuration.retainFilesMatchers.isEmpty { - retainedFiles = allSourceFiles.filter { configuration.retainFilesMatchers.anyMatch(filename: $0.string) } - } - - let jobs = unitsByFile.map { (file, units) -> Job in + let jobs = sourceFiles.map { (file, units) -> Job in return Job( - file: file, + sourceFile: file, units: units, - retainAllDeclarations: retainedFiles.contains(file), + retainAllDeclarations: isRetained(file), graph: graph, logger: logger, configuration: configuration @@ -90,7 +49,7 @@ public final class SwiftIndexer: Indexer { try job.phaseOne() } - phaseOneLogger.debug("\(job.file) (\(elapsed)s)") + phaseOneLogger.debug("\(job.sourceFile.path.string) (\(elapsed)s)") } logger.endInterval(phaseOneInterval) @@ -103,7 +62,7 @@ public final class SwiftIndexer: Indexer { try job.phaseTwo() } - phaseTwoLogger.debug("\(job.file) (\(elapsed)s)") + phaseTwoLogger.debug("\(job.sourceFile.path.string) (\(elapsed)s)") } logger.endInterval(phaseTwoInterval) @@ -112,25 +71,24 @@ public final class SwiftIndexer: Indexer { // MARK: - Private private class Job { - let file: FilePath + let sourceFile: SourceFile - private let units: [(IndexStore, IndexStoreUnit)] + private let units: [IndexUnit] private let graph: SourceGraph private let logger: ContextualLogger private let configuration: Configuration - private var sourceFile: SourceFile? private var retainAllDeclarations: Bool required init( - file: FilePath, - units: [(IndexStore, IndexStoreUnit)], + sourceFile: SourceFile, + units: [IndexUnit], retainAllDeclarations: Bool, graph: SourceGraph, logger: ContextualLogger, configuration: Configuration ) { - self.file = file + self.sourceFile = sourceFile self.units = units self.retainAllDeclarations = retainAllDeclarations self.graph = graph @@ -178,27 +136,27 @@ public final class SwiftIndexer: Indexer { var rawDeclsByKey: [RawDeclaration.Key: [(RawDeclaration, [RawRelation])]] = [:] var references: Set = [] - for (indexStore, unit) in units { - try indexStore.forEachRecordDependencies(for: unit) { dependency in + for unit in units { + try unit.store.forEachRecordDependencies(for: unit.unit) { dependency in guard case let .record(record) = dependency else { return true } - try indexStore.forEachOccurrences(for: record, language: .swift) { occurrence in + try unit.store.forEachOccurrences(for: record, language: .swift) { occurrence in guard let usr = occurrence.symbol.usr, let location = try transformLocation(occurrence.location) else { return true } if !occurrence.roles.isDisjoint(with: [.definition, .declaration]) { - if let (decl, relations) = try parseRawDeclaration(occurrence, usr, location, indexStore) { + if let (decl, relations) = try parseRawDeclaration(occurrence, usr, location, unit.store) { rawDeclsByKey[decl.key, default: []].append((decl, relations)) } } if occurrence.roles.contains(.reference) { - references.formUnion(try parseReference(occurrence, usr, location, indexStore)) + references.formUnion(try parseReference(occurrence, usr, location, unit.store)) } if occurrence.roles.contains(.implicit) { - references.formUnion(try parseImplicit(occurrence, usr, location, indexStore)) + references.formUnion(try parseImplicit(occurrence, usr, location, unit.store)) } return true @@ -247,7 +205,11 @@ public final class SwiftIndexer: Indexer { /// Phase two associates latent references, and performs other actions that depend on the completed source graph. func phaseTwo() throws { - let sourceFile = try getSourceFile() + if !configuration.disableUnusedImportAnalysis { + graph.addIndexedSourceFile(sourceFile) + graph.addIndexedModules(sourceFile.modules) + } + let multiplexingSyntaxVisitor = try MultiplexingSyntaxVisitor(file: sourceFile) let declarationSyntaxVisitor = multiplexingSyntaxVisitor.add(DeclarationSyntaxVisitor.self) let importSyntaxVisitor = multiplexingSyntaxVisitor.add(ImportSyntaxVisitor.self) @@ -277,27 +239,6 @@ public final class SwiftIndexer: Indexer { private var danglingReferences: [Reference] = [] private var varParameterUsrs: Set = [] - private func getSourceFile() throws -> SourceFile { - if let sourceFile { return sourceFile } - - let modules = try units.reduce(into: Set()) { (set, tuple) in - let (indexStore, unit) = tuple - if let name = try indexStore.moduleName(for: unit) { - set.insert(name) - } - } - - let sourceFile = SourceFile(path: file, modules: modules) - self.sourceFile = sourceFile - - if !configuration.disableUnusedImportAnalysis { - graph.addIndexedSourceFile(sourceFile) - graph.addIndexedModules(modules) - } - - return sourceFile - } - private func establishDeclarationHierarchy() { graph.withLock { for (parent, decls) in childDeclsByParentUsr { @@ -694,7 +635,7 @@ public final class SwiftIndexer: Indexer { } private func transformLocation(_ input: IndexStoreOccurrence.Location) throws -> Location? { - return Location(file: try getSourceFile(), line: Int(input.line), column: Int(input.column)) + return Location(file: sourceFile, line: Int(input.line), column: Int(input.column)) } private func transformDeclarationKind(_ kind: IndexStoreSymbol.Kind, _ subKind: IndexStoreSymbol.SubKind) -> Declaration.Kind? { diff --git a/Sources/PeripheryKit/Generic/GenericProjectDriver.swift b/Sources/PeripheryKit/Generic/GenericProjectDriver.swift index f31488fd94..fa98387188 100644 --- a/Sources/PeripheryKit/Generic/GenericProjectDriver.swift +++ b/Sources/PeripheryKit/Generic/GenericProjectDriver.swift @@ -3,10 +3,10 @@ import SystemPackage import Indexer import Shared import SourceGraph +import SwiftIndexStore public final class GenericProjectDriver { private enum FileKind: String { - case swift case plist } @@ -15,8 +15,8 @@ public final class GenericProjectDriver { let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase - let sourceFiles = try configuration.fileTargetsPath - .reduce(into: [FileKind: [FilePath: Set]]()) { result, mapPath in + let projectFiles = try configuration.fileTargetsPath + .reduce(into: [FileKind: Set]()) { result, mapPath in guard mapPath.exists else { throw PeripheryError.pathDoesNotExist(path: mapPath.string) } @@ -36,19 +36,18 @@ public final class GenericProjectDriver { throw PeripheryError.unsupportedFileKind(path: path) } - let indexTargets = value.mapSet { IndexTarget(name: $0) } - result[fileKind, default: [:]][path, default: []].formUnion(indexTargets) + result[fileKind, default: []].insert(path) } } - return self.init(sourceFiles: sourceFiles, configuration: configuration) + return self.init(projectFiles: projectFiles, configuration: configuration) } - private let sourceFiles: [FileKind: [FilePath: Set]] + private let projectFiles: [FileKind: Set] private let configuration: Configuration - private init(sourceFiles: [FileKind: [FilePath: Set]], configuration: Configuration) { - self.sourceFiles = sourceFiles + private init(projectFiles: [FileKind: Set], configuration: Configuration) { + self.projectFiles = projectFiles self.configuration = configuration } } @@ -56,13 +55,26 @@ public final class GenericProjectDriver { extension GenericProjectDriver: ProjectDriver { public func build() throws {} - public func index(graph: SourceGraph) throws { - if let swiftFiles = sourceFiles[.swift] { - try SwiftIndexer(sourceFiles: swiftFiles, graph: graph, indexStorePaths: configuration.indexStorePath).perform() - } + public func collect(logger: ContextualLogger) throws -> [SourceFile : [IndexUnit]] { + try SourceFileCollector( + indexStorePaths: configuration.indexStorePath, + logger: logger + ).collect() + } + + public func index( + sourceFiles: [SourceFile: [IndexUnit]], + graph: SourceGraph, + logger: ContextualLogger + ) throws { + try SwiftIndexer( + sourceFiles: sourceFiles, + graph: graph, + logger: logger + ).perform() - if let plistFiles = sourceFiles[.plist] { - try InfoPlistIndexer(infoPlistFiles: Set(plistFiles.keys), graph: graph).perform() + if let plistFiles = projectFiles[.plist] { + try InfoPlistIndexer(infoPlistFiles: plistFiles, graph: graph).perform() } graph.indexingComplete() diff --git a/Sources/PeripheryKit/ProjectDriver.swift b/Sources/PeripheryKit/ProjectDriver.swift index 024b539d13..4d0f4a16b3 100644 --- a/Sources/PeripheryKit/ProjectDriver.swift +++ b/Sources/PeripheryKit/ProjectDriver.swift @@ -1,9 +1,16 @@ import Foundation import SourceGraph +import SwiftIndexStore +import Shared public protocol ProjectDriver { static func build() throws -> Self func build() throws - func index(graph: SourceGraph) throws + func collect(logger: ContextualLogger) throws -> [SourceFile: [IndexUnit]] + func index( + sourceFiles: [SourceFile: [IndexUnit]], + graph: SourceGraph, + logger: ContextualLogger + ) throws } diff --git a/Sources/PeripheryKit/SPM/SPM.swift b/Sources/PeripheryKit/SPM/SPM.swift index cdd447d193..e014239988 100644 --- a/Sources/PeripheryKit/SPM/SPM.swift +++ b/Sources/PeripheryKit/SPM/SPM.swift @@ -6,93 +6,24 @@ public struct SPM { static let packageFile = "Package.swift" public static var isSupported: Bool { - FilePath.current.appending(packageFile).exists + Package().exists } - public struct Package: Decodable { - public static func load(jsonPackageManifestPath: String? = nil) throws -> Self { - Logger().contextualized(with: "spm:package").debug("Loading \(FilePath.current)") + public struct Package { + let path: FilePath = .current - let jsonData: Data - - if let jsonPackageManifestPath { - jsonData = try Data(contentsOf: URL(fileURLWithPath: jsonPackageManifestPath)) - } else { - let jsonString = try Shell.shared.exec(["swift", "package", "describe", "--type", "json"], stderr: false) - - guard let data = jsonString.data(using: .utf8) else { - throw PeripheryError.packageError(message: "Failed to read swift package description.") - } - - jsonData = data - } - - let decoder = JSONDecoder() - decoder.keyDecodingStrategy = .convertFromSnakeCase - return try decoder.decode(Package.self, from: jsonData) - } - - public let name: String - public let path: String - public let targets: [Target] - - public var swiftTargets: [Target] { - targets.filter(\.isSwiftTarget) + var exists: Bool { + path.appending(packageFile).exists } func clean() throws { try Shell.shared.exec(["swift", "package", "clean"]) } - } - - public struct Target: Decodable { - public let name: String - - let sources: [String] - let path: String - let moduleType: String - let type: String - - public var sourcePaths: [FilePath] { - let root = FilePath(path) - return sources.map { root.appending($0) } - } func build(additionalArguments: [String]) throws { - let args: [String] = ["swift", "build", "--target", name] + additionalArguments - try Shell.shared.exec(args) - } - - var isSwiftTarget: Bool { - moduleType == "SwiftTarget" - } - - public var isTestTarget: Bool { - type == "test" + // TODO: Configure test building? + try Shell.shared.exec(["swift", "build", "--build-tests"] + additionalArguments) } } } -extension SPM.Package: Hashable { - public func hash(into hasher: inout Hasher) { - hasher.combine(path) - } -} - -extension SPM.Package: Equatable { - public static func == (lhs: SPM.Package, rhs: SPM.Package) -> Bool { - lhs.path == rhs.path - } -} - -extension SPM.Target: Hashable { - public func hash(into hasher: inout Hasher) { - hasher.combine(name) - } -} - -extension SPM.Target: Equatable { - public static func == (lhs: SPM.Target, rhs: SPM.Target) -> Bool { - lhs.name == rhs.name - } -} diff --git a/Sources/PeripheryKit/SPM/SPMProjectDriver.swift b/Sources/PeripheryKit/SPM/SPMProjectDriver.swift index 85b3b72066..ece1828282 100644 --- a/Sources/PeripheryKit/SPM/SPMProjectDriver.swift +++ b/Sources/PeripheryKit/SPM/SPMProjectDriver.swift @@ -1,4 +1,5 @@ import Foundation +import SwiftIndexStore import SystemPackage import Shared import SourceGraph @@ -7,35 +8,21 @@ import Indexer public final class SPMProjectDriver { public static func build() throws -> Self { let configuration = Configuration.shared - let package = try SPM.Package.load(jsonPackageManifestPath: configuration.jsonPackageManifestPath) - let targets: [SPM.Target] if !configuration.schemes.isEmpty { throw PeripheryError.usageError("The --schemes option has no effect with Swift Package Manager projects.") } - if configuration.targets.isEmpty { - targets = package.swiftTargets - } else { - targets = package.swiftTargets.filter { configuration.targets.contains($0.name) } - let invalidTargetNames = Set(configuration.targets).subtracting(targets.map { $0.name }) - - if !invalidTargetNames.isEmpty { - throw PeripheryError.invalidTargets(names: invalidTargetNames.sorted(), project: SPM.packageFile) - } - } - - return self.init(package: package, targets: targets, configuration: configuration, logger: .init()) + let pkg = SPM.Package() + return self.init(pkg: pkg, configuration: configuration, logger: .init()) } - private let package: SPM.Package - let targets: [SPM.Target] + private let pkg: SPM.Package private let configuration: Configuration private let logger: Logger - init(package: SPM.Package, targets: [SPM.Target], configuration: Configuration, logger: Logger = .init()) { - self.package = package - self.targets = targets + init(pkg: SPM.Package, configuration: Configuration, logger: Logger = .init()) { + self.pkg = pkg self.configuration = configuration self.logger = logger } @@ -45,7 +32,7 @@ extension SPMProjectDriver: ProjectDriver { public func build() throws { if !configuration.skipBuild { if configuration.cleanBuild { - try package.clean() + try pkg.clean() } if configuration.outputFormat.supportsAuxiliaryOutput { @@ -53,37 +40,36 @@ extension SPMProjectDriver: ProjectDriver { logger.info("\(asterisk) Building...") } - try targets.forEach { - try $0.build(additionalArguments: configuration.buildArguments) - } + try pkg.build(additionalArguments: configuration.buildArguments) } } - public func index(graph: SourceGraph) throws { - let sourceFiles = targets.reduce(into: [FilePath: Set]()) { result, target in - let targetPath = absolutePath(for: target) - target.sources.forEach { - let indexTarget = IndexTarget(name: target.name) - result[targetPath.appending($0), default: []].insert(indexTarget) - } - } - + public func collect(logger: ContextualLogger) throws -> [SourceFile : [IndexUnit]] { let storePaths: [FilePath] if !configuration.indexStorePath.isEmpty { storePaths = configuration.indexStorePath } else { - storePaths = [FilePath(package.path).appending(".build/debug/index/store")] + storePaths = [pkg.path.appending(".build/debug/index/store")] } - try SwiftIndexer(sourceFiles: sourceFiles, graph: graph, indexStorePaths: storePaths).perform() - - graph.indexingComplete() + return try SourceFileCollector( + indexStorePaths: storePaths, + logger: logger + ).collect() } - // MARK: - Private + public func index( + sourceFiles: [SourceFile: [IndexUnit]], + graph: SourceGraph, + logger: ContextualLogger + ) throws { + try SwiftIndexer( + sourceFiles: sourceFiles, + graph: graph, + logger: logger + ).perform() - private func absolutePath(for target: SPM.Target) -> FilePath { - FilePath(package.path).appending(target.path) + graph.indexingComplete() } } diff --git a/Sources/Shared/Configuration.swift b/Sources/Shared/Configuration.swift index a4803d0c25..e63986355d 100644 --- a/Sources/Shared/Configuration.swift +++ b/Sources/Shared/Configuration.swift @@ -11,25 +11,19 @@ public final class Configuration { self.logger = logger } - @Setting(key: "workspace", defaultValue: nil) - public var workspace: String? - @Setting(key: "project", defaultValue: nil) public var project: String? - @Setting(key: "file_targets_path", defaultValue: [], valueConverter: filePathConverter) + @Setting(key: "file_targets_path", defaultValue: [], setter: filePathSetter) public var fileTargetsPath: [FilePath] - @Setting(key: "format", defaultValue: .default, valueConverter: { OutputFormat(anyValue: $0) }) + @Setting(key: "format", defaultValue: .default, setter: { OutputFormat(anyValue: $0) }) public var outputFormat: OutputFormat @Setting(key: "schemes", defaultValue: []) public var schemes: [String] - @Setting(key: "targets", defaultValue: []) - public var targets: [String] - - @Setting(key: "index_exclude", defaultValue: []) + @Setting(key: "index_exclude", defaultValue: ["**/.build/**/*"], requireDefaultValues: true) public var indexExclude: [String] @Setting(key: "report_exclude", defaultValue: []) @@ -44,7 +38,7 @@ public final class Configuration { @Setting(key: "xcode_list_arguments", defaultValue: []) public var xcodeListArguments: [String] - @Setting(key: "retain_assign_only_property_types", defaultValue: [], valueSanitizer: PropertyTypeSanitizer.sanitize) + @Setting(key: "retain_assign_only_property_types", defaultValue: [], setter: PropertyTypeSanitizer.sanitize) public var retainAssignOnlyPropertyTypes: [String] @Setting(key: "external_encodable_protocols", defaultValue: []) @@ -101,7 +95,7 @@ public final class Configuration { @Setting(key: "strict", defaultValue: false) public var strict: Bool - @Setting(key: "index_store_path", defaultValue: [], valueConverter: filePathConverter) + @Setting(key: "index_store_path", defaultValue: [], setter: filePathSetter) public var indexStorePath: [FilePath] @Setting(key: "skip_build", defaultValue: false) @@ -116,9 +110,6 @@ public final class Configuration { @Setting(key: "relative_results", defaultValue: false) public var relativeResults: Bool - @Setting(key: "json_package_manifest_path", defaultValue: nil) - public var jsonPackageManifestPath: String? - @Setting(key: "baseline", defaultValue: nil) public var baseline: FilePath? @@ -131,159 +122,17 @@ public final class Configuration { // Dependencies. private var logger: BaseLogger // Must use BaseLogger as Logger depends upon Configuration. + public var hasNonDefaultValues: Bool { + settings.contains(where: \.hasNonDefaultValue) + } + public func asYaml() throws -> String { var config: [String: Any?] = [:] - if $workspace.hasNonDefaultValue { - config[$workspace.key] = workspace - } - - if $project.hasNonDefaultValue { - config[$project.key] = project - } - - if $fileTargetsPath.hasNonDefaultValue { - config[$fileTargetsPath.key] = fileTargetsPath.map { $0.string } - } - - if $schemes.hasNonDefaultValue { - config[$schemes.key] = schemes - } - - if $targets.hasNonDefaultValue { - config[$targets.key] = targets - } - - if $outputFormat.hasNonDefaultValue { - config[$outputFormat.key] = outputFormat.rawValue - } - - if $indexExclude.hasNonDefaultValue { - config[$indexExclude.key] = indexExclude - } - - if $reportExclude.hasNonDefaultValue { - config[$reportExclude.key] = reportExclude - } - - if $reportInclude.hasNonDefaultValue { - config[$reportInclude.key] = reportInclude - } - - if $retainObjcAccessible.hasNonDefaultValue { - config[$retainObjcAccessible.key] = retainObjcAccessible - } - - if $retainObjcAnnotated.hasNonDefaultValue { - config[$retainObjcAnnotated.key] = retainObjcAnnotated - } - - if $retainPublic.hasNonDefaultValue { - config[$retainPublic.key] = retainPublic - } - - if $retainFiles.hasNonDefaultValue { - config[$retainFiles.key] = retainFiles - } - - if $retainAssignOnlyProperties.hasNonDefaultValue { - config[$retainAssignOnlyProperties.key] = retainAssignOnlyProperties - } - - if $retainAssignOnlyPropertyTypes.hasNonDefaultValue { - config[$retainAssignOnlyPropertyTypes.key] = retainAssignOnlyPropertyTypes - } - - if $externalEncodableProtocols.hasNonDefaultValue { - config[$externalEncodableProtocols.key] = externalEncodableProtocols - } - - if $externalCodableProtocols.hasNonDefaultValue { - config[$externalCodableProtocols.key] = externalCodableProtocols - } - - if $externalTestCaseClasses.hasNonDefaultValue { - config[$externalTestCaseClasses.key] = externalTestCaseClasses - } - - if $retainUnusedProtocolFuncParams.hasNonDefaultValue { - config[$retainUnusedProtocolFuncParams.key] = retainUnusedProtocolFuncParams - } - - if $retainSwiftUIPreviews.hasNonDefaultValue { - config[$retainSwiftUIPreviews.key] = retainSwiftUIPreviews - } - - if $disableRedundantPublicAnalysis.hasNonDefaultValue { - config[$disableRedundantPublicAnalysis.key] = disableRedundantPublicAnalysis - } - - if $disableUnusedImportAnalysis.hasNonDefaultValue { - config[$disableUnusedImportAnalysis.key] = disableUnusedImportAnalysis - } - - if $verbose.hasNonDefaultValue { - config[$verbose.key] = verbose - } - - if $quiet.hasNonDefaultValue { - config[$quiet.key] = quiet - } - - if $disableUpdateCheck.hasNonDefaultValue { - config[$disableUpdateCheck.key] = disableUpdateCheck - } - - if $strict.hasNonDefaultValue { - config[$strict.key] = strict - } - - if $indexStorePath.hasNonDefaultValue { - config[$indexStorePath.key] = indexStorePath.map { $0.string } - } - - if $skipBuild.hasNonDefaultValue { - config[$skipBuild.key] = skipBuild - } - - if $skipSchemesValidation.hasNonDefaultValue { - config[$skipSchemesValidation.key] = skipSchemesValidation - } - - if $cleanBuild.hasNonDefaultValue { - config[$cleanBuild.key] = cleanBuild - } - - if $buildArguments.hasNonDefaultValue { - config[$buildArguments.key] = buildArguments - } - - if $xcodeListArguments.hasNonDefaultValue { - config[$xcodeListArguments.key] = xcodeListArguments - } - - if $relativeResults.hasNonDefaultValue { - config[$relativeResults.key] = relativeResults - } - - if $retainCodableProperties.hasNonDefaultValue { - config[$retainCodableProperties.key] = retainCodableProperties - } - - if $retainEncodableProperties.hasNonDefaultValue { - config[$retainEncodableProperties.key] = retainEncodableProperties - } - - if $jsonPackageManifestPath.hasNonDefaultValue { - config[$jsonPackageManifestPath.key] = jsonPackageManifestPath - } - - if $baseline.hasNonDefaultValue { - config[$baseline.key] = baseline - } - - if $writeBaseline.hasNonDefaultValue { - config[$writeBaseline.key] = writeBaseline + for setting in settings { + if setting.hasNonDefaultValue { + config[setting.key] = setting.wrappedValue + } } return try Yams.dump(object: config) @@ -301,128 +150,16 @@ public final class Configuration { let yaml = try Yams.load(yaml: encodedYAML) as? [String: Any] ?? [:] for (key, value) in yaml { - switch key { - case $workspace.key: - $workspace.assign(value) - case $project.key: - $project.assign(value) - case $fileTargetsPath.key: - $fileTargetsPath.assign(value) - case $schemes.key: - $schemes.assign(value) - case $targets.key: - $targets.assign(value) - case $indexExclude.key: - $indexExclude.assign(value) - case $reportExclude.key: - $reportExclude.assign(value) - case $reportInclude.key: - $reportInclude.assign(value) - case $outputFormat.key: - $outputFormat.assign(value) - case $retainPublic.key: - $retainPublic.assign(value) - case $retainFiles.key: - $retainFiles.assign(value) - case $retainAssignOnlyProperties.key: - $retainAssignOnlyProperties.assign(value) - case $retainAssignOnlyPropertyTypes.key: - $retainAssignOnlyPropertyTypes.assign(value) - case $externalEncodableProtocols.key: - $externalEncodableProtocols.assign(value) - case $externalCodableProtocols.key: - $externalCodableProtocols.assign(value) - case $externalTestCaseClasses.key: - $externalTestCaseClasses.assign(value) - case $retainObjcAccessible.key: - $retainObjcAccessible.assign(value) - case $retainObjcAnnotated.key: - $retainObjcAnnotated.assign(value) - case $retainUnusedProtocolFuncParams.key: - $retainUnusedProtocolFuncParams.assign(value) - case $retainSwiftUIPreviews.key: - $retainSwiftUIPreviews.assign(value) - case $disableRedundantPublicAnalysis.key: - $disableRedundantPublicAnalysis.assign(value) - case $disableUnusedImportAnalysis.key: - $disableUnusedImportAnalysis.assign(value) - case $verbose.key: - $verbose.assign(value) - case $quiet.key: - $quiet.assign(value) - case $disableUpdateCheck.key: - $disableUpdateCheck.assign(value) - case $strict.key: - $strict.assign(value) - case $indexStorePath.key: - $indexStorePath.assign(value) - case $skipBuild.key: - $skipBuild.assign(value) - case $skipSchemesValidation.key: - $skipSchemesValidation.assign(value) - case $cleanBuild.key: - $cleanBuild.assign(value) - case $buildArguments.key: - $buildArguments.assign(value) - case $xcodeListArguments.key: - $xcodeListArguments.assign(value) - case $relativeResults.key: - $relativeResults.assign(value) - case $retainCodableProperties.key: - $retainCodableProperties.assign(value) - case $retainEncodableProperties.key: - $retainEncodableProperties.assign(value) - case $jsonPackageManifestPath.key: - $jsonPackageManifestPath.assign(value) - case $baseline.key: - $baseline.assign(value) - case $writeBaseline.key: - $writeBaseline.assign(value) - default: + if let setting = settings.first(where: { key == $0.key }) { + setting.assign(value) + } else { logger.warn("\(path.string): invalid key '\(key)'") } } } public func reset() { - $workspace.reset() - $project.reset() - $fileTargetsPath.reset() - $schemes.reset() - $targets.reset() - $indexExclude.reset() - $reportExclude.reset() - $reportInclude.reset() - $outputFormat.reset() - $retainPublic.reset() - $retainFiles.reset() - $retainAssignOnlyProperties.reset() - $retainAssignOnlyPropertyTypes.reset() - $retainObjcAccessible.reset() - $retainObjcAnnotated.reset() - $retainUnusedProtocolFuncParams.reset() - $retainSwiftUIPreviews.reset() - $disableRedundantPublicAnalysis.reset() - $disableUnusedImportAnalysis.reset() - $externalEncodableProtocols.reset() - $externalCodableProtocols.reset() - $externalTestCaseClasses.reset() - $verbose.reset() - $quiet.reset() - $disableUpdateCheck.reset() - $strict.reset() - $indexStorePath.reset() - $skipBuild.reset() - $skipSchemesValidation.reset() - $cleanBuild.reset() - $buildArguments.reset() - $xcodeListArguments.reset() - $relativeResults.reset() - $retainCodableProperties.reset() - $retainEncodableProperties.reset() - $jsonPackageManifestPath.reset() - $baseline.reset() - $writeBaseline.reset() + settings.forEach { $0.reset() } } // MARK: - Helpers @@ -472,6 +209,8 @@ public final class Configuration { // MARK: - Private + lazy var settings: [any AbstractSetting] = [$project, $fileTargetsPath, $schemes, $indexExclude, $reportExclude, $reportInclude, $outputFormat, $retainPublic, $retainFiles, $retainAssignOnlyProperties, $retainAssignOnlyPropertyTypes, $retainObjcAccessible, $retainObjcAnnotated, $retainUnusedProtocolFuncParams, $retainSwiftUIPreviews, $disableRedundantPublicAnalysis, $disableUnusedImportAnalysis, $externalEncodableProtocols, $externalCodableProtocols, $externalTestCaseClasses, $verbose, $quiet, $disableUpdateCheck, $strict, $indexStorePath, $skipBuild, $skipSchemesValidation, $cleanBuild, $buildArguments, $xcodeListArguments, $relativeResults, $retainCodableProperties, $retainEncodableProperties, $baseline, $writeBaseline] + private func buildFilenameMatchers(with patterns: [String]) -> [FilenameMatcher] { // TODO: respect filesystem case sensitivity. let pwd = FilePath.current.string @@ -491,50 +230,61 @@ public final class Configuration { } } -@propertyWrapper public final class Setting { - typealias ValueConverter = (Any) -> Value? - typealias ValueSanitizer = (Value) -> Value +protocol AbstractSetting { + associatedtype Value + + var key: String { get } + var hasNonDefaultValue: Bool { get } + var wrappedValue: Value { get } + + func reset() + func assign(_ value: Any) +} + +@propertyWrapper public final class Setting: AbstractSetting { + typealias Setter = (Any) -> Value? public let defaultValue: Value - fileprivate let key: String + let key: String - private let valueConverter: ValueConverter - private let valueSanitizer: ValueSanitizer + private let setter: Setter private var value: Value - fileprivate init(key: String, - defaultValue: Value, - valueConverter: @escaping ValueConverter = { $0 as? Value }, - valueSanitizer: @escaping ValueSanitizer = { $0 }) { + fileprivate init( + key: String, + defaultValue: Value, + setter: @escaping Setter = { $0 as? Value } + ) { self.key = key self.value = defaultValue self.defaultValue = defaultValue - self.valueConverter = valueConverter - self.valueSanitizer = valueSanitizer + self.setter = setter } public var wrappedValue: Value { get { value } - set { value = valueSanitizer(newValue) } + set { value = setter(newValue) ?? defaultValue } } public var projectedValue: Setting { self } - fileprivate var hasNonDefaultValue: Bool { + var hasNonDefaultValue: Bool { value != defaultValue } - fileprivate func assign(_ value: Any) { - wrappedValue = valueConverter(value) ?? defaultValue + public func assign(_ newValue: Any) { + value = setter(newValue) ?? defaultValue } - fileprivate func reset() { + func reset() { wrappedValue = defaultValue } } -private let filePathConverter: (Any) -> [FilePath]? = { value in - if let path = value as? String { +private let filePathSetter: (Any) -> [FilePath]? = { value in + if let value = value as? [FilePath] { + return value + } else if let path = value as? String { return [FilePath(path)] } else if let paths = value as? [String] { return paths.map { FilePath($0) } @@ -542,3 +292,34 @@ private let filePathConverter: (Any) -> [FilePath]? = { value in return nil } + +extension Setting where Value == [String] { + convenience init( + key: String, + defaultValue: Value, + requireDefaultValues: Bool + ) { + self.init( + key: key, + defaultValue: defaultValue, + setter: { value in + guard let typedValue = value as? [String] else { return nil } + return requireDefaultValues ? Array(Set(typedValue).union(defaultValue)) : typedValue + } + ) + } +} + +// MARK: - Yaml Encoding + +extension OutputFormat: ScalarRepresentable { + public func represented() -> Node.Scalar { + rawValue.represented() + } +} + +extension FilePath: ScalarRepresentable { + public func represented() -> Node.Scalar { + string.represented() + } +} diff --git a/Sources/Shared/OutputFormat.swift b/Sources/Shared/OutputFormat.swift index 77b5082161..30da1a8be5 100644 --- a/Sources/Shared/OutputFormat.swift +++ b/Sources/Shared/OutputFormat.swift @@ -11,7 +11,12 @@ public enum OutputFormat: String, CaseIterable { public static let `default` = OutputFormat.xcode init?(anyValue: Any) { - self.init(rawValue: anyValue as? String ?? "") + if let format = anyValue as? OutputFormat { + self = format + return + } + guard let stringValue = anyValue as? String else { return nil } + self.init(rawValue: stringValue) } @inlinable diff --git a/Sources/Shared/PeripheryError.swift b/Sources/Shared/PeripheryError.swift index 0428f888e9..44667414c4 100644 --- a/Sources/Shared/PeripheryError.swift +++ b/Sources/Shared/PeripheryError.swift @@ -7,7 +7,6 @@ public enum PeripheryError: Error, LocalizedError, CustomStringConvertible { case usageError(String) case underlyingError(Error) case invalidScheme(name: String, project: String) - case invalidTargets(names: [String], project: String) case sourceGraphIntegrityError(message: String) case guidedSetupError(message: String) case updateCheckError(message: String) @@ -17,7 +16,6 @@ public enum PeripheryError: Error, LocalizedError, CustomStringConvertible { case packageError(message: String) case swiftVersionParseError(fullVersion: String) case swiftVersionUnsupportedError(version: String, minimumVersion: String) - case unindexedTargetsError(targets: Set, indexStorePaths: [FilePath]) case jsonDeserializationError(error: Error, json: String) case indexStoreNotFound(derivedDataPath: String) case unsupportedFileKind(path: FilePath) @@ -36,11 +34,6 @@ public enum PeripheryError: Error, LocalizedError, CustomStringConvertible { return describe(error) case let .invalidScheme(name, project): return "Scheme '\(name)' does not exist in '\(project)'." - case let .invalidTargets(names, project): - let formattedNames = names.map { "'\($0)'" }.joined(separator: ", ") - let declinedTarget = names.count == 1 ? "Target" : "Targets" - let conjugatedDo = names.count == 1 ? "does" : "do" - return "\(declinedTarget) \(formattedNames) \(conjugatedDo) not exist in '\(project)'." case .sourceGraphIntegrityError(let message): return message case .guidedSetupError(let message): @@ -57,9 +50,6 @@ public enum PeripheryError: Error, LocalizedError, CustomStringConvertible { return message case .swiftVersionParseError(let fullVersion): return "Failed to parse Swift version from: \(fullVersion)" - case let .unindexedTargetsError(targets, indexStorePath): - let joinedTargets = targets.sorted().joined(separator: ", ") - return "The index store at '\(indexStorePath)' does not contain data for the following targets: \(joinedTargets). Either the index store is outdated, or you have requested to scan targets that have not been built. For Xcode projects, the chosen schemes must build all of the chosen targets." case let .swiftVersionUnsupportedError(version, minimumVersion): return "This version of Periphery only supports Swift >= \(minimumVersion), you're using \(version)." case let .jsonDeserializationError(error, json): diff --git a/Sources/Shared/PropertyTypeSanitizer.swift b/Sources/Shared/PropertyTypeSanitizer.swift index efcc3847b9..0a1d8eeffb 100644 --- a/Sources/Shared/PropertyTypeSanitizer.swift +++ b/Sources/Shared/PropertyTypeSanitizer.swift @@ -2,8 +2,9 @@ import Foundation public struct PropertyTypeSanitizer { @inlinable - public static func sanitize(_ types: [String]) -> [String] { - types.map { sanitize($0) } + public static func sanitize(_ value: Any) -> [String]? { + guard let typedValue = value as? [String] else { return nil } + return typedValue.map { sanitize($0) } } @inlinable diff --git a/Sources/SourceGraph/Elements/ProjectFileKind.swift b/Sources/SourceGraph/Elements/ProjectFileKind.swift index 56a1e59a7f..44fe528089 100644 --- a/Sources/SourceGraph/Elements/ProjectFileKind.swift +++ b/Sources/SourceGraph/Elements/ProjectFileKind.swift @@ -1,5 +1,4 @@ public enum ProjectFileKind { - case swift case interfaceBuilder case infoPlist case xcDataModel @@ -7,8 +6,6 @@ public enum ProjectFileKind { public var extensions: [String] { switch self { - case .swift: - return ["swift"] case .interfaceBuilder: return ["xib", "storyboard"] case .infoPlist: diff --git a/Sources/XcodeSupport/XcodeProject.swift b/Sources/XcodeSupport/XcodeProject.swift index c2b430cc1b..9d4b66c845 100644 --- a/Sources/XcodeSupport/XcodeProject.swift +++ b/Sources/XcodeSupport/XcodeProject.swift @@ -1,7 +1,6 @@ import Foundation import XcodeProj import SystemPackage -import PeripheryKit import Shared final class XcodeProject: XcodeProjectlike { @@ -34,7 +33,6 @@ final class XcodeProject: XcodeProjectlike { private let xcodebuild: Xcodebuild private(set) var targets: Set = [] - private(set) var packageTargets: [SPM.Package: Set] = [:] required init(path: FilePath, xcodebuild: Xcodebuild = .init(), logger: Logger = .init()) throws { logger.contextualized(with: "xcode:project").debug("Loading \(path)") @@ -66,30 +64,6 @@ final class XcodeProject: XcodeProjectlike { targets = xcodeProject.pbxproj.nativeTargets .mapSet { XcodeTarget(project: self, target: $0) } .union(subProjects.flatMapSet { $0.targets }) - - let packageTargetNames = targets.flatMapSet { $0.packageDependencyNames } - - if !packageTargetNames.isEmpty { - var packages: [SPM.Package] = [] - - for localPackage in xcodeProject.pbxproj.rootObject?.localPackages ?? [] { - let path = sourceRoot.appending(localPackage.relativePath) - if path.appending("Package.swift").exists { - try path.chdir { - let package = try SPM.Package.load() - packages.append(package) - } - } - } - - packageTargets = packageTargetNames.reduce(into: .init(), { result, targetName in - for package in packages { - if let target = package.targets.first(where: { $0.name == targetName }) { - result[package, default: []].insert(target) - } - } - }) - } } func schemes(additionalArguments: [String]) throws -> Set { diff --git a/Sources/XcodeSupport/XcodeProjectDriver.swift b/Sources/XcodeSupport/XcodeProjectDriver.swift index cdf16e161f..3bfd711196 100644 --- a/Sources/XcodeSupport/XcodeProjectDriver.swift +++ b/Sources/XcodeSupport/XcodeProjectDriver.swift @@ -2,56 +2,23 @@ import Foundation import SystemPackage import PeripheryKit import Shared -import SourceGraph -import Indexer +import SourceGrap public final class XcodeProjectDriver { public static func build() throws -> Self { let configuration = Configuration.shared try validateConfiguration(configuration: configuration) - let project: XcodeProjectlike - - if let workspacePath = configuration.workspace { - project = try XcodeWorkspace(path: .makeAbsolute(workspacePath)) - } else if let projectPath = configuration.project { - project = try XcodeProject(path: .makeAbsolute(projectPath)) - } else { - throw PeripheryError.usageError("Expected --workspace or --project option.") + guard let projectPath = configuration.project else { + throw PeripheryError.usageError("Expected --project option.") } - // Ensure targets are part of the project - var invalidTargetNames: [String] = [] - - var targets: Set = [] - var packageTargets: [SPM.Package: Set] = [:] - - for targetName in configuration.targets { - if let target = project.targets.first(where: { $0.name == targetName }) { - targets.insert(target) - } else { - let parts = targetName.split(separator: ".", maxSplits: 1) - - guard let packageName = parts.first, - let packageTargetName = parts.last, - let package = project.packageTargets.keys.first(where: { $0.name == packageName }) - else { - invalidTargetNames.append(targetName) - continue - } - - if let target = project.packageTargets[package]?.first(where: { $0.name == packageTargetName }) { - packageTargets[package, default: []].insert(target) - } else if let subTarget = package.targets.first(where: { $0.name == packageTargetName }) { - packageTargets[package, default: []].insert(subTarget) - } else { - invalidTargetNames.append(targetName) - } - } - } + let project: XcodeProjectlike - if !invalidTargetNames.isEmpty { - throw PeripheryError.invalidTargets(names: invalidTargetNames.sorted(), project: project.path.lastComponent?.string ?? "") + if projectPath.hasSuffix("xcworkspace") { + project = try XcodeWorkspace(path: .makeAbsolute(projectPath)) + } else { + project = try XcodeProject(path: .makeAbsolute(projectPath)) } let schemes: Set @@ -72,9 +39,7 @@ public final class XcodeProjectDriver { return self.init( project: project, - schemes: schemes, - targets: targets, - packageTargets: packageTargets + schemes: schemes ) } @@ -83,47 +48,32 @@ public final class XcodeProjectDriver { private let xcodebuild: Xcodebuild private let project: XcodeProjectlike private let schemes: Set - private let targets: Set - private let packageTargets: [SPM.Package: Set] init( logger: Logger = .init(), configuration: Configuration = .shared, xcodebuild: Xcodebuild = .init(), project: XcodeProjectlike, - schemes: Set, - targets: Set, - packageTargets: [SPM.Package: Set] + schemes: Set ) { self.logger = logger self.configuration = configuration self.xcodebuild = xcodebuild self.project = project self.schemes = schemes - self.targets = targets - self.packageTargets = packageTargets } // MARK: - Private private static func validateConfiguration(configuration: Configuration) throws { - guard configuration.workspace != nil || configuration.project != nil else { - let message = "You must supply either the --workspace or --project option. If your project uses an .xcworkspace to integrate multiple projects, then supply the --workspace option. Otherwise, supply the --project option." - throw PeripheryError.usageError(message) - } - - if configuration.workspace != nil && configuration.project != nil { - let message = "You must supply either the --workspace or --project option, not both. If your project uses an .xcworkspace to integrate multiple projects, then supply the --workspace option. Otherwise, supply the --project option." + guard configuration.project != nil else { + let message = "You must supply the --project option." throw PeripheryError.usageError(message) } guard !configuration.schemes.isEmpty else { throw PeripheryError.usageError("The '--schemes' option is required.") } - - guard !configuration.targets.isEmpty else { - throw PeripheryError.usageError("The '--targets' option is required.") - } } } @@ -141,18 +91,15 @@ extension XcodeProjectDriver: ProjectDriver { logger.info("\(asterisk) Building \(scheme)...") } - let containsXcodeTestTargets = targets.contains(where: \.isTestTarget) - let containsPackageTestTargets = packageTargets.values.contains { $0.contains(where: \.isTestTarget) } - let buildForTesting = containsXcodeTestTargets || containsPackageTestTargets try xcodebuild.build(project: project, scheme: scheme, allSchemes: Array(schemes), additionalArguments: configuration.buildArguments, - buildForTesting: buildForTesting) + buildForTesting: true) // TODO: auto detect? configurable? } } - public func index(graph: SourceGraph) throws { + public func collect(logger: ContextualLogger) throws -> [SourceFile : [IndexUnit]] { let storePaths: [FilePath] if !configuration.indexStorePath.isEmpty { @@ -161,29 +108,26 @@ extension XcodeProjectDriver: ProjectDriver { storePaths = [try xcodebuild.indexStorePath(project: project, schemes: Array(schemes))] } - try targets.forEach { try $0.identifyFiles() } - - var sourceFiles: [FilePath: Set] = [:] - - for target in targets { - target.files(kind: .swift).forEach { - let indexTarget = IndexTarget(name: target.name) - sourceFiles[$0, default: []].insert(indexTarget) - } - } - - for (package, targets) in packageTargets { - let packageRoot = FilePath(package.path) - - for target in targets { - target.sourcePaths.forEach { - let absolutePath = packageRoot.pushing($0) - let indexTarget = IndexTarget(name: target.name) - sourceFiles[absolutePath, default: []].insert(indexTarget) } - } - } + return try SourceFileCollector( + indexStorePaths: storePaths, + logger: logger + ).collect() + } - try SwiftIndexer(sourceFiles: sourceFiles, graph: graph, indexStorePaths: storePaths).perform() + public func index( + sourceFiles: [SourceFile: [IndexUnit]], + graph: SourceGraph, + logger: ContextualLogger + ) throws { + try SwiftIndexer( + sourceFiles: sourceFiles, + graph: graph, + logger: logger + ).perform() + + let modules = sourceFiles.keys.flatMapSet { $0.modules } + let targets = project.targets.filter { modules.contains($0.name) } + try targets.forEach { try $0.identifyFiles() } let xibFiles = targets.flatMapSet { $0.files(kind: .interfaceBuilder) } try XibIndexer(xibFiles: xibFiles, graph: graph).perform() diff --git a/Sources/XcodeSupport/XcodeProjectSetupGuide.swift b/Sources/XcodeSupport/XcodeProjectSetupGuide.swift index 089ff3a1a3..53548d59c4 100644 --- a/Sources/XcodeSupport/XcodeProjectSetupGuide.swift +++ b/Sources/XcodeSupport/XcodeProjectSetupGuide.swift @@ -29,31 +29,18 @@ public final class XcodeProjectSetupGuide: SetupGuideHelpers, ProjectSetupGuide project = try XcodeProject(path: projectPath) } - if let project = project { - guard !project.targets.isEmpty else { - throw PeripheryError.guidedSetupError(message: "Failed to identify any targets in \(project.path.lastComponent?.string ?? "")") - } - - var targets = project.targets.map { $0.name } - targets += project.packageTargets.flatMap { (package, targets) in - package.targets.map { "\(package.name).\($0.name)" } - } - targets = targets.sorted() - - print(colorize("Select build targets to analyze:", .bold)) - configuration.targets = select(multiple: targets, allowAll: true).selectedValues - - let schemes = try filter( - project.schemes(additionalArguments: configuration.xcodeListArguments), - project - ).map { $0 }.sorted() - - print(colorize("\nSelect the schemes necessary to build your chosen targets:", .bold)) - configuration.schemes = select(multiple: schemes, allowAll: false).selectedValues - } else { + guard let project else { throw PeripheryError.guidedSetupError(message: "Failed to find .xcworkspace or .xcodeproj in current directory") } + let schemes = try filter( + project.schemes(additionalArguments: configuration.xcodeListArguments), + project + ).map { $0 }.sorted() + + print(colorize("\nSelect the schemes necessary to build your chosen targets:", .bold)) + configuration.schemes = select(multiple: schemes, allowAll: false).selectedValues + print(colorize("\nAssume Objective-C accessible declarations are in use?", .bold)) print(colorize("?", .boldYellow) + " Declarations exposed to the Objective-C runtime explicitly with @objc, or implicitly by inheriting NSObject will be assumed to be in use. Choose 'No' if your project is pure Swift.") configuration.retainObjcAccessible = selectBoolean() @@ -62,16 +49,11 @@ public final class XcodeProjectSetupGuide: SetupGuideHelpers, ProjectSetupGuide public var commandLineOptions: [String] { var options: [String] = [] - if let workspace = configuration.workspace { - options.append("--workspace \"\(workspace)\"") - } - if let project = configuration.project { options.append("--project \"\(project)\"") } options.append("--schemes " + configuration.schemes.map { "\"\($0)\"" }.joined(separator: ",")) - options.append("--targets " + configuration.targets.map { "\"\($0)\"" }.joined(separator: ",")) if configuration.retainObjcAccessible { options.append("--retain-objc-accessible") @@ -114,7 +96,7 @@ public final class XcodeProjectSetupGuide: SetupGuideHelpers, ProjectSetupGuide } if let workspacePath = workspacePath { - configuration.workspace = workspacePath.relativeTo(.current).string + configuration.project = workspacePath.relativeTo(.current).string return workspacePath } diff --git a/Sources/XcodeSupport/XcodeProjectlike.swift b/Sources/XcodeSupport/XcodeProjectlike.swift index 34fa31537e..247833c4b4 100644 --- a/Sources/XcodeSupport/XcodeProjectlike.swift +++ b/Sources/XcodeSupport/XcodeProjectlike.swift @@ -1,11 +1,9 @@ import Foundation import SystemPackage -import PeripheryKit protocol XcodeProjectlike: AnyObject { var path: FilePath { get } var targets: Set { get } - var packageTargets: [SPM.Package: Set] { get } var type: String { get } var name: String { get } var sourceRoot: FilePath { get } diff --git a/Sources/XcodeSupport/XcodeTarget.swift b/Sources/XcodeSupport/XcodeTarget.swift index ec4d049a2b..cc236ea069 100644 --- a/Sources/XcodeSupport/XcodeTarget.swift +++ b/Sources/XcodeSupport/XcodeTarget.swift @@ -26,7 +26,6 @@ final class XcodeTarget { let sourcesBuildPhases = project.xcodeProject.pbxproj.sourcesBuildPhases let resourcesBuildPhases = project.xcodeProject.pbxproj.resourcesBuildPhases - try identifyFiles(kind: .swift, in: sourcesBuildPhases) try identifyFiles(kind: .xcDataModel, in: sourcesBuildPhases) try identifyFiles(kind: .xcMappingModel, in: sourcesBuildPhases) try identifyFiles(kind: .interfaceBuilder, in: resourcesBuildPhases) @@ -37,10 +36,6 @@ final class XcodeTarget { files[kind, default: []] } - var packageDependencyNames: Set { - target.packageProductDependencies.mapSet { $0.productName } - } - // MARK: - Private private func identifyFiles(kind: ProjectFileKind, in buildPhases: [PBXBuildPhase]) throws { diff --git a/Sources/XcodeSupport/XcodeWorkspace.swift b/Sources/XcodeSupport/XcodeWorkspace.swift index 940e37a6af..255467be22 100644 --- a/Sources/XcodeSupport/XcodeWorkspace.swift +++ b/Sources/XcodeSupport/XcodeWorkspace.swift @@ -1,7 +1,6 @@ import Foundation import XcodeProj import SystemPackage -import PeripheryKit import Shared final class XcodeWorkspace: XcodeProjectlike { @@ -14,7 +13,6 @@ final class XcodeWorkspace: XcodeProjectlike { private let xcworkspace: XCWorkspace private(set) var targets: Set = [] - private(set) var packageTargets: [SPM.Package: Set] = [:] required init(path: FilePath, xcodebuild: Xcodebuild = .init(), configuration: Configuration = .shared, logger: Logger = .init()) throws { logger.contextualized(with: "xcode:workspace").debug("Loading \(path)") @@ -36,10 +34,6 @@ final class XcodeWorkspace: XcodeProjectlike { targets = projects.reduce(into: .init()) { result, project in result.formUnion(project.targets) } - - packageTargets = projects.reduce(into: .init()) { result, project in - result.merge(project.packageTargets) { $0.union($1) } - } } func schemes(additionalArguments: [String]) throws -> Set { diff --git a/Tests/AccessibilityTests/AccessibilityProject/Package.swift b/Tests/AccessibilityTests/AccessibilityProject/Package.swift index 699d5d4c07..3ec209552e 100644 --- a/Tests/AccessibilityTests/AccessibilityProject/Package.swift +++ b/Tests/AccessibilityTests/AccessibilityProject/Package.swift @@ -1,5 +1,4 @@ -// swift-tools-version:5.8 - +// swift-tools-version:5.9 import PackageDescription let package = Package( diff --git a/Tests/AccessibilityTests/RedundantPublicAccessibilityTest.swift b/Tests/AccessibilityTests/RedundantPublicAccessibilityTest.swift index 578f8e48d6..840d2451a2 100644 --- a/Tests/AccessibilityTests/RedundantPublicAccessibilityTest.swift +++ b/Tests/AccessibilityTests/RedundantPublicAccessibilityTest.swift @@ -6,8 +6,6 @@ class RedundantPublicAccessibilityTest: SourceGraphTestCase { override static func setUp() { super.setUp() - configuration.targets = ["MainTarget", "TargetA", "TestTarget"] - build(driver: SPMProjectDriver.self, projectPath: AccessibilityProjectPath) } diff --git a/Tests/Fixtures/Package.swift b/Tests/Fixtures/Package.swift new file mode 100644 index 0000000000..48b28fe78a --- /dev/null +++ b/Tests/Fixtures/Package.swift @@ -0,0 +1,53 @@ +// swift-tools-version:5.9 +import PackageDescription + +var targets: [PackageDescription.Target] = [ + .target( + name: "ExternalModuleFixtures" + ), + .target( + name: "CrossModuleRetentionFixtures", + dependencies: [ + .target(name: "CrossModuleRetentionSupportFixtures") + ] + ), + .target( + name: "CrossModuleRetentionSupportFixtures" + ), + .target( + name: "RetentionFixtures", + dependencies: [ + .target(name: "ExternalModuleFixtures") + ] + ), + .target( + name: "UnusedParameterFixtures", + swiftSettings: [ + .unsafeFlags(["-suppress-warnings"]) // Suppress warnings from testLocalVariableAssignment + ] + ), + .target( + name: "TypeSyntaxInspectorFixtures" + ), + .target( + name: "DeclarationVisitorFixtures" + ), +] + +#if os(macOS) +targets.append(contentsOf: [ + .target( + name: "ObjcAccessibleRetentionFixtures" + ), + .target( + name: "ObjcAnnotatedRetentionFixtures" + ) +]) +#endif + +let package = Package( + name: "Fixtures", + platforms: [.macOS(.v13)], + targets: targets, + swiftLanguageVersions: [.v5] +) diff --git a/Tests/Fixtures/CrossModuleRetentionFixtures/testCrossModuleInheritanceWithSameName.swift b/Tests/Fixtures/Sources/CrossModuleRetentionFixtures/testCrossModuleInheritanceWithSameName.swift similarity index 100% rename from Tests/Fixtures/CrossModuleRetentionFixtures/testCrossModuleInheritanceWithSameName.swift rename to Tests/Fixtures/Sources/CrossModuleRetentionFixtures/testCrossModuleInheritanceWithSameName.swift diff --git a/Tests/Fixtures/CrossModuleRetentionSupportFixtures/CrossModuleSuperclass.swift b/Tests/Fixtures/Sources/CrossModuleRetentionSupportFixtures/CrossModuleSuperclass.swift similarity index 100% rename from Tests/Fixtures/CrossModuleRetentionSupportFixtures/CrossModuleSuperclass.swift rename to Tests/Fixtures/Sources/CrossModuleRetentionSupportFixtures/CrossModuleSuperclass.swift diff --git a/Tests/Fixtures/DeclarationVisitorFixtures/FunctionFixture.swift b/Tests/Fixtures/Sources/DeclarationVisitorFixtures/FunctionFixture.swift similarity index 100% rename from Tests/Fixtures/DeclarationVisitorFixtures/FunctionFixture.swift rename to Tests/Fixtures/Sources/DeclarationVisitorFixtures/FunctionFixture.swift diff --git a/Tests/Fixtures/DeclarationVisitorFixtures/PropertyFixture.swift b/Tests/Fixtures/Sources/DeclarationVisitorFixtures/PropertyFixture.swift similarity index 100% rename from Tests/Fixtures/DeclarationVisitorFixtures/PropertyFixture.swift rename to Tests/Fixtures/Sources/DeclarationVisitorFixtures/PropertyFixture.swift diff --git a/Tests/Fixtures/ExternalModuleFixtures/ExternalAssociatedType.swift b/Tests/Fixtures/Sources/ExternalModuleFixtures/ExternalAssociatedType.swift similarity index 100% rename from Tests/Fixtures/ExternalModuleFixtures/ExternalAssociatedType.swift rename to Tests/Fixtures/Sources/ExternalModuleFixtures/ExternalAssociatedType.swift diff --git a/Tests/Fixtures/ExternalModuleFixtures/ExternalTestCase.swift b/Tests/Fixtures/Sources/ExternalModuleFixtures/ExternalTestCase.swift similarity index 100% rename from Tests/Fixtures/ExternalModuleFixtures/ExternalTestCase.swift rename to Tests/Fixtures/Sources/ExternalModuleFixtures/ExternalTestCase.swift diff --git a/Tests/Fixtures/ObjcAccessibleRetentionFixtures/testDoesNotRetainMembersOfObjcAnnotatedClass.swift b/Tests/Fixtures/Sources/ObjcAccessibleRetentionFixtures/testDoesNotRetainMembersOfObjcAnnotatedClass.swift similarity index 100% rename from Tests/Fixtures/ObjcAccessibleRetentionFixtures/testDoesNotRetainMembersOfObjcAnnotatedClass.swift rename to Tests/Fixtures/Sources/ObjcAccessibleRetentionFixtures/testDoesNotRetainMembersOfObjcAnnotatedClass.swift diff --git a/Tests/Fixtures/ObjcAccessibleRetentionFixtures/testDoesNotRetainObjcAnnotatedWithoutOption.swift b/Tests/Fixtures/Sources/ObjcAccessibleRetentionFixtures/testDoesNotRetainObjcAnnotatedWithoutOption.swift similarity index 100% rename from Tests/Fixtures/ObjcAccessibleRetentionFixtures/testDoesNotRetainObjcAnnotatedWithoutOption.swift rename to Tests/Fixtures/Sources/ObjcAccessibleRetentionFixtures/testDoesNotRetainObjcAnnotatedWithoutOption.swift diff --git a/Tests/Fixtures/ObjcAccessibleRetentionFixtures/testObjcMembersAnnotationRetainsMembers.swift b/Tests/Fixtures/Sources/ObjcAccessibleRetentionFixtures/testObjcMembersAnnotationRetainsMembers.swift similarity index 100% rename from Tests/Fixtures/ObjcAccessibleRetentionFixtures/testObjcMembersAnnotationRetainsMembers.swift rename to Tests/Fixtures/Sources/ObjcAccessibleRetentionFixtures/testObjcMembersAnnotationRetainsMembers.swift diff --git a/Tests/Fixtures/ObjcAccessibleRetentionFixtures/testRetainsImplicitlyObjcAccessibleClass.swift b/Tests/Fixtures/Sources/ObjcAccessibleRetentionFixtures/testRetainsImplicitlyObjcAccessibleClass.swift similarity index 100% rename from Tests/Fixtures/ObjcAccessibleRetentionFixtures/testRetainsImplicitlyObjcAccessibleClass.swift rename to Tests/Fixtures/Sources/ObjcAccessibleRetentionFixtures/testRetainsImplicitlyObjcAccessibleClass.swift diff --git a/Tests/Fixtures/ObjcAccessibleRetentionFixtures/testRetainsObjcAnnotatedClass.swift b/Tests/Fixtures/Sources/ObjcAccessibleRetentionFixtures/testRetainsObjcAnnotatedClass.swift similarity index 100% rename from Tests/Fixtures/ObjcAccessibleRetentionFixtures/testRetainsObjcAnnotatedClass.swift rename to Tests/Fixtures/Sources/ObjcAccessibleRetentionFixtures/testRetainsObjcAnnotatedClass.swift diff --git a/Tests/Fixtures/ObjcAccessibleRetentionFixtures/testRetainsObjcAnnotatedMembers.swift b/Tests/Fixtures/Sources/ObjcAccessibleRetentionFixtures/testRetainsObjcAnnotatedMembers.swift similarity index 100% rename from Tests/Fixtures/ObjcAccessibleRetentionFixtures/testRetainsObjcAnnotatedMembers.swift rename to Tests/Fixtures/Sources/ObjcAccessibleRetentionFixtures/testRetainsObjcAnnotatedMembers.swift diff --git a/Tests/Fixtures/ObjcAccessibleRetentionFixtures/testRetainsOptionalProtocolMethod.swift b/Tests/Fixtures/Sources/ObjcAccessibleRetentionFixtures/testRetainsOptionalProtocolMethod.swift similarity index 100% rename from Tests/Fixtures/ObjcAccessibleRetentionFixtures/testRetainsOptionalProtocolMethod.swift rename to Tests/Fixtures/Sources/ObjcAccessibleRetentionFixtures/testRetainsOptionalProtocolMethod.swift diff --git a/Tests/Fixtures/ObjcAccessibleRetentionFixtures/testRetainsOptionalProtocolMethodImplementedInSubclass.swift b/Tests/Fixtures/Sources/ObjcAccessibleRetentionFixtures/testRetainsOptionalProtocolMethodImplementedInSubclass.swift similarity index 100% rename from Tests/Fixtures/ObjcAccessibleRetentionFixtures/testRetainsOptionalProtocolMethodImplementedInSubclass.swift rename to Tests/Fixtures/Sources/ObjcAccessibleRetentionFixtures/testRetainsOptionalProtocolMethodImplementedInSubclass.swift diff --git a/Tests/Fixtures/ObjcAnnotatedRetentionFixtures/testRetainsAnnotatedExtensionDeclarations.swift b/Tests/Fixtures/Sources/ObjcAnnotatedRetentionFixtures/testRetainsAnnotatedExtensionDeclarations.swift similarity index 100% rename from Tests/Fixtures/ObjcAnnotatedRetentionFixtures/testRetainsAnnotatedExtensionDeclarations.swift rename to Tests/Fixtures/Sources/ObjcAnnotatedRetentionFixtures/testRetainsAnnotatedExtensionDeclarations.swift diff --git a/Tests/Fixtures/ObjcAnnotatedRetentionFixtures/testRetainsExtensionDeclarationsOnObjcMembersAnnotatedClass.swift b/Tests/Fixtures/Sources/ObjcAnnotatedRetentionFixtures/testRetainsExtensionDeclarationsOnObjcMembersAnnotatedClass.swift similarity index 100% rename from Tests/Fixtures/ObjcAnnotatedRetentionFixtures/testRetainsExtensionDeclarationsOnObjcMembersAnnotatedClass.swift rename to Tests/Fixtures/Sources/ObjcAnnotatedRetentionFixtures/testRetainsExtensionDeclarationsOnObjcMembersAnnotatedClass.swift diff --git a/Tests/Fixtures/ObjcAnnotatedRetentionFixtures/testRetainsObjcProtocolConformingDeclarations.swift b/Tests/Fixtures/Sources/ObjcAnnotatedRetentionFixtures/testRetainsObjcProtocolConformingDeclarations.swift similarity index 100% rename from Tests/Fixtures/ObjcAnnotatedRetentionFixtures/testRetainsObjcProtocolConformingDeclarations.swift rename to Tests/Fixtures/Sources/ObjcAnnotatedRetentionFixtures/testRetainsObjcProtocolConformingDeclarations.swift diff --git a/Tests/Fixtures/ObjcAnnotatedRetentionFixtures/testRetainsObjcProtocolMembers.swift b/Tests/Fixtures/Sources/ObjcAnnotatedRetentionFixtures/testRetainsObjcProtocolMembers.swift similarity index 100% rename from Tests/Fixtures/ObjcAnnotatedRetentionFixtures/testRetainsObjcProtocolMembers.swift rename to Tests/Fixtures/Sources/ObjcAnnotatedRetentionFixtures/testRetainsObjcProtocolMembers.swift diff --git a/Tests/Fixtures/RetentionFixtures/testAccessibility.swift b/Tests/Fixtures/Sources/RetentionFixtures/testAccessibility.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testAccessibility.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testAccessibility.swift diff --git a/Tests/Fixtures/RetentionFixtures/testAssignOnlyPropertyAnalysisDoesNotApplyToProtocolProperties.swift b/Tests/Fixtures/Sources/RetentionFixtures/testAssignOnlyPropertyAnalysisDoesNotApplyToProtocolProperties.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testAssignOnlyPropertyAnalysisDoesNotApplyToProtocolProperties.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testAssignOnlyPropertyAnalysisDoesNotApplyToProtocolProperties.swift diff --git a/Tests/Fixtures/RetentionFixtures/testCircularTypeInheritance.swift b/Tests/Fixtures/Sources/RetentionFixtures/testCircularTypeInheritance.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testCircularTypeInheritance.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testCircularTypeInheritance.swift diff --git a/Tests/Fixtures/RetentionFixtures/testClassRetainedByUnusedInstanceVariable.swift b/Tests/Fixtures/Sources/RetentionFixtures/testClassRetainedByUnusedInstanceVariable.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testClassRetainedByUnusedInstanceVariable.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testClassRetainedByUnusedInstanceVariable.swift diff --git a/Tests/Fixtures/RetentionFixtures/testCodingKeyEnum.swift b/Tests/Fixtures/Sources/RetentionFixtures/testCodingKeyEnum.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testCodingKeyEnum.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testCodingKeyEnum.swift diff --git a/Tests/Fixtures/RetentionFixtures/testConformanceToExternalProtocolIsRetained.swift b/Tests/Fixtures/Sources/RetentionFixtures/testConformanceToExternalProtocolIsRetained.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testConformanceToExternalProtocolIsRetained.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testConformanceToExternalProtocolIsRetained.swift diff --git a/Tests/Fixtures/RetentionFixtures/testConformingProtocolReferencedByNonReferencedClass.swift b/Tests/Fixtures/Sources/RetentionFixtures/testConformingProtocolReferencedByNonReferencedClass.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testConformingProtocolReferencedByNonReferencedClass.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testConformingProtocolReferencedByNonReferencedClass.swift diff --git a/Tests/Fixtures/RetentionFixtures/testCrossReferencedClasses.swift b/Tests/Fixtures/Sources/RetentionFixtures/testCrossReferencedClasses.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testCrossReferencedClasses.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testCrossReferencedClasses.swift diff --git a/Tests/Fixtures/RetentionFixtures/testCustomConstructorWithLiteral.swift b/Tests/Fixtures/Sources/RetentionFixtures/testCustomConstructorWithLiteral.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testCustomConstructorWithLiteral.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testCustomConstructorWithLiteral.swift diff --git a/Tests/Fixtures/RetentionFixtures/testDeeplyNestedClassReferences.swift b/Tests/Fixtures/Sources/RetentionFixtures/testDeeplyNestedClassReferences.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testDeeplyNestedClassReferences.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testDeeplyNestedClassReferences.swift diff --git a/Tests/Fixtures/RetentionFixtures/testDoesNotRetainDescendantsOfUnusedDeclaration.swift b/Tests/Fixtures/Sources/RetentionFixtures/testDoesNotRetainDescendantsOfUnusedDeclaration.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testDoesNotRetainDescendantsOfUnusedDeclaration.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testDoesNotRetainDescendantsOfUnusedDeclaration.swift diff --git a/Tests/Fixtures/RetentionFixtures/testDoesNotRetainLazyProperty.swift b/Tests/Fixtures/Sources/RetentionFixtures/testDoesNotRetainLazyProperty.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testDoesNotRetainLazyProperty.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testDoesNotRetainLazyProperty.swift diff --git a/Tests/Fixtures/RetentionFixtures/testDoesNotRetainProtocolMembersImplementedByExternalType.swift b/Tests/Fixtures/Sources/RetentionFixtures/testDoesNotRetainProtocolMembersImplementedByExternalType.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testDoesNotRetainProtocolMembersImplementedByExternalType.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testDoesNotRetainProtocolMembersImplementedByExternalType.swift diff --git a/Tests/Fixtures/RetentionFixtures/testDoesNotRetainProtocolMethodInSubclassWithDefaultImplementation.swift b/Tests/Fixtures/Sources/RetentionFixtures/testDoesNotRetainProtocolMethodInSubclassWithDefaultImplementation.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testDoesNotRetainProtocolMethodInSubclassWithDefaultImplementation.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testDoesNotRetainProtocolMethodInSubclassWithDefaultImplementation.swift diff --git a/Tests/Fixtures/RetentionFixtures/testDoesNotRetainUnusedProtocolMethodWithDefaultImplementation.swift b/Tests/Fixtures/Sources/RetentionFixtures/testDoesNotRetainUnusedProtocolMethodWithDefaultImplementation.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testDoesNotRetainUnusedProtocolMethodWithDefaultImplementation.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testDoesNotRetainUnusedProtocolMethodWithDefaultImplementation.swift diff --git a/Tests/Fixtures/RetentionFixtures/testEnumCases.swift b/Tests/Fixtures/Sources/RetentionFixtures/testEnumCases.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testEnumCases.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testEnumCases.swift diff --git a/Tests/Fixtures/RetentionFixtures/testExternalXCTestCaseClass.swift b/Tests/Fixtures/Sources/RetentionFixtures/testExternalXCTestCaseClass.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testExternalXCTestCaseClass.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testExternalXCTestCaseClass.swift diff --git a/Tests/Fixtures/RetentionFixtures/testFunctionAccessorsRetainReferences.swift b/Tests/Fixtures/Sources/RetentionFixtures/testFunctionAccessorsRetainReferences.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testFunctionAccessorsRetainReferences.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testFunctionAccessorsRetainReferences.swift diff --git a/Tests/Fixtures/RetentionFixtures/testIdenticallyNamedVarsInStaticAndInstanceScopes.swift b/Tests/Fixtures/Sources/RetentionFixtures/testIdenticallyNamedVarsInStaticAndInstanceScopes.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testIdenticallyNamedVarsInStaticAndInstanceScopes.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testIdenticallyNamedVarsInStaticAndInstanceScopes.swift diff --git a/Tests/Fixtures/RetentionFixtures/testIgnoreAllComment.swift b/Tests/Fixtures/Sources/RetentionFixtures/testIgnoreAllComment.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testIgnoreAllComment.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testIgnoreAllComment.swift diff --git a/Tests/Fixtures/RetentionFixtures/testIgnoreComments.swift b/Tests/Fixtures/Sources/RetentionFixtures/testIgnoreComments.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testIgnoreComments.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testIgnoreComments.swift diff --git a/Tests/Fixtures/RetentionFixtures/testIgnoreUnusedParamInUnusedFunction.swift b/Tests/Fixtures/Sources/RetentionFixtures/testIgnoreUnusedParamInUnusedFunction.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testIgnoreUnusedParamInUnusedFunction.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testIgnoreUnusedParamInUnusedFunction.swift diff --git a/Tests/Fixtures/RetentionFixtures/testInstanceVarReferencedInClosure.swift b/Tests/Fixtures/Sources/RetentionFixtures/testInstanceVarReferencedInClosure.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testInstanceVarReferencedInClosure.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testInstanceVarReferencedInClosure.swift diff --git a/Tests/Fixtures/RetentionFixtures/testIsolatedCyclicRootReferences.swift b/Tests/Fixtures/Sources/RetentionFixtures/testIsolatedCyclicRootReferences.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testIsolatedCyclicRootReferences.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testIsolatedCyclicRootReferences.swift diff --git a/Tests/Fixtures/RetentionFixtures/testMainActorAnnotation.swift b/Tests/Fixtures/Sources/RetentionFixtures/testMainActorAnnotation.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testMainActorAnnotation.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testMainActorAnnotation.swift diff --git a/Tests/Fixtures/RetentionFixtures/testNestedDeclarations.swift b/Tests/Fixtures/Sources/RetentionFixtures/testNestedDeclarations.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testNestedDeclarations.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testNestedDeclarations.swift diff --git a/Tests/Fixtures/RetentionFixtures/testNonReferencedClass.swift b/Tests/Fixtures/Sources/RetentionFixtures/testNonReferencedClass.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testNonReferencedClass.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testNonReferencedClass.swift diff --git a/Tests/Fixtures/RetentionFixtures/testNonReferencedFreeFunction.swift b/Tests/Fixtures/Sources/RetentionFixtures/testNonReferencedFreeFunction.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testNonReferencedFreeFunction.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testNonReferencedFreeFunction.swift diff --git a/Tests/Fixtures/RetentionFixtures/testNonReferencedMethod.swift b/Tests/Fixtures/Sources/RetentionFixtures/testNonReferencedMethod.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testNonReferencedMethod.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testNonReferencedMethod.swift diff --git a/Tests/Fixtures/RetentionFixtures/testNonReferencedMethodInClassExtension.swift b/Tests/Fixtures/Sources/RetentionFixtures/testNonReferencedMethodInClassExtension.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testNonReferencedMethodInClassExtension.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testNonReferencedMethodInClassExtension.swift diff --git a/Tests/Fixtures/RetentionFixtures/testNonReferencedProperty.swift b/Tests/Fixtures/Sources/RetentionFixtures/testNonReferencedProperty.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testNonReferencedProperty.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testNonReferencedProperty.swift diff --git a/Tests/Fixtures/RetentionFixtures/testOverriddenMethodRetainedBySuper.swift b/Tests/Fixtures/Sources/RetentionFixtures/testOverriddenMethodRetainedBySuper.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testOverriddenMethodRetainedBySuper.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testOverriddenMethodRetainedBySuper.swift diff --git a/Tests/Fixtures/RetentionFixtures/testPropertyReferencedByComputedValue.swift b/Tests/Fixtures/Sources/RetentionFixtures/testPropertyReferencedByComputedValue.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testPropertyReferencedByComputedValue.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testPropertyReferencedByComputedValue.swift diff --git a/Tests/Fixtures/RetentionFixtures/testProtocolConformedByStaticMethodOutsideExtension.swift b/Tests/Fixtures/Sources/RetentionFixtures/testProtocolConformedByStaticMethodOutsideExtension.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testProtocolConformedByStaticMethodOutsideExtension.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testProtocolConformedByStaticMethodOutsideExtension.swift diff --git a/Tests/Fixtures/RetentionFixtures/testProtocolConformingMembersAreRetained.swift b/Tests/Fixtures/Sources/RetentionFixtures/testProtocolConformingMembersAreRetained.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testProtocolConformingMembersAreRetained.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testProtocolConformingMembersAreRetained.swift diff --git a/Tests/Fixtures/RetentionFixtures/testProtocolImplementInClassAndExtension.swift b/Tests/Fixtures/Sources/RetentionFixtures/testProtocolImplementInClassAndExtension.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testProtocolImplementInClassAndExtension.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testProtocolImplementInClassAndExtension.swift diff --git a/Tests/Fixtures/RetentionFixtures/testProtocolMethodCalledIndirectlyByProtocolIsRetained.swift b/Tests/Fixtures/Sources/RetentionFixtures/testProtocolMethodCalledIndirectlyByProtocolIsRetained.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testProtocolMethodCalledIndirectlyByProtocolIsRetained.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testProtocolMethodCalledIndirectlyByProtocolIsRetained.swift diff --git a/Tests/Fixtures/RetentionFixtures/testProtocolMethodsImplementedOnlyInExtension.swift b/Tests/Fixtures/Sources/RetentionFixtures/testProtocolMethodsImplementedOnlyInExtension.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testProtocolMethodsImplementedOnlyInExtension.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testProtocolMethodsImplementedOnlyInExtension.swift diff --git a/Tests/Fixtures/RetentionFixtures/testProtocolUsedAsExistentialType.swift b/Tests/Fixtures/Sources/RetentionFixtures/testProtocolUsedAsExistentialType.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testProtocolUsedAsExistentialType.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testProtocolUsedAsExistentialType.swift diff --git a/Tests/Fixtures/RetentionFixtures/testProtocolVarReferencedByProtocolMethodInSameClassIsRedundant.swift b/Tests/Fixtures/Sources/RetentionFixtures/testProtocolVarReferencedByProtocolMethodInSameClassIsRedundant.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testProtocolVarReferencedByProtocolMethodInSameClassIsRedundant.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testProtocolVarReferencedByProtocolMethodInSameClassIsRedundant.swift diff --git a/Tests/Fixtures/RetentionFixtures/testPublicProtocolMethodImplementedOnlyInExtension.swift b/Tests/Fixtures/Sources/RetentionFixtures/testPublicProtocolMethodImplementedOnlyInExtension.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testPublicProtocolMethodImplementedOnlyInExtension.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testPublicProtocolMethodImplementedOnlyInExtension.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRedundantProtocolThatInheritsAnyObject.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRedundantProtocolThatInheritsAnyObject.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRedundantProtocolThatInheritsAnyObject.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRedundantProtocolThatInheritsAnyObject.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRedundantProtocolThatInheritsForeignProtocol.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRedundantProtocolThatInheritsForeignProtocol.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRedundantProtocolThatInheritsForeignProtocol.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRedundantProtocolThatInheritsForeignProtocol.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRedundantProtocolThatInheritsOtherProtocols.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRedundantProtocolThatInheritsOtherProtocols.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRedundantProtocolThatInheritsOtherProtocols.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRedundantProtocolThatInheritsOtherProtocols.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRequiredInitInSubclass.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRequiredInitInSubclass.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRequiredInitInSubclass.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRequiredInitInSubclass.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainImplicitDeclarations.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainImplicitDeclarations.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainImplicitDeclarations.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainImplicitDeclarations.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainOverridingMethod.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainOverridingMethod.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainOverridingMethod.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainOverridingMethod.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainPublicMembers.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainPublicMembers.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainPublicMembers.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainPublicMembers.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainUnusedProtocolFuncParams.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainUnusedProtocolFuncParams.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainUnusedProtocolFuncParams.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainUnusedProtocolFuncParams.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainedProtocolDoesNotRetainImplementationInUnusedClass.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainedProtocolDoesNotRetainImplementationInUnusedClass.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainedProtocolDoesNotRetainImplementationInUnusedClass.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainedProtocolDoesNotRetainImplementationInUnusedClass.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainedProtocolDoesNotRetainUnusedClass.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainedProtocolDoesNotRetainUnusedClass.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainedProtocolDoesNotRetainUnusedClass.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainedProtocolDoesNotRetainUnusedClass.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsAssignOnlyPropertyTypes.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsAssignOnlyPropertyTypes.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsAssignOnlyPropertyTypes.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsAssignOnlyPropertyTypes.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsAssociatedTypeTypeAlias.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsAssociatedTypeTypeAlias.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsAssociatedTypeTypeAlias.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsAssociatedTypeTypeAlias.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsCallAsFunction.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsCallAsFunction.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsCallAsFunction.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsCallAsFunction.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsCodableProperties.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsCodableProperties.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsCodableProperties.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsCodableProperties.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsConstructorOfGenericClassAndStruct.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsConstructorOfGenericClassAndStruct.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsConstructorOfGenericClassAndStruct.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsConstructorOfGenericClassAndStruct.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsDefaultConstructor.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsDefaultConstructor.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsDefaultConstructor.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsDefaultConstructor.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsDestructor.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsDestructor.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsDestructor.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsDestructor.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsDynamicMemberLookupSubscript.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsDynamicMemberLookupSubscript.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsDynamicMemberLookupSubscript.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsDynamicMemberLookupSubscript.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsEncodableProperties.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsEncodableProperties.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsEncodableProperties.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsEncodableProperties.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsExtendedExternalTypeAlias.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsExtendedExternalTypeAlias.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsExtendedExternalTypeAlias.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsExtendedExternalTypeAlias.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsExtendedProtocolTypeAlias.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsExtendedProtocolTypeAlias.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsExtendedProtocolTypeAlias.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsExtendedProtocolTypeAlias.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsExtendedTypeAlias.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsExtendedTypeAlias.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsExtendedTypeAlias.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsExtendedTypeAlias.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsExternalAssociatedTypeTypeAlias.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsExternalAssociatedTypeTypeAlias.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsExternalAssociatedTypeTypeAlias.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsExternalAssociatedTypeTypeAlias.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsExternalTypeExtension.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsExternalTypeExtension.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsExternalTypeExtension.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsExternalTypeExtension.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsFilesOption.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsFilesOption.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsFilesOption.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsFilesOption.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsForeignProtocolParameters.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsForeignProtocolParameters.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsForeignProtocolParameters.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsForeignProtocolParameters.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsForeignProtocolParametersInSubclass.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsForeignProtocolParametersInSubclass.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsForeignProtocolParametersInSubclass.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsForeignProtocolParametersInSubclass.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsFunctionParametersOnProtocolMembersImplementedByExternalType.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsFunctionParametersOnProtocolMembersImplementedByExternalType.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsFunctionParametersOnProtocolMembersImplementedByExternalType.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsFunctionParametersOnProtocolMembersImplementedByExternalType.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsFunctionParametersOnUnimplementedProtocolMembers.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsFunctionParametersOnUnimplementedProtocolMembers.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsFunctionParametersOnUnimplementedProtocolMembers.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsFunctionParametersOnUnimplementedProtocolMembers.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsGenericProtocolExtensionMembers.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsGenericProtocolExtensionMembers.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsGenericProtocolExtensionMembers.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsGenericProtocolExtensionMembers.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsGenericType.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsGenericType.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsGenericType.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsGenericType.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsInferredAssociatedType.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsInferredAssociatedType.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsInferredAssociatedType.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsInferredAssociatedType.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsInheritedClass.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsInheritedClass.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsInheritedClass.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsInheritedClass.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsInitializerCalledOnTypeAlias.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsInitializerCalledOnTypeAlias.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsInitializerCalledOnTypeAlias.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsInitializerCalledOnTypeAlias.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsMethodDefinedInExtensionOnStandardType.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsMethodDefinedInExtensionOnStandardType.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsMethodDefinedInExtensionOnStandardType.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsMethodDefinedInExtensionOnStandardType.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsNonProtocolMethodDefinedInProtocolExtension.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsNonProtocolMethodDefinedInProtocolExtension.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsNonProtocolMethodDefinedInProtocolExtension.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsNonProtocolMethodDefinedInProtocolExtension.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsOpenClassParameters.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsOpenClassParameters.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsOpenClassParameters.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsOpenClassParameters.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsParamUsedInOverriddenMethod.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsParamUsedInOverriddenMethod.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsParamUsedInOverriddenMethod.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsParamUsedInOverriddenMethod.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsPropertyWrappers.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsPropertyWrappers.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsPropertyWrappers.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsPropertyWrappers.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsProtocolExtension.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsProtocolExtension.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsProtocolExtension.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsProtocolExtension.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsProtocolMethodImplementedInExtension.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsProtocolMethodImplementedInExtension.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsProtocolMethodImplementedInExtension.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsProtocolMethodImplementedInExtension.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsProtocolMethodsImplementedInSuperclasss.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsProtocolMethodsImplementedInSuperclasss.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsProtocolMethodsImplementedInSuperclasss.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsProtocolMethodsImplementedInSuperclasss.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsProtocolParameters.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsProtocolParameters.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsProtocolParameters.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsProtocolParameters.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsProtocolsViaCompositeTypealias.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsProtocolsViaCompositeTypealias.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsProtocolsViaCompositeTypealias.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsProtocolsViaCompositeTypealias.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsPublicEnumCases.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsPublicEnumCases.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsPublicEnumCases.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsPublicEnumCases.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsReferencedMethodViaReceiver.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsReferencedMethodViaReceiver.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsReferencedMethodViaReceiver.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsReferencedMethodViaReceiver.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsResultBuilderMethods.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsResultBuilderMethods.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsResultBuilderMethods.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsResultBuilderMethods.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsSelfReferencedMethodViaReceiver.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsSelfReferencedMethodViaReceiver.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsSelfReferencedMethodViaReceiver.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsSelfReferencedMethodViaReceiver.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsStringInterpolationAppendInterpolation.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsStringInterpolationAppendInterpolation.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsStringInterpolationAppendInterpolation.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsStringInterpolationAppendInterpolation.swift diff --git a/Tests/Fixtures/RetentionFixtures/testRetainsUsedProtocolThatInheritsForeignProtocol.swift b/Tests/Fixtures/Sources/RetentionFixtures/testRetainsUsedProtocolThatInheritsForeignProtocol.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testRetainsUsedProtocolThatInheritsForeignProtocol.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testRetainsUsedProtocolThatInheritsForeignProtocol.swift diff --git a/Tests/Fixtures/RetentionFixtures/testSelfReferencedClass.swift b/Tests/Fixtures/Sources/RetentionFixtures/testSelfReferencedClass.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testSelfReferencedClass.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testSelfReferencedClass.swift diff --git a/Tests/Fixtures/RetentionFixtures/testSelfReferencedConstructor.swift b/Tests/Fixtures/Sources/RetentionFixtures/testSelfReferencedConstructor.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testSelfReferencedConstructor.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testSelfReferencedConstructor.swift diff --git a/Tests/Fixtures/RetentionFixtures/testSelfReferencedProperty.swift b/Tests/Fixtures/Sources/RetentionFixtures/testSelfReferencedProperty.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testSelfReferencedProperty.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testSelfReferencedProperty.swift diff --git a/Tests/Fixtures/RetentionFixtures/testSelfReferencedRecursiveMethod.swift b/Tests/Fixtures/Sources/RetentionFixtures/testSelfReferencedRecursiveMethod.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testSelfReferencedRecursiveMethod.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testSelfReferencedRecursiveMethod.swift diff --git a/Tests/Fixtures/RetentionFixtures/testSimpleAssignOnlyPropertyNameConflict.swift b/Tests/Fixtures/Sources/RetentionFixtures/testSimpleAssignOnlyPropertyNameConflict.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testSimpleAssignOnlyPropertyNameConflict.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testSimpleAssignOnlyPropertyNameConflict.swift diff --git a/Tests/Fixtures/RetentionFixtures/testSimplePropertyAssignedButNeverRead.swift b/Tests/Fixtures/Sources/RetentionFixtures/testSimplePropertyAssignedButNeverRead.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testSimplePropertyAssignedButNeverRead.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testSimplePropertyAssignedButNeverRead.swift diff --git a/Tests/Fixtures/RetentionFixtures/testSimpleRedundantProtocol.swift b/Tests/Fixtures/Sources/RetentionFixtures/testSimpleRedundantProtocol.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testSimpleRedundantProtocol.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testSimpleRedundantProtocol.swift diff --git a/Tests/Fixtures/RetentionFixtures/testStaticMemberUsedAsSubscriptKey.swift b/Tests/Fixtures/Sources/RetentionFixtures/testStaticMemberUsedAsSubscriptKey.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testStaticMemberUsedAsSubscriptKey.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testStaticMemberUsedAsSubscriptKey.swift diff --git a/Tests/Fixtures/RetentionFixtures/testStaticPropertyDeclaredWithCompositeValuesIsNotRetained.swift b/Tests/Fixtures/Sources/RetentionFixtures/testStaticPropertyDeclaredWithCompositeValuesIsNotRetained.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testStaticPropertyDeclaredWithCompositeValuesIsNotRetained.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testStaticPropertyDeclaredWithCompositeValuesIsNotRetained.swift diff --git a/Tests/Fixtures/RetentionFixtures/testStructImplicitInitializer.swift b/Tests/Fixtures/Sources/RetentionFixtures/testStructImplicitInitializer.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testStructImplicitInitializer.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testStructImplicitInitializer.swift diff --git a/Tests/Fixtures/RetentionFixtures/testUnusedAssociatedType.swift b/Tests/Fixtures/Sources/RetentionFixtures/testUnusedAssociatedType.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testUnusedAssociatedType.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testUnusedAssociatedType.swift diff --git a/Tests/Fixtures/RetentionFixtures/testUnusedOverriddenMethod.swift b/Tests/Fixtures/Sources/RetentionFixtures/testUnusedOverriddenMethod.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testUnusedOverriddenMethod.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testUnusedOverriddenMethod.swift diff --git a/Tests/Fixtures/RetentionFixtures/testUnusedProtocolWithExtension.swift b/Tests/Fixtures/Sources/RetentionFixtures/testUnusedProtocolWithExtension.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testUnusedProtocolWithExtension.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testUnusedProtocolWithExtension.swift diff --git a/Tests/Fixtures/RetentionFixtures/testUnusedTypealias.swift b/Tests/Fixtures/Sources/RetentionFixtures/testUnusedTypealias.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testUnusedTypealias.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testUnusedTypealias.swift diff --git a/Tests/Fixtures/RetentionFixtures/testXCTestCaseClassesAndMethodsAreRetained.swift b/Tests/Fixtures/Sources/RetentionFixtures/testXCTestCaseClassesAndMethodsAreRetained.swift similarity index 100% rename from Tests/Fixtures/RetentionFixtures/testXCTestCaseClassesAndMethodsAreRetained.swift rename to Tests/Fixtures/Sources/RetentionFixtures/testXCTestCaseClassesAndMethodsAreRetained.swift diff --git a/Tests/Fixtures/TypeSyntaxInspectorFixtures/TypeSyntaxInspectorFixture.swift b/Tests/Fixtures/Sources/TypeSyntaxInspectorFixtures/TypeSyntaxInspectorFixture.swift similarity index 100% rename from Tests/Fixtures/TypeSyntaxInspectorFixtures/TypeSyntaxInspectorFixture.swift rename to Tests/Fixtures/Sources/TypeSyntaxInspectorFixtures/TypeSyntaxInspectorFixture.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testBackquote.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testBackquote.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testBackquote.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testBackquote.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testBlockParameter.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testBlockParameter.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testBlockParameter.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testBlockParameter.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testFatalErrorFunction.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testFatalErrorFunction.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testFatalErrorFunction.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testFatalErrorFunction.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testFunctionCallWithNamedParameter.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testFunctionCallWithNamedParameter.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testFunctionCallWithNamedParameter.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testFunctionCallWithNamedParameter.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testIBActionAnnotatedFunction.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testIBActionAnnotatedFunction.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testIBActionAnnotatedFunction.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testIBActionAnnotatedFunction.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testIgnoreProtocolDeclaration.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testIgnoreProtocolDeclaration.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testIgnoreProtocolDeclaration.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testIgnoreProtocolDeclaration.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testIgnoredParameter.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testIgnoredParameter.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testIgnoredParameter.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testIgnoredParameter.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testInitializer.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testInitializer.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testInitializer.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testInitializer.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testInitializerPosition.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testInitializerPosition.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testInitializerPosition.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testInitializerPosition.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testLocalVarDeclaredInBlock.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testLocalVarDeclaredInBlock.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testLocalVarDeclaredInBlock.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testLocalVarDeclaredInBlock.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testLocalVariableAssignment.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testLocalVariableAssignment.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testLocalVariableAssignment.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testLocalVariableAssignment.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testMultiLineParameterPosition.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testMultiLineParameterPosition.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testMultiLineParameterPosition.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testMultiLineParameterPosition.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testNestedFunction.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testNestedFunction.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testNestedFunction.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testNestedFunction.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testNestedVariable.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testNestedVariable.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testNestedVariable.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testNestedVariable.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testParamForGenericSpecialization.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testParamForGenericSpecialization.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testParamForGenericSpecialization.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testParamForGenericSpecialization.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testParameterPosition.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testParameterPosition.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testParameterPosition.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testParameterPosition.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testReturn.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testReturn.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testReturn.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testReturn.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testShadowed.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testShadowed.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testShadowed.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testShadowed.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testShadowedAfterUse.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testShadowedAfterUse.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testShadowedAfterUse.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testShadowedAfterUse.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testShadowedByBlockParameter.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testShadowedByBlockParameter.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testShadowedByBlockParameter.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testShadowedByBlockParameter.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testSimpleFunctionCall.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testSimpleFunctionCall.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testSimpleFunctionCall.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testSimpleFunctionCall.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testSimpleUnused.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testSimpleUnused.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testSimpleUnused.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testSimpleUnused.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testStringInterpolation.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testStringInterpolation.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testStringInterpolation.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testStringInterpolation.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testSubscriptArgument.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testSubscriptArgument.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testSubscriptArgument.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testSubscriptArgument.swift diff --git a/Tests/Fixtures/UnusedParameterFixtures/testUsedInInitializerCall.swift b/Tests/Fixtures/Sources/UnusedParameterFixtures/testUsedInInitializerCall.swift similarity index 100% rename from Tests/Fixtures/UnusedParameterFixtures/testUsedInInitializerCall.swift rename to Tests/Fixtures/Sources/UnusedParameterFixtures/testUsedInInitializerCall.swift diff --git a/Tests/PeripheryTests/CrossModuleRetentionTest.swift b/Tests/PeripheryTests/CrossModuleRetentionTest.swift index e79be95b24..6f8ae23b12 100644 --- a/Tests/PeripheryTests/CrossModuleRetentionTest.swift +++ b/Tests/PeripheryTests/CrossModuleRetentionTest.swift @@ -7,8 +7,7 @@ final class CrossModuleRetentionTest: SourceGraphTestCase { static override func setUp() { super.setUp() - configuration.targets = ["CrossModuleRetentionFixtures", "CrossModuleRetentionSupportFixtures"] - build(driver: SPMProjectDriver.self) + build(driver: SPMProjectDriver.self, projectPath: FixturesProjectPath) index() } diff --git a/Tests/PeripheryTests/ObjcAccessibleRetentionTest.swift b/Tests/PeripheryTests/ObjcAccessibleRetentionTest.swift index 743d32ba72..3c6edb655a 100644 --- a/Tests/PeripheryTests/ObjcAccessibleRetentionTest.swift +++ b/Tests/PeripheryTests/ObjcAccessibleRetentionTest.swift @@ -1,20 +1,11 @@ import XCTest import SystemPackage @testable import TestShared -@testable import PeripheryKit #if os(macOS) final class ObjcAccessibleRetentionTest: FixtureSourceGraphTestCase { let performKnownFailures = false - static override func setUp() { - super.setUp() - - configuration.targets = ["ObjcAccessibleRetentionFixtures"] - - build(driver: SPMProjectDriver.self) - } - // https://github.com/apple/swift/issues/56327 func testRetainsOptionalProtocolMethodImplementedInSubclass() { guard performKnownFailures else { return } diff --git a/Tests/PeripheryTests/ObjcAnnotatedRetentionTest.swift b/Tests/PeripheryTests/ObjcAnnotatedRetentionTest.swift index 3fa5e0e7c4..66d3e8f955 100644 --- a/Tests/PeripheryTests/ObjcAnnotatedRetentionTest.swift +++ b/Tests/PeripheryTests/ObjcAnnotatedRetentionTest.swift @@ -1,18 +1,9 @@ import XCTest import SystemPackage @testable import TestShared -@testable import PeripheryKit #if os(macOS) final class ObjcAnnotatedRetentionTest: FixtureSourceGraphTestCase { - static override func setUp() { - super.setUp() - - configuration.targets = ["ObjcAnnotatedRetentionFixtures"] - - build(driver: SPMProjectDriver.self) - } - func testRetainsAnnotatedExtensionDeclarations() { analyze(retainObjcAnnotated: true) { assertReferenced(.class("FixtureClass214")) { diff --git a/Tests/PeripheryTests/RetentionTest.swift b/Tests/PeripheryTests/RetentionTest.swift index ef32a5517d..b74e1e6bb2 100644 --- a/Tests/PeripheryTests/RetentionTest.swift +++ b/Tests/PeripheryTests/RetentionTest.swift @@ -2,19 +2,10 @@ import XCTest import SystemPackage import Shared @testable import TestShared -@testable import PeripheryKit final class RetentionTest: FixtureSourceGraphTestCase { let performKnownFailures = false - static override func setUp() { - super.setUp() - - configuration.targets = ["RetentionFixtures"] - - build(driver: SPMProjectDriver.self) - } - func testNonReferencedClass() { analyze() { assertNotReferenced(.class("FixtureClass1")) diff --git a/Tests/PeripheryTests/Syntax/FunctionVisitTest.swift b/Tests/PeripheryTests/Syntax/FunctionVisitTest.swift index dc9855c619..e9a209d2d4 100644 --- a/Tests/PeripheryTests/Syntax/FunctionVisitTest.swift +++ b/Tests/PeripheryTests/Syntax/FunctionVisitTest.swift @@ -151,7 +151,7 @@ class FunctionVisitTest: XCTestCase { // MARK: - Private private var fixturePath: SourceFile { - let path = ProjectRootPath.appending( "Tests/Fixtures/DeclarationVisitorFixtures/FunctionFixture.swift") + let path = FixturesProjectPath.appending( "Sources/DeclarationVisitorFixtures/FunctionFixture.swift") return SourceFile(path: path, modules: ["DeclarationVisitorFixtures"]) } diff --git a/Tests/PeripheryTests/Syntax/PropertyVisitTest.swift b/Tests/PeripheryTests/Syntax/PropertyVisitTest.swift index f575981602..3ae487f5ac 100644 --- a/Tests/PeripheryTests/Syntax/PropertyVisitTest.swift +++ b/Tests/PeripheryTests/Syntax/PropertyVisitTest.swift @@ -114,7 +114,7 @@ class PropertyVisitTest: XCTestCase { // MARK: - Private private var fixturePath: SourceFile { - let path = ProjectRootPath.appending( "Tests/Fixtures/DeclarationVisitorFixtures/PropertyFixture.swift") + let path = FixturesProjectPath.appending( "Sources/DeclarationVisitorFixtures/PropertyFixture.swift") return SourceFile(path: path, modules: ["DeclarationVisitorFixtures"]) } diff --git a/Tests/PeripheryTests/Syntax/TypeSyntaxInspectorTest.swift b/Tests/PeripheryTests/Syntax/TypeSyntaxInspectorTest.swift index 5131caf378..92718c2389 100644 --- a/Tests/PeripheryTests/Syntax/TypeSyntaxInspectorTest.swift +++ b/Tests/PeripheryTests/Syntax/TypeSyntaxInspectorTest.swift @@ -114,7 +114,7 @@ class TypeSyntaxInspectorTest: XCTestCase { // MARK: - Private private var fixturePath: SourceFile { - let path = ProjectRootPath.appending( "Tests/Fixtures/TypeSyntaxInspectorFixtures/TypeSyntaxInspectorFixture.swift") + let path = FixturesProjectPath.appending( "Sources/TypeSyntaxInspectorFixtures/TypeSyntaxInspectorFixture.swift") return SourceFile(path: path, modules: ["TypeSyntaxInspectorFixtures"]) } diff --git a/Tests/SPMTests/SPMProject/Package.swift b/Tests/SPMTests/SPMProject/Package.swift index 255f0d42a7..8c18f8986e 100644 --- a/Tests/SPMTests/SPMProject/Package.swift +++ b/Tests/SPMTests/SPMProject/Package.swift @@ -1,5 +1,4 @@ -// swift-tools-version:5.2 - +// swift-tools-version:5.9 import PackageDescription let package = Package( @@ -7,15 +6,15 @@ let package = Package( products: [ .executable( name: "frontend", - targets: ["Frontend", "SPMProjectKit"] - ) + targets: ["Frontend"] + ), ], targets: [ + .executableTarget( + name: "Frontend", + dependencies: ["SPMProjectKit"]) .target( name: "SPMProjectKit", - dependencies: []), - .target( - name: "Frontend", dependencies: []) ] ) diff --git a/Tests/SPMTests/SPMProjectTest.swift b/Tests/SPMTests/SPMProjectTest.swift index 7e08b1a378..321148d5b5 100644 --- a/Tests/SPMTests/SPMProjectTest.swift +++ b/Tests/SPMTests/SPMProjectTest.swift @@ -6,8 +6,6 @@ class SPMProjectTest: SourceGraphTestCase { override static func setUp() { super.setUp() - configuration.targets = ["SPMProjectKit", "Frontend"] - build(driver: SPMProjectDriver.self, projectPath: SPMProjectPath) index() } diff --git a/Tests/Shared/FixtureSourceGraphTestCase.swift b/Tests/Shared/FixtureSourceGraphTestCase.swift index 64d0dbf93f..2858967b31 100644 --- a/Tests/Shared/FixtureSourceGraphTestCase.swift +++ b/Tests/Shared/FixtureSourceGraphTestCase.swift @@ -3,9 +3,10 @@ import SystemPackage import XCTest class FixtureSourceGraphTestCase: SourceGraphTestCase { - class override func setUp() { + static override func setUp() { super.setUp() - _sourceFiles = nil + + build(driver: SPMProjectDriver.self, projectPath: FixturesProjectPath) } @discardableResult @@ -19,32 +20,14 @@ class FixtureSourceGraphTestCase: SourceGraphTestCase { configuration.retainObjcAccessible = retainObjcAccessible configuration.retainObjcAnnotated = retainObjcAnnotated configuration.disableRedundantPublicAnalysis = disableRedundantPublicAnalysis - configuration.indexExclude = Self.sourceFiles.subtracting([testFixturePath]).map { $0.string } configuration.resetMatchers() if !testFixturePath.exists { fatalError("\(testFixturePath.string) does not exist") } - Self.index() + Self.index(sourceFile: testFixturePath) try testBlock() return Self.results } - - // MARK: - Private - - private static var _sourceFiles: Set? - private static var sourceFiles: Set { - if let files = _sourceFiles { - return files - } - - if let driver = driver as? SPMProjectDriver { - let files = Set(driver.targets.flatMap { $0.sourcePaths }.map { ProjectRootPath.appending($0.string) }) - _sourceFiles = files - return files - } else { - fatalError("Not implemented") - } - } } diff --git a/Tests/Shared/Helper.swift b/Tests/Shared/Helper.swift index 3bc4cbd8ae..5498254f75 100644 --- a/Tests/Shared/Helper.swift +++ b/Tests/Shared/Helper.swift @@ -4,3 +4,7 @@ import SystemPackage var ProjectRootPath: FilePath { FilePath(#filePath).appending("../../..").lexicallyNormalized() } + +var FixturesProjectPath: FilePath { + ProjectRootPath.appending("Tests/Fixtures") +} diff --git a/Tests/Shared/SourceGraphTestCase.swift b/Tests/Shared/SourceGraphTestCase.swift index bb28380c8b..fb0ce7fad3 100644 --- a/Tests/Shared/SourceGraphTestCase.swift +++ b/Tests/Shared/SourceGraphTestCase.swift @@ -12,6 +12,7 @@ open class SourceGraphTestCase: XCTestCase { private static var graph = SourceGraph() private static var allIndexedDeclarations: Set = [] + private static var sourceFiles: [SourceFile: [IndexUnit]] = [:] var configuration: Configuration { Self.configuration } @@ -42,12 +43,19 @@ open class SourceGraphTestCase: XCTestCase { projectPath.chdir { driver = try! driverType.build() try! driver.build() + sourceFiles = try! driver.collect(logger: Logger().contextualized(with: "index")) } } - static func index() { + static func index(sourceFile: FilePath? = nil) { + var indexSourceFiles = sourceFiles + + if let sourceFile { + indexSourceFiles = sourceFiles.filter { $0.key.path == sourceFile } + } + graph = SourceGraph() - try! Self.driver.index(graph: graph) + try! Self.driver.index(sourceFiles: indexSourceFiles, graph: graph, logger: Logger().contextualized(with: "index")) allIndexedDeclarations = graph.allDeclarations try! SourceGraphMutatorRunner.perform(graph: graph) results = ScanResultBuilder.build(for: graph) diff --git a/Tests/Shared/XCTestCase+Extensions.swift b/Tests/Shared/XCTestCase+Extensions.swift index 4466de8263..741bd8970f 100644 --- a/Tests/Shared/XCTestCase+Extensions.swift +++ b/Tests/Shared/XCTestCase+Extensions.swift @@ -10,6 +10,6 @@ public extension XCTestCase { #endif let suiteName = String(describing: Self.self).dropLast(4) - return ProjectRootPath.appending("Tests/Fixtures/\(suiteName)Fixtures/\(testName).swift") + return FixturesProjectPath.appending("Sources/\(suiteName)Fixtures/\(testName).swift") } } diff --git a/Tests/XcodeTests/SwiftUIProjectTest.swift b/Tests/XcodeTests/SwiftUIProjectTest.swift index 812a0b921d..8e28e5165a 100644 --- a/Tests/XcodeTests/SwiftUIProjectTest.swift +++ b/Tests/XcodeTests/SwiftUIProjectTest.swift @@ -8,7 +8,6 @@ class SwiftUIProjectTest: SourceGraphTestCase { configuration.project = SwiftUIProjectPath.string configuration.schemes = ["SwiftUIProject"] - configuration.targets = ["SwiftUIProject"] build(driver: XcodeProjectDriver.self) index() diff --git a/Tests/XcodeTests/UIKitProject/LocalPackages/LocalPackage/Package.swift b/Tests/XcodeTests/UIKitProject/LocalPackages/LocalPackage/Package.swift index 5d9773ed94..ef0cd76208 100644 --- a/Tests/XcodeTests/UIKitProject/LocalPackages/LocalPackage/Package.swift +++ b/Tests/XcodeTests/UIKitProject/LocalPackages/LocalPackage/Package.swift @@ -1,23 +1,14 @@ -// swift-tools-version: 5.7 -// The swift-tools-version declares the minimum version of Swift required to build this package. - +// swift-tools-version: 5.9 import PackageDescription let package = Package( name: "LocalPackage", products: [ - // Products define the executables and libraries a package produces, and make them visible to other packages. .library( name: "LocalPackageTarget", targets: ["LocalPackageTarget"]) ], - dependencies: [ - // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0"), - ], targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages this package depends on. .target( name: "LocalPackageTarget", dependencies: []) diff --git a/Tests/XcodeTests/UIKitProject/UIKitProject.xcodeproj/project.pbxproj b/Tests/XcodeTests/UIKitProject/UIKitProject.xcodeproj/project.pbxproj index d5a7a14ada..05684c5fcf 100644 --- a/Tests/XcodeTests/UIKitProject/UIKitProject.xcodeproj/project.pbxproj +++ b/Tests/XcodeTests/UIKitProject/UIKitProject.xcodeproj/project.pbxproj @@ -10,7 +10,6 @@ 3C1A70B4256BBAB300E07E4A /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C1A70B3256BBAB300E07E4A /* NotificationService.swift */; }; 3C1A70B8256BBAB300E07E4A /* NotificationServiceExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 3C1A70B1256BBAB300E07E4A /* NotificationServiceExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 3C1FED002556D5AB0001BD58 /* UIKitProjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C1FECFF2556D5AB0001BD58 /* UIKitProjectTests.swift */; }; - 3C1FED0D2556D7C60001BD58 /* FileInGroupWithoutFolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C1FED0C2556D7C60001BD58 /* FileInGroupWithoutFolder.swift */; }; 3C1FED1E2556D91D0001BD58 /* Target_With_Spaces.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C1FED172556D91C0001BD58 /* Target_With_Spaces.framework */; }; 3C1FED1F2556D91D0001BD58 /* Target_With_Spaces.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3C1FED172556D91C0001BD58 /* Target_With_Spaces.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 3C1FED2B2556D9360001BD58 /* File With Spaces.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C1FED2A2556D9360001BD58 /* File With Spaces.swift */; }; @@ -31,6 +30,7 @@ 3CBEB9D62AEFC25B00F5947D /* WatchWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CBEB9D52AEFC25B00F5947D /* WatchWidget.swift */; }; 3CBEB9D92AEFC25C00F5947D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3CBEB9D82AEFC25C00F5947D /* Assets.xcassets */; }; 3CBEB9DB2AEFC25C00F5947D /* WatchWidget.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 3CBEB9D72AEFC25B00F5947D /* WatchWidget.intentdefinition */; }; + 3CC94B432C3AE1FD000826B7 /* FileInGroupWithoutFolder.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3CC94B422C3AE1FD000826B7 /* FileInGroupWithoutFolder.xib */; }; 3CD46111256C034C00856DAC /* MultiTargetStruct.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CD46107256C02FE00856DAC /* MultiTargetStruct.swift */; }; 3CD46115256C034D00856DAC /* MultiTargetStruct.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CD46107256C02FE00856DAC /* MultiTargetStruct.swift */; }; 3CD8FF922683ACBB001951CD /* EntityValueTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CD8FF912683ACBB001951CD /* EntityValueTransformer.swift */; }; @@ -97,7 +97,6 @@ 3C1FECFD2556D5AB0001BD58 /* UIKitProjectTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UIKitProjectTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3C1FECFF2556D5AB0001BD58 /* UIKitProjectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitProjectTests.swift; sourceTree = ""; }; 3C1FED012556D5AB0001BD58 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 3C1FED0C2556D7C60001BD58 /* FileInGroupWithoutFolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileInGroupWithoutFolder.swift; sourceTree = ""; }; 3C1FED172556D91C0001BD58 /* Target_With_Spaces.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Target_With_Spaces.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3C1FED1A2556D91D0001BD58 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3C1FED2A2556D9360001BD58 /* File With Spaces.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "File With Spaces.swift"; sourceTree = ""; }; @@ -120,6 +119,7 @@ 3CBEB9D72AEFC25B00F5947D /* WatchWidget.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = WatchWidget.intentdefinition; sourceTree = ""; }; 3CBEB9D82AEFC25C00F5947D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 3CBEB9DA2AEFC25C00F5947D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 3CC94B422C3AE1FD000826B7 /* FileInGroupWithoutFolder.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = FileInGroupWithoutFolder.xib; sourceTree = ""; }; 3CD46107256C02FE00856DAC /* MultiTargetStruct.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiTargetStruct.swift; sourceTree = ""; }; 3CD8FF912683ACBB001951CD /* EntityValueTransformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntityValueTransformer.swift; sourceTree = ""; }; 3CE3F7CB2685DEFB0047231C /* OldModel.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = OldModel.xcdatamodel; sourceTree = ""; }; @@ -194,7 +194,7 @@ 3C1FED0B2556D7B30001BD58 /* GroupWithoutFolder */ = { isa = PBXGroup; children = ( - 3C1FED0C2556D7C60001BD58 /* FileInGroupWithoutFolder.swift */, + 3CC94B422C3AE1FD000826B7 /* FileInGroupWithoutFolder.xib */, ); name = GroupWithoutFolder; sourceTree = ""; @@ -486,6 +486,7 @@ 3C3ABC05256522AB00EAB309 /* XibViewController2Subclass.xib in Resources */, 3C849665255405B000900DA9 /* Preview Assets.xcassets in Resources */, 3C9B06C125542DD700E45614 /* XibViewController.xib in Resources */, + 3CC94B432C3AE1FD000826B7 /* FileInGroupWithoutFolder.xib in Resources */, 3C9B06C425542F2500E45614 /* Launch Screen.storyboard in Resources */, 3C849662255405B000900DA9 /* Assets.xcassets in Resources */, 3C9B06E725547C3800E45614 /* StoryboardViewController.storyboard in Resources */, @@ -533,7 +534,6 @@ buildActionMask = 2147483647; files = ( 3C5E4E58255C25D200BF728D /* AppDelegate.swift in Sources */, - 3C1FED0D2556D7C60001BD58 /* FileInGroupWithoutFolder.swift in Sources */, 3CD46111256C034C00856DAC /* MultiTargetStruct.swift in Sources */, 3CE3F7CC2685DEFB0047231C /* OldModel.xcdatamodeld in Sources */, 3C3ABC09256526F900EAB309 /* XibViewController2.swift in Sources */, diff --git a/Tests/XcodeTests/UIKitProject/UIKitProject/FileInGroupWithoutFolder.swift b/Tests/XcodeTests/UIKitProject/UIKitProject/FileInGroupWithoutFolder.swift deleted file mode 100644 index 2bd6dce476..0000000000 --- a/Tests/XcodeTests/UIKitProject/UIKitProject/FileInGroupWithoutFolder.swift +++ /dev/null @@ -1 +0,0 @@ -// Empty. diff --git a/Tests/XcodeTests/UIKitProject/UIKitProject/FileInGroupWithoutFolder.xib b/Tests/XcodeTests/UIKitProject/UIKitProject/FileInGroupWithoutFolder.xib new file mode 100644 index 0000000000..a6037d00f1 --- /dev/null +++ b/Tests/XcodeTests/UIKitProject/UIKitProject/FileInGroupWithoutFolder.xib @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Tests/XcodeTests/UIKitProjectTest.swift b/Tests/XcodeTests/UIKitProjectTest.swift index e295a293a9..3e0638f6cc 100644 --- a/Tests/XcodeTests/UIKitProjectTest.swift +++ b/Tests/XcodeTests/UIKitProjectTest.swift @@ -8,8 +8,6 @@ class UIKitProjectTest: SourceGraphTestCase { configuration.project = UIKitProjectPath.string configuration.schemes = ["UIKitProject"] - configuration.targets = ["UIKitProject", "NotificationServiceExtension", "WatchWidgetExtension", - "UIKitProjectTests", "LocalPackage.LocalPackageTarget"] build(driver: XcodeProjectDriver.self) index() diff --git a/Tests/XcodeTests/XcodeTargetTest.swift b/Tests/XcodeTests/XcodeTargetTest.swift index 7c40f3baba..b53850985b 100644 --- a/Tests/XcodeTests/XcodeTargetTest.swift +++ b/Tests/XcodeTests/XcodeTargetTest.swift @@ -9,8 +9,8 @@ class XcodeTargetTest: XCTestCase { let target = project.targets.first { $0.name == "UIKitProject" }! try target.identifyFiles() - XCTAssertTrue(target.files(kind: .swift).contains { - $0.relativeTo(ProjectRootPath).string == "Tests/XcodeTests/UIKitProject/UIKitProject/FileInGroupWithoutFolder.swift" + XCTAssertTrue(target.files(kind: .interfaceBuilder).contains { + $0.relativeTo(ProjectRootPath).string == "Tests/XcodeTests/UIKitProject/UIKitProject/FileInGroupWithoutFolder.xib" }) }