├── .flake8 ├── .github └── workflows │ ├── automerge.yml │ ├── publish_release.yml │ └── pull_request.yml ├── .gitignore ├── .license_header_template ├── .licenseignore ├── .pre-commit-hooks.yaml ├── .spi.yml ├── .swift-format ├── .swiftci ├── 5_10_ubuntu2204 ├── 5_7_ubuntu2204 ├── 5_8_ubuntu2204 ├── 5_9_ubuntu2204 ├── nightly_6_0_macos ├── nightly_6_0_ubuntu2204 ├── nightly_main_macos ├── nightly_main_ubuntu2204 └── nightly_main_windows ├── CMakeLists.txt ├── CODEOWNERS ├── Documentation ├── Configuration.md ├── Development.md ├── IgnoringSource.md ├── PrettyPrinter.md └── RuleDocumentation.md ├── LICENSE.txt ├── Package.swift ├── Plugins ├── FormatPlugin │ └── plugin.swift └── LintPlugin │ └── plugin.swift ├── README.md ├── Scripts └── format-diff.sh ├── Sources ├── CMakeLists.txt ├── SwiftFormat │ ├── API │ │ ├── Configuration+Default.swift │ │ ├── Configuration+Dump.swift │ │ ├── Configuration.swift │ │ ├── DebugOptions.swift │ │ ├── Finding.swift │ │ ├── FindingCategorizing.swift │ │ ├── Indent.swift │ │ ├── Selection.swift │ │ ├── SwiftFormatError.swift │ │ ├── SwiftFormatter.swift │ │ └── SwiftLinter.swift │ ├── CMakeLists.txt │ ├── Core │ │ ├── Context.swift │ │ ├── DocumentationComment.swift │ │ ├── DocumentationCommentText.swift │ │ ├── Finding+Convenience.swift │ │ ├── FindingEmitter.swift │ │ ├── FormatPipeline.swift │ │ ├── FunctionDeclSyntax+Convenience.swift │ │ ├── ImportsXCTestVisitor.swift │ │ ├── LazySplitSequence.swift │ │ ├── LintPipeline.swift │ │ ├── ModifierListSyntax+Convenience.swift │ │ ├── Parsing.swift │ │ ├── Pipelines+Generated.swift │ │ ├── RememberingIterator.swift │ │ ├── Rule.swift │ │ ├── RuleBasedFindingCategory.swift │ │ ├── RuleMask.swift │ │ ├── RuleNameCache+Generated.swift │ │ ├── RuleRegistry+Generated.swift │ │ ├── RuleState.swift │ │ ├── SyntaxFormatRule.swift │ │ ├── SyntaxLintRule.swift │ │ ├── SyntaxProtocol+Convenience.swift │ │ ├── SyntaxTraits.swift │ │ ├── Trivia+Convenience.swift │ │ ├── WithAttributesSyntax+Convenience.swift │ │ └── WithSemicolonSyntax.swift │ ├── PrettyPrint │ │ ├── Comment.swift │ │ ├── Indent+Length.swift │ │ ├── PrettyPrint.swift │ │ ├── PrettyPrintBuffer.swift │ │ ├── PrettyPrintFindingCategory.swift │ │ ├── Token.swift │ │ ├── TokenStreamCreator.swift │ │ ├── Verbatim.swift │ │ ├── WhitespaceFindingCategory.swift │ │ └── WhitespaceLinter.swift │ ├── Rules │ │ ├── AllPublicDeclarationsHaveDocumentation.swift │ │ ├── AlwaysUseLiteralForEmptyCollectionInit.swift │ │ ├── AlwaysUseLowerCamelCase.swift │ │ ├── AmbiguousTrailingClosureOverload.swift │ │ ├── AvoidRetroactiveConformances.swift │ │ ├── BeginDocumentationCommentWithOneLineSummary.swift │ │ ├── DoNotUseSemicolons.swift │ │ ├── DontRepeatTypeInStaticProperties.swift │ │ ├── FileScopedDeclarationPrivacy.swift │ │ ├── FullyIndirectEnum.swift │ │ ├── GroupNumericLiterals.swift │ │ ├── IdentifiersMustBeASCII.swift │ │ ├── NeverForceUnwrap.swift │ │ ├── NeverUseForceTry.swift │ │ ├── NeverUseImplicitlyUnwrappedOptionals.swift │ │ ├── NoAccessLevelOnExtensionDeclaration.swift │ │ ├── NoAssignmentInExpressions.swift │ │ ├── NoBlockComments.swift │ │ ├── NoCasesWithOnlyFallthrough.swift │ │ ├── NoEmptyLineOpeningClosingBraces.swift │ │ ├── NoEmptyTrailingClosureParentheses.swift │ │ ├── NoLabelsInCasePatterns.swift │ │ ├── NoLeadingUnderscores.swift │ │ ├── NoParensAroundConditions.swift │ │ ├── NoPlaygroundLiterals.swift │ │ ├── NoVoidReturnOnFunctionSignature.swift │ │ ├── OmitExplicitReturns.swift │ │ ├── OneCasePerLine.swift │ │ ├── OneVariableDeclarationPerLine.swift │ │ ├── OnlyOneTrailingClosureArgument.swift │ │ ├── OrderedImports.swift │ │ ├── ReplaceForEachWithForLoop.swift │ │ ├── ReturnVoidInsteadOfEmptyTuple.swift │ │ ├── TypeNamesShouldBeCapitalized.swift │ │ ├── UseEarlyExits.swift │ │ ├── UseExplicitNilCheckInConditions.swift │ │ ├── UseLetInEveryBoundCaseVariable.swift │ │ ├── UseShorthandTypeNames.swift │ │ ├── UseSingleLinePropertyGetter.swift │ │ ├── UseSynthesizedInitializer.swift │ │ ├── UseTripleSlashForDocumentationComments.swift │ │ ├── UseWhereClausesInForLoops.swift │ │ └── ValidateDocumentationComments.swift │ └── Utilities │ │ ├── FileIterator.swift │ │ └── URL+isRoot.swift ├── _SwiftFormatInstructionCounter │ ├── CMakeLists.txt │ ├── include │ │ ├── InstructionsExecuted.h │ │ └── module.modulemap │ └── src │ │ └── InstructionsExecuted.c ├── _SwiftFormatTestSupport │ ├── Configuration+Testing.swift │ ├── DiagnosingTestCase.swift │ ├── FindingSpec.swift │ ├── MarkedText.swift │ └── Parsing.swift ├── generate-swift-format │ ├── FileGenerator.swift │ ├── PipelineGenerator.swift │ ├── RuleCollector.swift │ ├── RuleDocumentationGenerator.swift │ ├── RuleNameCacheGenerator.swift │ ├── RuleRegistryGenerator.swift │ ├── Syntax+Convenience.swift │ └── main.swift └── swift-format │ ├── CMakeLists.txt │ ├── Frontend │ ├── ConfigurationLoader.swift │ ├── FormatFrontend.swift │ ├── Frontend.swift │ └── LintFrontend.swift │ ├── PrintVersion.swift │ ├── Subcommands │ ├── ConfigurationOptions.swift │ ├── DumpConfiguration.swift │ ├── Format.swift │ ├── Lint.swift │ ├── LintFormatOptions.swift │ └── PerformanceMeasurement.swift │ ├── SwiftFormatCommand.swift │ ├── Utilities │ ├── Diagnostic.swift │ ├── DiagnosticsEngine.swift │ ├── FileHandleTextOutputStream.swift │ ├── StderrDiagnosticPrinter.swift │ └── TTY.swift │ └── VersionOptions.swift ├── Tests ├── SwiftFormatPerformanceTests │ └── WhitespaceLinterPerformanceTests.swift └── SwiftFormatTests │ ├── API │ └── ConfigurationTests.swift │ ├── Core │ ├── DocumentationCommentTests.swift │ ├── DocumentationCommentTextTests.swift │ └── RuleMaskTests.swift │ ├── PrettyPrint │ ├── AccessorTests.swift │ ├── ArrayDeclTests.swift │ ├── AsExprTests.swift │ ├── AssignmentExprTests.swift │ ├── AttributeTests.swift │ ├── AvailabilityConditionTests.swift │ ├── BackDeployAttributeTests.swift │ ├── BacktickTests.swift │ ├── BinaryOperatorExprTests.swift │ ├── ClassDeclTests.swift │ ├── ClosureExprTests.swift │ ├── CommaTests.swift │ ├── CommentTests.swift │ ├── ConstrainedSugarTypeTests.swift │ ├── ConsumeExprTests.swift │ ├── CopyExprSyntax.swift │ ├── DeclNameArgumentTests.swift │ ├── DeinitializerDeclTests.swift │ ├── DictionaryDeclTests.swift │ ├── DifferentiationAttributeTests.swift │ ├── DiscardStmtTests.swift │ ├── DoStmtTests.swift │ ├── EnumDeclTests.swift │ ├── ExpressionModifierTests.swift │ ├── ExtensionDeclTests.swift │ ├── ForInStmtTests.swift │ ├── FunctionCallTests.swift │ ├── FunctionDeclTests.swift │ ├── FunctionTypeTests.swift │ ├── GarbageTextTests.swift │ ├── GuardStmtTests.swift │ ├── IfConfigTests.swift │ ├── IfStmtTests.swift │ ├── IgnoreNodeTests.swift │ ├── ImportTests.swift │ ├── IndentBlankLinesTests.swift │ ├── InitializerDeclTests.swift │ ├── KeyPathExprTests.swift │ ├── LineNumbersTests.swift │ ├── MacroCallTests.swift │ ├── MacroDeclTests.swift │ ├── MemberAccessExprTests.swift │ ├── MemberTypeIdentifierTests.swift │ ├── NewlineTests.swift │ ├── ObjectLiteralExprTests.swift │ ├── OperatorDeclTests.swift │ ├── ParameterPackTests.swift │ ├── ParenthesizedExprTests.swift │ ├── PatternBindingTests.swift │ ├── PrettyPrintTestCase.swift │ ├── ProtocolDeclTests.swift │ ├── RepeatStmtTests.swift │ ├── RespectsExistingLineBreaksTests.swift │ ├── SelectionTests.swift │ ├── SemicolonTests.swift │ ├── StringTests.swift │ ├── StructDeclTests.swift │ ├── SubscriptDeclTests.swift │ ├── SubscriptExprTests.swift │ ├── SwitchCaseIndentConfigTests.swift │ ├── SwitchStmtTests.swift │ ├── TernaryExprTests.swift │ ├── TupleDeclTests.swift │ ├── TypeAliasTests.swift │ ├── ValueGenericsTests.swift │ ├── VariableDeclTests.swift │ ├── WhileStmtTests.swift │ ├── WhitespaceLintTests.swift │ ├── WhitespaceTestCase.swift │ └── YieldStmtTests.swift │ ├── Rules │ ├── AllPublicDeclarationsHaveDocumentationTests.swift │ ├── AlwaysUseLiteralForEmptyCollectionInitTests.swift │ ├── AlwaysUseLowerCamelCaseTests.swift │ ├── AmbiguousTrailingClosureOverloadTests.swift │ ├── AvoidRetroactiveConformancesTests.swift │ ├── BeginDocumentationCommentWithOneLineSummaryTests.swift │ ├── DoNotUseSemicolonsTests.swift │ ├── DontRepeatTypeInStaticPropertiesTests.swift │ ├── FileScopedDeclarationPrivacyTests.swift │ ├── FullyIndirectEnumTests.swift │ ├── GroupNumericLiteralsTests.swift │ ├── IdentifiersMustBeASCIITests.swift │ ├── ImportsXCTestVisitorTests.swift │ ├── LintOrFormatRuleTestCase.swift │ ├── NeverForceUnwrapTests.swift │ ├── NeverUseForceTryTests.swift │ ├── NeverUseImplicitlyUnwrappedOptionalsTests.swift │ ├── NoAccessLevelOnExtensionDeclarationTests.swift │ ├── NoAssignmentInExpressionsTests.swift │ ├── NoBlockCommentsTests.swift │ ├── NoCasesWithOnlyFallthroughTests.swift │ ├── NoEmptyLinesOpeningClosingBracesTests.swift │ ├── NoEmptyTrailingClosureParenthesesTests.swift │ ├── NoLabelsInCasePatternsTests.swift │ ├── NoLeadingUnderscoresTests.swift │ ├── NoParensAroundConditionsTests.swift │ ├── NoPlaygroundLiteralsTests.swift │ ├── NoVoidReturnOnFunctionSignatureTests.swift │ ├── OmitReturnsTests.swift │ ├── OneCasePerLineTests.swift │ ├── OneVariableDeclarationPerLineTests.swift │ ├── OnlyOneTrailingClosureArgumentTests.swift │ ├── OrderedImportsTests.swift │ ├── ReplaceForEachWithForLoopTests.swift │ ├── ReturnVoidInsteadOfEmptyTupleTests.swift │ ├── TypeNamesShouldBeCapitalizedTests.swift │ ├── UseEarlyExitsTests.swift │ ├── UseExplicitNilCheckInConditionsTests.swift │ ├── UseLetInEveryBoundCaseVariableTests.swift │ ├── UseShorthandTypeNamesTests.swift │ ├── UseSingleLinePropertyGetterTests.swift │ ├── UseSynthesizedInitializerTests.swift │ ├── UseTripleSlashForDocumentationCommentsTests.swift │ ├── UseWhereClausesInForLoopsTests.swift │ └── ValidateDocumentationCommentsTests.swift │ └── Utilities │ └── FileIteratorTests.swift ├── api-breakages.txt ├── build-script-helper.py └── cmake └── modules ├── CMakeLists.txt ├── SwiftFormatConfig.cmake.in └── SwiftSupport.cmake /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | 3 | # Match the maximum line length in Swift files. 4 | max-line-length = 120 5 | 6 | # E265, E266 and conflict with the header expected by the license header check 7 | ignore = E265, E266 8 | -------------------------------------------------------------------------------- /.github/workflows/automerge.yml: -------------------------------------------------------------------------------- 1 | name: Create PR to merge main into release branch 2 | # In the first period after branching the release branch, we typically want to include all changes from `main` also in the release branch. This workflow automatically creates a PR every Monday to merge main into the release branch. 3 | # Later in the release cycle we should stop this practice to avoid landing risky changes by disabling this workflow. To do so, disable the workflow as described in https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/disabling-and-enabling-a-workflow 4 | on: 5 | schedule: 6 | - cron: '0 9 * * MON' 7 | workflow_dispatch: 8 | jobs: 9 | create_merge_pr: 10 | name: Create PR to merge main into release branch 11 | uses: swiftlang/github-workflows/.github/workflows/create_automerge_pr.yml@main 12 | with: 13 | base_branch: release/6.2 14 | permissions: 15 | contents: write 16 | pull-requests: write 17 | if: (github.event_name == 'schedule' && github.repository == 'swiftlang/swift-format') || (github.event_name != 'schedule') # Ensure that we don't run this on a schedule in a fork 18 | -------------------------------------------------------------------------------- /.github/workflows/pull_request.yml: -------------------------------------------------------------------------------- 1 | name: Pull request 2 | 3 | # PRs created by GitHub Actions don't kick off further actions (https://github.com/peter-evans/create-pull-request/blob/d57e551ebc1a16dee0b8c9ea6d24dba7627a6e35/docs/concepts-guidelines.md#triggering-further-workflow-runs). 4 | # As a workaround, we mark automerge PRs that are created by GitHub actions as draft and trigger the GitHub actions by marking the PR as ready for review. We'd prefer not re-triggering testing on a normal user's PR in this case, but skipping them causes the checks to reset. 5 | 6 | on: 7 | pull_request: 8 | types: [opened, reopened, synchronize, ready_for_review] 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | tests: 16 | name: Test 17 | uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main 18 | with: 19 | linux_exclude_swift_versions: "[{\"swift_version\": \"5.8\"}]" 20 | soundness: 21 | name: Soundness 22 | uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main 23 | with: 24 | license_header_check_project_name: "Swift.org" 25 | api_breakage_check_allowlist_path: "api-breakages.txt" 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .build/ 3 | .swiftpm/ 4 | swift-format.xcodeproj/ 5 | Package.resolved 6 | /.vscode 7 | .index-build 8 | -------------------------------------------------------------------------------- /.license_header_template: -------------------------------------------------------------------------------- 1 | @@===----------------------------------------------------------------------===@@ 2 | @@ 3 | @@ This source file is part of the Swift.org open source project 4 | @@ 5 | @@ Copyright (c) YEARS Apple Inc. and the Swift project authors 6 | @@ Licensed under Apache License v2.0 with Runtime Library Exception 7 | @@ 8 | @@ See https://swift.org/LICENSE.txt for license information 9 | @@ See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | @@ 11 | @@===----------------------------------------------------------------------===@@ 12 | -------------------------------------------------------------------------------- /.licenseignore: -------------------------------------------------------------------------------- 1 | .flake8 2 | .gitignore 3 | .swiftci 4 | *.md 5 | *.txt 6 | *.yaml 7 | *.yml 8 | **/*.docc/** 9 | **/*.modulemap 10 | CODEOWNERS 11 | Package.swift 12 | -------------------------------------------------------------------------------- /.pre-commit-hooks.yaml: -------------------------------------------------------------------------------- 1 | - id: swift-format 2 | name: swift-format 3 | entry: swift-format format --in-place --recursive --parallel 4 | language: swift 5 | types: [swift] 6 | require_serial: true 7 | - id: swift-format-lint 8 | name: swift-format-lint 9 | entry: swift-format lint --strict --recursive --parallel 10 | language: swift 11 | types: [swift] 12 | require_serial: true 13 | -------------------------------------------------------------------------------- /.spi.yml: -------------------------------------------------------------------------------- 1 | # This is manifest file for the Swift Package Index for it to 2 | # auto-generate and host DocC documentation. 3 | # For reference see https://swiftpackageindex.com/swiftpackageindex/spimanifest/documentation/spimanifest/commonusecases#Host-DocC-documentation-in-the-Swift-Package-Index. 4 | 5 | version: 1 6 | builder: 7 | configs: 8 | - documentation_targets: 9 | # First item in the list is the "landing" (default) target 10 | - SwiftFormat 11 | custom_documentation_parameters: [--experimental-skip-synthesized-symbols] 12 | -------------------------------------------------------------------------------- /.swift-format: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "lineLength": 120, 4 | "indentation": { 5 | "spaces": 2 6 | }, 7 | "lineBreakBeforeEachArgument": true, 8 | "indentConditionalCompilationBlocks": false, 9 | "prioritizeKeepingFunctionOutputTogether": true, 10 | "rules": { 11 | "AlwaysUseLowerCamelCase": false, 12 | "AmbiguousTrailingClosureOverload": false, 13 | "NoBlockComments": false, 14 | "OrderedImports": true, 15 | "UseLetInEveryBoundCaseVariable": false, 16 | "UseSynthesizedInitializer": false 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.swiftci/5_10_ubuntu2204: -------------------------------------------------------------------------------- 1 | LinuxSwiftPackageJob { 2 | swift_version_tag = "5.10-jammy" 3 | repo = "swift-format" 4 | branch = "main" 5 | } 6 | -------------------------------------------------------------------------------- /.swiftci/5_7_ubuntu2204: -------------------------------------------------------------------------------- 1 | LinuxSwiftPackageJob { 2 | swift_version_tag = "5.7-jammy" 3 | repo = "swift-format" 4 | branch = "main" 5 | } 6 | -------------------------------------------------------------------------------- /.swiftci/5_8_ubuntu2204: -------------------------------------------------------------------------------- 1 | LinuxSwiftPackageJob { 2 | swift_version_tag = "5.8-jammy" 3 | repo = "swift-format" 4 | branch = "main" 5 | } 6 | -------------------------------------------------------------------------------- /.swiftci/5_9_ubuntu2204: -------------------------------------------------------------------------------- 1 | LinuxSwiftPackageJob { 2 | swift_version_tag = "5.9-jammy" 3 | repo = "swift-format" 4 | branch = "main" 5 | } 6 | -------------------------------------------------------------------------------- /.swiftci/nightly_6_0_macos: -------------------------------------------------------------------------------- 1 | macOSSwiftPackageJob { 2 | swift_version = "6.0" 3 | repo = "swift-format" 4 | branch = "main" 5 | } 6 | -------------------------------------------------------------------------------- /.swiftci/nightly_6_0_ubuntu2204: -------------------------------------------------------------------------------- 1 | LinuxSwiftPackageJob { 2 | nightly_docker_tag = "nightly-6.0-jammy" 3 | repo = "swift-format" 4 | branch = "main" 5 | } 6 | -------------------------------------------------------------------------------- /.swiftci/nightly_main_macos: -------------------------------------------------------------------------------- 1 | macOSSwiftPackageJob { 2 | swift_version = "main" 3 | repo = "swift-format" 4 | branch = "main" 5 | } 6 | -------------------------------------------------------------------------------- /.swiftci/nightly_main_ubuntu2204: -------------------------------------------------------------------------------- 1 | LinuxSwiftPackageJob { 2 | nightly_docker_tag = "nightly-jammy" 3 | repo = "swift-format" 4 | branch = "main" 5 | } 6 | -------------------------------------------------------------------------------- /.swiftci/nightly_main_windows: -------------------------------------------------------------------------------- 1 | WindowsSwiftPackageWithDockerImageJob { 2 | docker_image = "swiftlang/swift:nightly-windowsservercore-1809" 3 | repo = "swift-format" 4 | branch = "main" 5 | sub_dir = "swift-format" 6 | label = "windows-server-2019" 7 | } 8 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #[[ 2 | This source file is part of the swift-format open source project 3 | 4 | Copyright (c) 2024 Apple Inc. and the swift-format project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See https://swift.org/LICENSE.txt for license information 8 | #]] 9 | 10 | cmake_minimum_required(VERSION 3.19.0) 11 | 12 | if(POLICY CMP0077) 13 | cmake_policy(SET CMP0077 NEW) 14 | endif() 15 | if(POLICY CMP0091) 16 | cmake_policy(SET CMP0091 NEW) 17 | endif() 18 | 19 | project(SwiftFormat 20 | LANGUAGES C Swift) 21 | 22 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 23 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) 24 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) 25 | 26 | set(CMAKE_Swift_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/swift) 27 | set(CMAKE_Swift_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY MultiThreadedDLL) 28 | 29 | list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/modules) 30 | 31 | include(FetchContent) 32 | include(GNUInstallDirs) 33 | include(SwiftSupport) 34 | 35 | find_package(Foundation CONFIG) 36 | 37 | set(_SF_VENDOR_DEPENDENCIES) 38 | 39 | set(BUILD_EXAMPLES NO) 40 | set(BUILD_TESTING NO) 41 | 42 | find_package(ArgumentParser CONFIG) 43 | if(NOT ArgumentParser_FOUND) 44 | FetchContent_Declare(ArgumentParser 45 | GIT_REPOSITORY https://github.com/apple/swift-argument-parser 46 | GIT_TAG 1.2.3) 47 | list(APPEND _SF_VENDOR_DEPENDENCIES ArgumentParser) 48 | endif() 49 | 50 | find_package(cmark-gfm CONFIG) 51 | if(NOT cmark-gfm_FOUND) 52 | FetchContent_Declare(cmark-gfm 53 | GIT_REPOSITORY https://github.com/apple/swift-cmark 54 | GIT_TAG gfm) 55 | list(APPEND _SF_VENDOR_DEPENDENCIES cmark-gfm) 56 | endif() 57 | 58 | find_package(SwiftMarkdown CONFIG) 59 | if(NOT SwiftMarkdown_FOUND) 60 | # TODO(compnerd) we need a latest version for now as we need the CMake support 61 | # which is untagged. 62 | FetchContent_Declare(Markdown 63 | GIT_REPOSITORY https://github.com/apple/swift-markdown 64 | GIT_TAG main) 65 | list(APPEND _SF_VENDOR_DEPENDENCIES Markdown) 66 | endif() 67 | 68 | find_package(SwiftSyntax CONFIG) 69 | if(NOT SwiftSyntax_FOUND) 70 | FetchContent_Declare(Syntax 71 | GIT_REPOSITORY https://github.com/swiftlang/swift-syntax 72 | GIT_TAG main) 73 | list(APPEND _SF_VENDOR_DEPENDENCIES Syntax) 74 | endif() 75 | 76 | if(_SF_VENDOR_DEPENDENCIES) 77 | FetchContent_MakeAvailable(${_SF_VENDOR_DEPENDENCIES}) 78 | 79 | if(NOT TARGET SwiftMarkdown::Markdown) 80 | add_library(SwiftMarkdown::Markdown ALIAS Markdown) 81 | endif() 82 | 83 | if(NOT TARGET SwiftSyntax::SwiftSyntax) 84 | add_library(SwiftSyntax::SwiftSyntax ALIAS SwiftSyntax) 85 | add_library(SwiftSyntax::SwiftSyntaxBuilder ALIAS SwiftSyntaxBuilder) 86 | add_library(SwiftSyntax::SwiftOperators ALIAS SwiftOperators) 87 | add_library(SwiftSyntax::SwiftParser ALIAS SwiftParser) 88 | add_library(SwiftSyntax::SwiftParserDiagnostics ALIAS SwiftParserDiagnostics) 89 | endif() 90 | endif() 91 | 92 | add_subdirectory(Sources) 93 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # 2 | # This source file is part of the Swift.org open source project 3 | # 4 | # Copyright (c) 2024 Apple Inc. and the Swift project authors 5 | # Licensed under Apache License v2.0 with Runtime Library Exception 6 | # 7 | # See https://swift.org/LICENSE.txt for license information 8 | # See https://swift.org/CONTRIBUTORS.txt for Swift project authors 9 | # 10 | 11 | * @allevato @bnbarham @hamishknight @rintaro 12 | 13 | .github/ @bnbarham @shahmishal 14 | .swiftci/ @bnbarham @shahmishal 15 | -------------------------------------------------------------------------------- /Documentation/Development.md: -------------------------------------------------------------------------------- 1 | # Developing `swift-format` 2 | 3 | ## Keeping the Pipeline and Tests Updated 4 | 5 | Since Swift does not yet have a runtime reflection system, we use code 6 | generation to keep the linting/formatting pipeline up-to-date. If you add or 7 | remove any rules from the `SwiftFormat` module, or if you add or remove 8 | any `visit` methods from an existing rule in that module, you must run the 9 | `generate-swift-format` tool update the pipeline and configuration sources. 10 | 11 | The easiest way to do this is to run the following command in your terminal: 12 | 13 | ```shell 14 | swift run generate-swift-format 15 | ``` 16 | 17 | If successful, this tool will update the files `Pipelines+Generated.swift`, 18 | `RuleNameCache+Generated.swift`, and `RuleRegistry+Generated.swift` in 19 | the `Sources/SwiftFormat/Core` directory. 20 | 21 | ## Command Line Options for Debugging 22 | 23 | `swift-format` provides some hidden command line options to facilitate 24 | debugging the tool during development: 25 | 26 | * `--debug-disable-pretty-print`: Disables the pretty-printing pass of the 27 | formatter, causing only the syntax tree transformations in the first phase 28 | pipeline to run. 29 | 30 | * `--debug-dump-token-stream`: Dumps a human-readable indented structure 31 | representing the pseudotoken stream constructed by the pretty printing 32 | phase. 33 | 34 | ## Support Scripts 35 | 36 | The [Scripts](../Scripts) directory contains a `format-diff.sh` script 37 | that some developers may find useful. When invoked, it rebuilds 38 | `swift-format` (if necessary to pick up any recent changes) and lets 39 | you view a side-by-side `diff` with the original file on the left side 40 | and the formatted output on the right side. 41 | 42 | This script will use `colordiff` if it is installed on your `PATH`; 43 | otherwise, it will fall back to `diff`. 44 | -------------------------------------------------------------------------------- /Plugins/FormatPlugin/plugin.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import Foundation 14 | import PackagePlugin 15 | 16 | @main 17 | struct FormatPlugin { 18 | func format(tool: PluginContext.Tool, targetDirectories: [String], configurationFilePath: String?) throws { 19 | let swiftFormatExec = URL(fileURLWithPath: tool.path.string) 20 | 21 | var arguments: [String] = ["format"] 22 | 23 | arguments.append(contentsOf: targetDirectories) 24 | 25 | arguments.append(contentsOf: ["--recursive", "--parallel", "--in-place"]) 26 | 27 | if let configurationFilePath = configurationFilePath { 28 | arguments.append(contentsOf: ["--configuration", configurationFilePath]) 29 | } 30 | 31 | let process = try Process.run(swiftFormatExec, arguments: arguments) 32 | process.waitUntilExit() 33 | 34 | if process.terminationReason == .exit && process.terminationStatus == 0 { 35 | print("Formatted the source code.") 36 | } else { 37 | let problem = "\(process.terminationReason):\(process.terminationStatus)" 38 | Diagnostics.error("swift-format invocation failed: \(problem)") 39 | } 40 | } 41 | } 42 | 43 | extension FormatPlugin: CommandPlugin { 44 | func performCommand( 45 | context: PluginContext, 46 | arguments: [String] 47 | ) async throws { 48 | let swiftFormatTool = try context.tool(named: "swift-format") 49 | 50 | var argExtractor = ArgumentExtractor(arguments) 51 | let targetNames = argExtractor.extractOption(named: "target") 52 | let targetsToFormat = 53 | targetNames.isEmpty ? context.package.targets : try context.package.targets(named: targetNames) 54 | 55 | let configurationFilePath = argExtractor.extractOption(named: "swift-format-configuration").first 56 | 57 | let sourceCodeTargets = targetsToFormat.compactMap { $0 as? SourceModuleTarget } 58 | 59 | try format( 60 | tool: swiftFormatTool, 61 | targetDirectories: sourceCodeTargets.map(\.directory.string), 62 | configurationFilePath: configurationFilePath 63 | ) 64 | } 65 | } 66 | 67 | #if canImport(XcodeProjectPlugin) 68 | import XcodeProjectPlugin 69 | 70 | extension FormatPlugin: XcodeCommandPlugin { 71 | func performCommand(context: XcodeProjectPlugin.XcodePluginContext, arguments: [String]) throws { 72 | let swiftFormatTool = try context.tool(named: "swift-format") 73 | 74 | var argExtractor = ArgumentExtractor(arguments) 75 | let configurationFilePath = argExtractor.extractOption(named: "swift-format-configuration").first 76 | 77 | try format( 78 | tool: swiftFormatTool, 79 | targetDirectories: [context.xcodeProject.directory.string], 80 | configurationFilePath: configurationFilePath 81 | ) 82 | } 83 | } 84 | #endif 85 | -------------------------------------------------------------------------------- /Plugins/LintPlugin/plugin.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import Foundation 14 | import PackagePlugin 15 | 16 | @main 17 | struct LintPlugin { 18 | func lint(tool: PluginContext.Tool, targetDirectories: [String], configurationFilePath: String?) throws { 19 | let swiftFormatExec = URL(fileURLWithPath: tool.path.string) 20 | 21 | var arguments: [String] = ["lint"] 22 | 23 | arguments.append(contentsOf: targetDirectories) 24 | 25 | arguments.append(contentsOf: ["--recursive", "--parallel", "--strict"]) 26 | 27 | if let configurationFilePath = configurationFilePath { 28 | arguments.append(contentsOf: ["--configuration", configurationFilePath]) 29 | } 30 | 31 | let process = try Process.run(swiftFormatExec, arguments: arguments) 32 | process.waitUntilExit() 33 | 34 | if process.terminationReason == .exit && process.terminationStatus == 0 { 35 | print("Linted the source code.") 36 | } else { 37 | let problem = "\(process.terminationReason):\(process.terminationStatus)" 38 | Diagnostics.error("swift-format invocation failed: \(problem)") 39 | } 40 | } 41 | } 42 | 43 | extension LintPlugin: CommandPlugin { 44 | func performCommand( 45 | context: PluginContext, 46 | arguments: [String] 47 | ) async throws { 48 | let swiftFormatTool = try context.tool(named: "swift-format") 49 | 50 | // Extract the arguments that specify what targets to format. 51 | var argExtractor = ArgumentExtractor(arguments) 52 | let targetNames = argExtractor.extractOption(named: "target") 53 | 54 | let targetsToFormat = 55 | targetNames.isEmpty ? context.package.targets : try context.package.targets(named: targetNames) 56 | let configurationFilePath = argExtractor.extractOption(named: "swift-format-configuration").first 57 | 58 | let sourceCodeTargets = targetsToFormat.compactMap { $0 as? SourceModuleTarget } 59 | 60 | try lint( 61 | tool: swiftFormatTool, 62 | targetDirectories: sourceCodeTargets.map(\.directory.string), 63 | configurationFilePath: configurationFilePath 64 | ) 65 | } 66 | } 67 | 68 | #if canImport(XcodeProjectPlugin) 69 | import XcodeProjectPlugin 70 | 71 | extension LintPlugin: XcodeCommandPlugin { 72 | func performCommand(context: XcodeProjectPlugin.XcodePluginContext, arguments: [String]) throws { 73 | let swiftFormatTool = try context.tool(named: "swift-format") 74 | var argExtractor = ArgumentExtractor(arguments) 75 | let configurationFilePath = argExtractor.extractOption(named: "swift-format-configuration").first 76 | 77 | try lint( 78 | tool: swiftFormatTool, 79 | targetDirectories: [context.xcodeProject.directory.string], 80 | configurationFilePath: configurationFilePath 81 | ) 82 | } 83 | } 84 | #endif 85 | -------------------------------------------------------------------------------- /Scripts/format-diff.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ##===----------------------------------------------------------------------===## 3 | ## 4 | ## This source file is part of the Swift.org open source project 5 | ## 6 | ## Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 7 | ## Licensed under Apache License v2.0 with Runtime Library Exception 8 | ## 9 | ## See https://swift.org/LICENSE.txt for license information 10 | ## See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 11 | ## 12 | ##===----------------------------------------------------------------------===## 13 | 14 | # SYNOPSIS 15 | # format-diff.sh FILE [OPTION]... 16 | # 17 | # DESCRIPTION 18 | # Runs the formatter and displays a side-by-side diff of the original file 19 | # and the formatted results. The script will use `colordiff` for the output 20 | # if it is present; otherwise, regular `diff` will be used. 21 | # 22 | # The first argument to this script must be the `.swift` source file to be 23 | # formatted. Any remaining arguments after that will be passed directly to 24 | # `swift-format`. 25 | 26 | set -euo pipefail 27 | 28 | SRCFILE="$1" ; shift 29 | 30 | # Use `colordiff` if it's present; otherwise, fall back to `diff`. 31 | if which colordiff >/dev/null ; then 32 | DIFF="$(which colordiff)" 33 | else 34 | DIFF="$(which diff)" 35 | fi 36 | 37 | # Make sure the formatter is built in debug mode so we can reference the 38 | # executable easily. 39 | swift build --product swift-format 40 | 41 | # Run a side-by-side diff with the original source file on the left and the 42 | # formatted output on the right. 43 | "$DIFF" -y -W 210 "$SRCFILE" <(.build/debug/swift-format "$@" "$SRCFILE") 44 | -------------------------------------------------------------------------------- /Sources/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #[[ 2 | This source file is part of the swift-format open source project 3 | 4 | Copyright (c) 2024 Apple Inc. and the swift-format project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See https://swift.org/LICENSE.txt for license information 8 | #]] 9 | 10 | add_subdirectory(SwiftFormat) 11 | add_subdirectory(_SwiftFormatInstructionCounter) 12 | add_subdirectory(swift-format) 13 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/API/Configuration+Default.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | extension Configuration { 14 | /// Creates a new `Configuration` with default values. 15 | /// 16 | /// This initializer is isolated to its own file to make it easier for users who are forking or 17 | /// building swift-format themselves to hardcode a different default configuration. To do this, 18 | /// simply replace this file with your own default initializer that sets the values to whatever 19 | /// you want. 20 | /// 21 | /// When swift-format reads a configuration file from disk, any values that are not specified in 22 | /// the JSON will be populated from this default configuration. 23 | public init() { 24 | self.rules = Self.defaultRuleEnablements 25 | self.maximumBlankLines = 1 26 | self.lineLength = 100 27 | self.tabWidth = 8 28 | self.indentation = .spaces(2) 29 | self.spacesBeforeEndOfLineComments = 2 30 | self.respectsExistingLineBreaks = true 31 | self.lineBreakBeforeControlFlowKeywords = false 32 | self.lineBreakBeforeEachArgument = false 33 | self.lineBreakBeforeEachGenericRequirement = false 34 | self.lineBreakBetweenDeclarationAttributes = false 35 | self.prioritizeKeepingFunctionOutputTogether = false 36 | self.indentConditionalCompilationBlocks = true 37 | self.lineBreakAroundMultilineExpressionChainComponents = false 38 | self.fileScopedDeclarationPrivacy = FileScopedDeclarationPrivacyConfiguration() 39 | self.indentSwitchCaseLabels = false 40 | self.spacesAroundRangeFormationOperators = false 41 | self.noAssignmentInExpressions = NoAssignmentInExpressionsConfiguration() 42 | self.multiElementCollectionTrailingCommas = true 43 | self.reflowMultilineStringLiterals = .never 44 | self.indentBlankLines = false 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/API/Configuration+Dump.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import Foundation 14 | 15 | extension Configuration { 16 | /// Return the configuration as a JSON string. 17 | public func asJsonString() throws -> String { 18 | let data: Data 19 | 20 | do { 21 | let encoder = JSONEncoder() 22 | encoder.outputFormatting = [.prettyPrinted, .sortedKeys] 23 | data = try encoder.encode(self) 24 | } catch { 25 | throw SwiftFormatError.configurationDumpFailed("\(error)") 26 | } 27 | 28 | guard let jsonString = String(data: data, encoding: .utf8) else { 29 | // This should never happen, but let's make sure we fail more gracefully than crashing, just in case. 30 | throw SwiftFormatError.configurationDumpFailed("The JSON was not valid UTF-8") 31 | } 32 | 33 | return jsonString 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/API/DebugOptions.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | /// Advanced options that are useful when debugging and developing the formatter, but are otherwise 14 | /// not meant for general use. 15 | public struct DebugOptions: OptionSet { 16 | 17 | /// Disables the pretty-printer pass entirely, executing only the syntax-transforming rules in the 18 | /// pipeline. 19 | public static let disablePrettyPrint = DebugOptions(rawValue: 1 << 0) 20 | 21 | /// Dumps a verbose representation of the raw pretty-printer token stream. 22 | public static let dumpTokenStream = DebugOptions(rawValue: 1 << 1) 23 | 24 | public let rawValue: Int 25 | 26 | public init(rawValue: Int) { self.rawValue = rawValue } 27 | 28 | /// Inserts or removes the given element from the option set, based on the value of `enabled`. 29 | public mutating func set(_ element: Element, enabled: Bool) { 30 | if enabled { insert(element) } else { remove(element) } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/API/FindingCategorizing.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | /// Types that conform to this protocol can be used as the category of a finding. 14 | /// 15 | /// A finding's category should have a human-readable string representation (by overriding the 16 | /// `description` property from the inherited `CustomStringConvertible` conformance). This is meant 17 | /// to be displayed as part of the diagnostic message when the finding is presented to the user. 18 | /// For example, the category `Indentation` in the message `[Indentation] Indent by 2 spaces`. 19 | public protocol FindingCategorizing: CustomStringConvertible { 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/API/Indent.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | /// Represents an indentation unit that is applied to lines that are pretty-printed. 14 | public enum Indent: Hashable, Codable { 15 | 16 | /// An indentation unit equal to the given number of tab characters. 17 | /// 18 | /// This value is independent of the actual tab width, which is set separately in the 19 | /// `Configuration`. 20 | case tabs(Int) 21 | 22 | /// An indentation unit equal to the given number of spaces. 23 | case spaces(Int) 24 | 25 | private enum CodingKeys: CodingKey { 26 | case tabs 27 | case spaces 28 | } 29 | 30 | public init(from decoder: Decoder) throws { 31 | let container = try decoder.container(keyedBy: CodingKeys.self) 32 | let spacesCount = try container.decodeIfPresent(Int.self, forKey: .spaces) 33 | let tabsCount = try container.decodeIfPresent(Int.self, forKey: .tabs) 34 | 35 | if spacesCount != nil && tabsCount != nil { 36 | throw DecodingError.dataCorrupted( 37 | DecodingError.Context( 38 | codingPath: decoder.codingPath, 39 | debugDescription: "Only one of \"tabs\" or \"spaces\" may be specified" 40 | ) 41 | ) 42 | } 43 | if let spacesCount = spacesCount { 44 | self = .spaces(spacesCount) 45 | return 46 | } 47 | if let tabsCount = tabsCount { 48 | self = .tabs(tabsCount) 49 | return 50 | } 51 | 52 | throw DecodingError.dataCorrupted( 53 | DecodingError.Context( 54 | codingPath: decoder.codingPath, 55 | debugDescription: "One of \"tabs\" or \"spaces\" must be specified" 56 | ) 57 | ) 58 | } 59 | 60 | public func encode(to encoder: Encoder) throws { 61 | var container = encoder.container(keyedBy: CodingKeys.self) 62 | switch self { 63 | case .tabs(let count): 64 | try container.encode(count, forKey: .tabs) 65 | case .spaces(let count): 66 | try container.encode(count, forKey: .spaces) 67 | } 68 | } 69 | 70 | /// Returns the number of units (tabs or spaces) that this indent represents. 71 | /// 72 | /// Note that this does _not_ represent the physical number of spaces occupied by the indentation. 73 | public var count: Int { 74 | switch self { 75 | case .spaces(let count): 76 | return count 77 | case .tabs(let count): 78 | return count 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/API/Selection.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2024 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import Foundation 14 | import SwiftSyntax 15 | 16 | /// The selection as given on the command line - an array of offets and lengths 17 | public enum Selection { 18 | case infinite 19 | case ranges([Range]) 20 | 21 | /// Create a selection from an array of utf8 ranges. An empty array means an infinite selection. 22 | public init(offsetRanges: [Range]) { 23 | if offsetRanges.isEmpty { 24 | self = .infinite 25 | } else { 26 | let ranges = offsetRanges.map { 27 | AbsolutePosition(utf8Offset: $0.lowerBound).. Bool { 34 | switch self { 35 | case .infinite: 36 | return true 37 | case .ranges(let ranges): 38 | return ranges.contains { $0.contains(position) } 39 | } 40 | } 41 | 42 | public func overlapsOrTouches(_ range: Range) -> Bool { 43 | switch self { 44 | case .infinite: 45 | return true 46 | case .ranges(let ranges): 47 | return ranges.contains { $0.overlapsOrTouches(range) } 48 | } 49 | } 50 | } 51 | 52 | public extension Syntax { 53 | /// - Returns: `true` if the node is _completely_ inside any range in the selection 54 | func isInsideSelection(_ selection: Selection) -> Bool { 55 | switch selection { 56 | case .infinite: 57 | return true 58 | case .ranges(let ranges): 59 | return ranges.contains { return $0.lowerBound <= position && endPosition <= $0.upperBound } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/API/SwiftFormatError.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import Foundation 14 | import SwiftSyntax 15 | 16 | /// Errors that can be thrown by the `SwiftFormatter` and `SwiftLinter` APIs. 17 | public enum SwiftFormatError: LocalizedError { 18 | 19 | /// The requested file was not readable or it did not exist. 20 | case fileNotReadable 21 | 22 | /// The requested file was a directory. 23 | case isDirectory 24 | 25 | /// The file contains invalid or unrecognized Swift syntax and cannot be handled safely. 26 | case fileContainsInvalidSyntax 27 | 28 | /// The requested experimental feature name was not recognized by the parser. 29 | case unrecognizedExperimentalFeature(String) 30 | 31 | /// An error happened while dumping the tool's configuration. 32 | case configurationDumpFailed(String) 33 | 34 | /// The provided configuration version is not supported by this version of the formatter. 35 | case unsupportedConfigurationVersion(Int, highestSupported: Int) 36 | 37 | public var errorDescription: String? { 38 | switch self { 39 | case .fileNotReadable: 40 | return "file is not readable or does not exist" 41 | case .isDirectory: 42 | return "requested path is a directory, not a file" 43 | case .fileContainsInvalidSyntax: 44 | return "file contains invalid Swift syntax" 45 | case .unrecognizedExperimentalFeature(let name): 46 | return "experimental feature '\(name)' was not recognized by the Swift parser" 47 | case .configurationDumpFailed(let message): 48 | return "dumping configuration failed: \(message)" 49 | case .unsupportedConfigurationVersion(let version, let highestSupported): 50 | return 51 | "This version of the formatter does not support configuration version \(version). The highest supported version is \(highestSupported)." 52 | } 53 | } 54 | } 55 | 56 | extension SwiftFormatError: Equatable {} 57 | extension SwiftFormatError: Hashable {} 58 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Core/Finding+Convenience.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | extension Finding.Location { 16 | /// Creates a new `Finding.Location` by converting the given `SourceLocation` from `SwiftSyntax`. 17 | init(_ sourceLocation: SourceLocation) { 18 | self.init(file: sourceLocation.file, line: sourceLocation.line, column: sourceLocation.column) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Core/FindingEmitter.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | /// Emits findings encountered during linting or formatting. 14 | /// 15 | /// The finding emitter is initialized with an optional consumer function that will be invoked each 16 | /// time a finding is emitted when linting or formatting the syntax tree. This function is expected 17 | /// to act on the finding -- for example, by printing it as a diagnostic to standard error. 18 | /// 19 | /// If the consumer function is nil, then the `emit` function is a no-op. This allows callers, such 20 | /// as lint/format rules and the pretty-printer, to emit findings unconditionally, without wrapping 21 | /// each call in a check about whether the client is interested in receiving those findings or not. 22 | final class FindingEmitter { 23 | /// An optional function that will be called and passed a finding each time one is emitted. 24 | private let consumer: ((Finding) -> Void)? 25 | 26 | /// Creates a new finding emitter with the given consumer function. 27 | /// 28 | /// - Parameter consumer: An optional function that will be called and passed a finding each time 29 | /// one is emitted. 30 | public init(consumer: ((Finding) -> Void)?) { 31 | self.consumer = consumer 32 | } 33 | 34 | /// Emits a new finding. 35 | /// 36 | /// - Parameters: 37 | /// - message: A descriptive message about the finding. 38 | /// - category: A value that groups the finding into a category. 39 | /// - location: The source location where the finding was encountered. In rare cases where the 40 | /// finding does not apply to a particular location in the source code, this may be nil. 41 | /// - notes: Notes that provide additional detail about the finding, possibly referring to other 42 | /// related locations in the source file. 43 | public func emit( 44 | _ message: Finding.Message, 45 | category: FindingCategorizing, 46 | location: Finding.Location? = nil, 47 | notes: [Finding.Note] = [] 48 | ) { 49 | guard let consumer = self.consumer else { return } 50 | 51 | consumer( 52 | Finding( 53 | category: category, 54 | message: message, 55 | location: location, 56 | notes: notes 57 | ) 58 | ) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Core/FormatPipeline.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | /// A type that invokes individual format rules. 14 | /// 15 | /// Note that this type is not a `SyntaxVisitor` or `SyntaxRewriter`. That is because, at this time, 16 | /// we need to run each of the format rules individually over the entire syntax tree. We cannot 17 | /// interleave them at the individual nodes like we do for lint rules, because some rules may want 18 | /// to access the previous or next tokens. Doing so requires walking up to the parent node, but as 19 | /// the tree is rewritten by one formatting rule, it will not be reattached to the tree until the 20 | /// entire `visit` method has returned. 21 | /// 22 | /// This file will be extended with a `visit` method in Pipelines+Generated.swift. 23 | struct FormatPipeline { 24 | 25 | /// The formatter context. 26 | let context: Context 27 | 28 | /// Creates a new format pipeline. 29 | init(context: Context) { 30 | self.context = context 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Core/FunctionDeclSyntax+Convenience.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | extension FunctionDeclSyntax { 16 | /// Constructs a name for a function that includes parameter labels, i.e. `foo(_:bar:)`. 17 | var fullDeclName: String { 18 | let params = signature.parameterClause.parameters.map { param in 19 | "\(param.firstName.text):" 20 | } 21 | return "\(name.text)(\(params.joined()))" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Core/ImportsXCTestVisitor.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | /// A visitor that determines if the target source file imports `XCTest`. 16 | private class ImportsXCTestVisitor: SyntaxVisitor { 17 | private let context: Context 18 | 19 | init(context: Context) { 20 | self.context = context 21 | super.init(viewMode: .sourceAccurate) 22 | } 23 | 24 | override func visit(_ node: ImportDeclSyntax) -> SyntaxVisitorContinueKind { 25 | // If we already know whether or not `XCTest` is imported, don't bother doing anything else. 26 | guard context.importsXCTest == .notDetermined else { return .skipChildren } 27 | 28 | // If the first import path component is the `XCTest` module, record that fact. Checking in this 29 | // way lets us catch `import XCTest` but also specific decl imports like 30 | // `import class XCTest.XCTestCase`, if someone wants to do that. 31 | if node.path.first!.name.tokenKind == .identifier("XCTest") { 32 | context.importsXCTest = .importsXCTest 33 | } 34 | 35 | return .skipChildren 36 | } 37 | 38 | override func visitPost(_ node: SourceFileSyntax) { 39 | // If we visited the entire source file and didn't find an `XCTest` import, record that fact. 40 | if context.importsXCTest == .notDetermined { 41 | context.importsXCTest = .doesNotImportXCTest 42 | } 43 | } 44 | } 45 | 46 | /// Sets the appropriate value of the `importsXCTest` field in the context, which approximates 47 | /// whether the file contains test code or not. 48 | /// 49 | /// This setter will only run the visitor if another rule hasn't already called this function to 50 | /// determine if the source file imports `XCTest`. 51 | /// 52 | /// - Parameters: 53 | /// - context: The context information of the target source file. 54 | /// - sourceFile: The file to be visited. 55 | @_spi(Testing) 56 | public func setImportsXCTest(context: Context, sourceFile: SourceFileSyntax) { 57 | guard context.importsXCTest == .notDetermined else { return } 58 | let visitor = ImportsXCTestVisitor(context: context) 59 | visitor.walk(sourceFile) 60 | } 61 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Core/ModifierListSyntax+Convenience.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | extension DeclModifierListSyntax { 16 | /// Returns the declaration's access level modifier, if present. 17 | var accessLevelModifier: DeclModifierSyntax? { 18 | for modifier in self { 19 | switch modifier.name.tokenKind { 20 | case .keyword(.public), .keyword(.private), .keyword(.fileprivate), .keyword(.internal), 21 | .keyword(.package): 22 | return modifier 23 | default: 24 | continue 25 | } 26 | } 27 | return nil 28 | } 29 | 30 | /// Returns true if the modifier list contains any of the keywords in the given set. 31 | func contains(anyOf keywords: Set) -> Bool { 32 | return contains { 33 | switch $0.name.tokenKind { 34 | case .keyword(let keyword): return keywords.contains(keyword) 35 | default: return false 36 | } 37 | } 38 | } 39 | 40 | /// Removes any of the modifiers in the given set from the modifier list, mutating it in-place. 41 | mutating func remove(anyOf keywords: Set) { 42 | self = filter { 43 | switch $0.name.tokenKind { 44 | case .keyword(let keyword): return !keywords.contains(keyword) 45 | default: return true 46 | } 47 | } 48 | } 49 | 50 | /// Returns a copy of the modifier list with any of the modifiers in the given set removed. 51 | func removing(anyOf keywords: Set) -> DeclModifierListSyntax { 52 | return filter { 53 | switch $0.name.tokenKind { 54 | case .keyword(let keyword): return !keywords.contains(keyword) 55 | default: return true 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Core/RememberingIterator.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | /// An iterator that persistently remembers the most recent element returned by `next()`. 14 | struct RememberingIterator: IteratorProtocol { 15 | /// The wrapped iterator. 16 | private var base: Base 17 | 18 | /// The element most recently returned by the `next()` method. 19 | /// 20 | /// This value will always remain equal to the last non-nil element returned by `next()`, even if 21 | /// multiple calls to `next()` are made that return nil after the iterator has been exhausted. 22 | /// Therefore, this property only evaluates to `nil` if the iterator had no elements in the first 23 | /// place. 24 | private(set) var latestElement: Base.Element? 25 | 26 | /// Creates a new remembering iterator that wraps the specified iterator. 27 | init(_ base: Base) { 28 | self.base = base 29 | self.latestElement = nil 30 | } 31 | 32 | mutating func next() -> Base.Element? { 33 | let element = base.next() 34 | if element != nil { 35 | latestElement = element 36 | } 37 | return element 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Core/RuleBasedFindingCategory.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | /// A finding category that wraps a `Rule` type. 14 | /// 15 | /// Findings emitted by `SyntaxLintRule` and `SyntaxFormatRule` subclasses automatically emit their 16 | /// findings using this category type, via an instance that wraps the calling rule. The displayable 17 | /// name of the category is the same as the rule's name provided by the `ruleName` property (which 18 | /// defaults to the rule's type name). 19 | struct RuleBasedFindingCategory: FindingCategorizing { 20 | /// The type of the rule associated with this category. 21 | private let ruleType: Rule.Type 22 | 23 | var description: String { ruleType.ruleName } 24 | 25 | /// Creates a finding category that wraps the given rule type. 26 | init(ruleType: Rule.Type) { 27 | self.ruleType = ruleType 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Core/RuleRegistry+Generated.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | // This file is automatically generated with generate-swift-format. Do not edit! 14 | 15 | @_spi(Internal) public enum RuleRegistry { 16 | public static let rules: [String: Bool] = [ 17 | "AllPublicDeclarationsHaveDocumentation": false, 18 | "AlwaysUseLiteralForEmptyCollectionInit": false, 19 | "AlwaysUseLowerCamelCase": true, 20 | "AmbiguousTrailingClosureOverload": true, 21 | "AvoidRetroactiveConformances": true, 22 | "BeginDocumentationCommentWithOneLineSummary": false, 23 | "DoNotUseSemicolons": true, 24 | "DontRepeatTypeInStaticProperties": true, 25 | "FileScopedDeclarationPrivacy": true, 26 | "FullyIndirectEnum": true, 27 | "GroupNumericLiterals": true, 28 | "IdentifiersMustBeASCII": true, 29 | "NeverForceUnwrap": false, 30 | "NeverUseForceTry": false, 31 | "NeverUseImplicitlyUnwrappedOptionals": false, 32 | "NoAccessLevelOnExtensionDeclaration": true, 33 | "NoAssignmentInExpressions": true, 34 | "NoBlockComments": true, 35 | "NoCasesWithOnlyFallthrough": true, 36 | "NoEmptyLinesOpeningClosingBraces": false, 37 | "NoEmptyTrailingClosureParentheses": true, 38 | "NoLabelsInCasePatterns": true, 39 | "NoLeadingUnderscores": false, 40 | "NoParensAroundConditions": true, 41 | "NoPlaygroundLiterals": true, 42 | "NoVoidReturnOnFunctionSignature": true, 43 | "OmitExplicitReturns": false, 44 | "OneCasePerLine": true, 45 | "OneVariableDeclarationPerLine": true, 46 | "OnlyOneTrailingClosureArgument": true, 47 | "OrderedImports": true, 48 | "ReplaceForEachWithForLoop": true, 49 | "ReturnVoidInsteadOfEmptyTuple": true, 50 | "TypeNamesShouldBeCapitalized": true, 51 | "UseEarlyExits": false, 52 | "UseExplicitNilCheckInConditions": true, 53 | "UseLetInEveryBoundCaseVariable": true, 54 | "UseShorthandTypeNames": true, 55 | "UseSingleLinePropertyGetter": true, 56 | "UseSynthesizedInitializer": true, 57 | "UseTripleSlashForDocumentationComments": true, 58 | "UseWhereClausesInForLoops": false, 59 | "ValidateDocumentationComments": false, 60 | ] 61 | } 62 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Core/RuleState.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | /// The enablement of a lint/format rule based on the presence or absence of comment directives in 14 | /// the source file. 15 | @_spi(Testing) 16 | public enum RuleState { 17 | 18 | /// There is no explicit information in the source file about whether the rule should be enabled 19 | /// or disabled at the requested location, so the configuration default should be used. 20 | case `default` 21 | 22 | /// The rule is explicitly disabled at the requested location. 23 | case disabled 24 | } 25 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Core/SyntaxFormatRule.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | /// A rule that both formats and lints a given file. 16 | @_spi(Rules) 17 | public class SyntaxFormatRule: SyntaxRewriter, Rule { 18 | /// Whether this rule is opt-in, meaning it's disabled by default. Rules are opt-out unless they 19 | /// override this property. 20 | public class var isOptIn: Bool { 21 | return false 22 | } 23 | 24 | /// The context in which the rule is executed. 25 | public let context: Context 26 | 27 | /// Creates a new SyntaxFormatRule in the given context. 28 | public required init(context: Context) { 29 | self.context = context 30 | } 31 | 32 | public override func visitAny(_ node: Syntax) -> Syntax? { 33 | // If the rule is not enabled, then return the node unmodified; otherwise, returning nil tells 34 | // SwiftSyntax to continue with the standard dispatch. 35 | guard context.shouldFormat(type(of: self), node: node) else { return node } 36 | return nil 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Core/SyntaxLintRule.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import Foundation 14 | import SwiftSyntax 15 | 16 | /// A rule that lints a given file. 17 | @_spi(Rules) 18 | public class SyntaxLintRule: SyntaxVisitor, Rule { 19 | /// Whether this rule is opt-in, meaning it's disabled by default. Rules are opt-out unless they 20 | /// override this property. 21 | public class var isOptIn: Bool { 22 | return false 23 | } 24 | 25 | /// The context in which the rule is executed. 26 | public let context: Context 27 | 28 | /// Creates a new rule in a given context. 29 | public required init(context: Context) { 30 | self.context = context 31 | super.init(viewMode: .sourceAccurate) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Core/SyntaxTraits.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | /// Common protocol implemented by expression syntax types that support calling another expression. 16 | protocol CallingExprSyntaxProtocol: ExprSyntaxProtocol { 17 | var calledExpression: ExprSyntax { get } 18 | } 19 | 20 | extension FunctionCallExprSyntax: CallingExprSyntaxProtocol {} 21 | extension SubscriptCallExprSyntax: CallingExprSyntaxProtocol {} 22 | 23 | extension Syntax { 24 | func asProtocol(_: CallingExprSyntaxProtocol.Protocol) -> CallingExprSyntaxProtocol? { 25 | return self.asProtocol(SyntaxProtocol.self) as? CallingExprSyntaxProtocol 26 | } 27 | func isProtocol(_: CallingExprSyntaxProtocol.Protocol) -> Bool { 28 | return self.asProtocol(CallingExprSyntaxProtocol.self) != nil 29 | } 30 | } 31 | 32 | extension ExprSyntax { 33 | func asProtocol(_: CallingExprSyntaxProtocol.Protocol) -> CallingExprSyntaxProtocol? { 34 | return Syntax(self).asProtocol(SyntaxProtocol.self) as? CallingExprSyntaxProtocol 35 | } 36 | func isProtocol(_: CallingExprSyntaxProtocol.Protocol) -> Bool { 37 | return self.asProtocol(CallingExprSyntaxProtocol.self) != nil 38 | } 39 | } 40 | 41 | /// Common protocol implemented by expression syntax types that are expressed as a modified 42 | /// subexpression of the form ` `. 43 | protocol KeywordModifiedExprSyntaxProtocol: ExprSyntaxProtocol { 44 | var expression: ExprSyntax { get } 45 | } 46 | 47 | extension AwaitExprSyntax: KeywordModifiedExprSyntaxProtocol {} 48 | extension TryExprSyntax: KeywordModifiedExprSyntaxProtocol {} 49 | extension UnsafeExprSyntax: KeywordModifiedExprSyntaxProtocol {} 50 | 51 | extension Syntax { 52 | func asProtocol(_: KeywordModifiedExprSyntaxProtocol.Protocol) -> KeywordModifiedExprSyntaxProtocol? { 53 | return self.asProtocol(SyntaxProtocol.self) as? KeywordModifiedExprSyntaxProtocol 54 | } 55 | func isProtocol(_: KeywordModifiedExprSyntaxProtocol.Protocol) -> Bool { 56 | return self.asProtocol(KeywordModifiedExprSyntaxProtocol.self) != nil 57 | } 58 | } 59 | 60 | extension ExprSyntax { 61 | func asProtocol(_: KeywordModifiedExprSyntaxProtocol.Protocol) -> KeywordModifiedExprSyntaxProtocol? { 62 | return Syntax(self).asProtocol(SyntaxProtocol.self) as? KeywordModifiedExprSyntaxProtocol 63 | } 64 | func isProtocol(_: KeywordModifiedExprSyntaxProtocol.Protocol) -> Bool { 65 | return self.asProtocol(KeywordModifiedExprSyntaxProtocol.self) != nil 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Core/Trivia+Convenience.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | extension Trivia { 16 | var hasAnyComments: Bool { 17 | return contains { 18 | switch $0 { 19 | case .lineComment, .docLineComment, .blockComment, .docBlockComment: 20 | return true 21 | default: 22 | return false 23 | } 24 | } 25 | } 26 | 27 | /// Returns whether the trivia contains at least 1 `lineComment`. 28 | var hasLineComment: Bool { 29 | return self.contains { 30 | if case .lineComment = $0 { return true } 31 | return false 32 | } 33 | } 34 | 35 | /// Returns this set of trivia, without any leading spaces. 36 | func withoutLeadingSpaces() -> Trivia { 37 | return Trivia(pieces: self.pieces.drop(while: \.isSpaceOrTab)) 38 | } 39 | 40 | func withoutTrailingSpaces() -> Trivia { 41 | guard let lastNonSpaceIndex = self.pieces.lastIndex(where: \.isSpaceOrTab) else { 42 | return self 43 | } 44 | return Trivia(pieces: self[.. Trivia { 51 | var maybeLastNewlineOffset: Int? = nil 52 | for (offset, piece) in self.enumerated() { 53 | switch piece { 54 | case .newlines, .carriageReturns, .carriageReturnLineFeeds: 55 | maybeLastNewlineOffset = offset 56 | default: 57 | break 58 | } 59 | } 60 | guard let lastNewlineOffset = maybeLastNewlineOffset else { return self } 61 | return Trivia(pieces: self.dropLast(self.count - lastNewlineOffset)) 62 | } 63 | 64 | /// Returns `true` if this trivia contains any newlines. 65 | var containsNewlines: Bool { 66 | return contains( 67 | where: { 68 | if case .newlines = $0 { return true } 69 | return false 70 | }) 71 | } 72 | 73 | /// Returns `true` if this trivia contains any spaces. 74 | var containsSpaces: Bool { 75 | return contains( 76 | where: { 77 | if case .spaces = $0 { return true } 78 | if case .tabs = $0 { return true } 79 | return false 80 | }) 81 | } 82 | 83 | /// Returns `true` if this trivia contains any backslashes (used for multiline string newline 84 | /// suppression). 85 | var containsBackslashes: Bool { 86 | return contains( 87 | where: { 88 | if case .backslashes = $0 { return true } 89 | return false 90 | }) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Core/WithAttributesSyntax+Convenience.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | extension WithAttributesSyntax { 16 | /// Indicates whether the node has attribute with the given `name` and `module`. 17 | /// The `module` is only considered if the attribute is written as `@Module.Attribute`. 18 | /// 19 | /// - Parameter name: The name of the attribute to lookup. 20 | /// - Parameter module: The module name to lookup the attribute in. 21 | /// - Returns: True if the node has an attribute with the given `name`, otherwise false. 22 | func hasAttribute(_ name: String, inModule module: String) -> Bool { 23 | attributes.contains { attribute in 24 | let attributeName = attribute.as(AttributeSyntax.self)?.attributeName 25 | if let identifier = attributeName?.as(IdentifierTypeSyntax.self) { 26 | // @Attribute syntax 27 | return identifier.name.text == name 28 | } 29 | if let memberType = attributeName?.as(MemberTypeSyntax.self) { 30 | // @Module.Attribute syntax 31 | return memberType.name.text == name 32 | && memberType.baseType.as(IdentifierTypeSyntax.self)?.name.text == module 33 | } 34 | return false 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Core/WithSemicolonSyntax.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | /// Protocol that declares support for accessing and modifying a token that represents a semicolon. 16 | protocol WithSemicolonSyntax: SyntaxProtocol { 17 | var semicolon: TokenSyntax? { get set } 18 | } 19 | 20 | extension MemberBlockItemSyntax: WithSemicolonSyntax {} 21 | extension CodeBlockItemSyntax: WithSemicolonSyntax {} 22 | 23 | extension SyntaxProtocol { 24 | func asProtocol(_: WithSemicolonSyntax.Protocol) -> WithSemicolonSyntax? { 25 | return Syntax(self).asProtocol(SyntaxProtocol.self) as? WithSemicolonSyntax 26 | } 27 | 28 | func isProtocol(_: WithSemicolonSyntax.Protocol) -> Bool { 29 | return self.asProtocol(WithSemicolonSyntax.self) != nil 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/PrettyPrint/Indent+Length.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | extension Indent { 14 | var character: Character { 15 | switch self { 16 | case .tabs: return "\t" 17 | case .spaces: return " " 18 | } 19 | } 20 | 21 | var text: String { 22 | return String(repeating: character, count: count) 23 | } 24 | 25 | func length(tabWidth: Int) -> Int { 26 | switch self { 27 | case .spaces(let count): return count 28 | case .tabs(let count): return count * tabWidth 29 | } 30 | } 31 | } 32 | 33 | extension Array where Element == Indent { 34 | func indentation() -> String { 35 | return map { $0.text }.joined() 36 | } 37 | 38 | func length(in configuration: Configuration) -> Int { 39 | return self.length(tabWidth: configuration.tabWidth) 40 | } 41 | 42 | func length(tabWidth: Int) -> Int { 43 | return reduce(into: 0) { $0 += $1.length(tabWidth: tabWidth) } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/PrettyPrint/PrettyPrintFindingCategory.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | /// Categories for findings emitted by the pretty printer. 14 | enum PrettyPrintFindingCategory: FindingCategorizing { 15 | /// Finding related to an end-of-line comment. 16 | case endOfLineComment 17 | 18 | /// Findings related to the presence of absence of a trailing comma in collection literals. 19 | case trailingComma 20 | 21 | var description: String { 22 | switch self { 23 | case .endOfLineComment: return "EndOfLineComment" 24 | case .trailingComma: return "TrailingComma" 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/PrettyPrint/WhitespaceFindingCategory.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | /// Categories for findings emitted by the whitespace linter. 14 | enum WhitespaceFindingCategory: FindingCategorizing { 15 | /// Findings related to trailing whitespace on a line. 16 | case trailingWhitespace 17 | 18 | /// Findings related to indentation (i.e., whitespace at the beginning of a line). 19 | case indentation 20 | 21 | /// Findings related to interior whitespace (i.e., neither leading nor trailing space). 22 | case spacing 23 | 24 | /// Findings related to specific characters used for interior whitespace. 25 | case spacingCharacter 26 | 27 | /// Findings related to the removal of line breaks. 28 | case removeLine 29 | 30 | /// Findings related to the addition of line breaks. 31 | case addLines 32 | 33 | /// Findings related to the length of a line. 34 | case lineLength 35 | 36 | var description: String { 37 | switch self { 38 | case .trailingWhitespace: return "TrailingWhitespace" 39 | case .indentation: return "Indentation" 40 | case .spacing: return "Spacing" 41 | case .spacingCharacter: return "SpacingCharacter" 42 | case .removeLine: return "RemoveLine" 43 | case .addLines: return "AddLines" 44 | case .lineLength: return "LineLength" 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Rules/AvoidRetroactiveConformances.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | /// `@retroactive` conformances are forbidden. 16 | /// 17 | /// Lint: Using `@retroactive` results in a lint error. 18 | @_spi(Rules) 19 | public final class AvoidRetroactiveConformances: SyntaxLintRule { 20 | public override func visit(_ node: ExtensionDeclSyntax) -> SyntaxVisitorContinueKind { 21 | if let inheritanceClause = node.inheritanceClause { 22 | walk(inheritanceClause) 23 | } 24 | return .skipChildren 25 | } 26 | public override func visit(_ type: AttributeSyntax) -> SyntaxVisitorContinueKind { 27 | if let identifier = type.attributeName.as(IdentifierTypeSyntax.self) { 28 | if identifier.name.text == "retroactive" { 29 | diagnose(.doNotUseRetroactive, on: type) 30 | } 31 | } 32 | return .skipChildren 33 | } 34 | } 35 | 36 | extension Finding.Message { 37 | fileprivate static let doNotUseRetroactive: Finding.Message = "do not declare retroactive conformances" 38 | } 39 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Rules/IdentifiersMustBeASCII.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | /// All identifiers must be ASCII. 16 | /// 17 | /// Lint: If an identifier contains non-ASCII characters, a lint error is raised. 18 | @_spi(Rules) 19 | public final class IdentifiersMustBeASCII: SyntaxLintRule { 20 | 21 | public override func visit(_ node: IdentifierPatternSyntax) -> SyntaxVisitorContinueKind { 22 | let identifier = node.identifier.text 23 | let invalidCharacters = identifier.unicodeScalars.filter { !$0.isASCII }.map { $0.description } 24 | 25 | if !invalidCharacters.isEmpty { 26 | diagnose(.nonASCIICharsNotAllowed(invalidCharacters, identifier), on: node) 27 | } 28 | 29 | return .skipChildren 30 | } 31 | } 32 | 33 | extension Finding.Message { 34 | fileprivate static func nonASCIICharsNotAllowed( 35 | _ invalidCharacters: [String], 36 | _ identifierName: String 37 | ) -> Finding.Message { 38 | """ 39 | remove non-ASCII characters from '\(identifierName)': \ 40 | \(invalidCharacters.joined(separator: ", ")) 41 | """ 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Rules/NeverForceUnwrap.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | /// Force-unwraps are strongly discouraged and must be documented. 16 | /// 17 | /// This rule does not apply to test code, defined as code which: 18 | /// * Contains the line `import XCTest` 19 | /// * The function is marked with `@Test` attribute 20 | /// 21 | /// Lint: If a force unwrap is used, a lint warning is raised. 22 | @_spi(Rules) 23 | public final class NeverForceUnwrap: SyntaxLintRule { 24 | 25 | /// Identifies this rule as being opt-in. While force unwrap is an unsafe pattern (i.e. it can 26 | /// crash), there are valid contexts for force unwrap where it won't crash. This rule can't 27 | /// evaluate the context around the force unwrap to make that determination. 28 | public override class var isOptIn: Bool { return true } 29 | 30 | public override func visit(_ node: SourceFileSyntax) -> SyntaxVisitorContinueKind { 31 | // Tracks whether "XCTest" is imported in the source file before processing individual nodes. 32 | setImportsXCTest(context: context, sourceFile: node) 33 | return .visitChildren 34 | } 35 | 36 | public override func visit(_ node: ForceUnwrapExprSyntax) -> SyntaxVisitorContinueKind { 37 | guard context.importsXCTest == .doesNotImportXCTest else { return .skipChildren } 38 | // Allow force unwrapping if it is in a function marked with @Test attribute. 39 | if node.hasTestAncestor { return .skipChildren } 40 | diagnose(.doNotForceUnwrap(name: node.expression.trimmedDescription), on: node) 41 | return .skipChildren 42 | } 43 | 44 | public override func visit(_ node: AsExprSyntax) -> SyntaxVisitorContinueKind { 45 | // Only fire if we're not in a test file and if there is an exclamation mark following the `as` 46 | // keyword. 47 | guard context.importsXCTest == .doesNotImportXCTest else { return .skipChildren } 48 | guard let questionOrExclamation = node.questionOrExclamationMark else { return .skipChildren } 49 | guard questionOrExclamation.tokenKind == .exclamationMark else { return .skipChildren } 50 | // Allow force cast if it is in a function marked with @Test attribute. 51 | if node.hasTestAncestor { return .skipChildren } 52 | diagnose(.doNotForceCast(name: node.type.trimmedDescription), on: node) 53 | return .skipChildren 54 | } 55 | } 56 | 57 | extension Finding.Message { 58 | fileprivate static func doNotForceUnwrap(name: String) -> Finding.Message { 59 | "do not force unwrap '\(name)'" 60 | } 61 | 62 | fileprivate static func doNotForceCast(name: String) -> Finding.Message { 63 | "do not force cast to '\(name)'" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Rules/NeverUseForceTry.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | /// Force-try (`try!`) is forbidden. 16 | /// 17 | /// This rule does not apply to test code, defined as code which: 18 | /// * Contains the line `import XCTest` 19 | /// * The function is marked with `@Test` attribute 20 | /// 21 | /// Lint: Using `try!` results in a lint error. 22 | /// 23 | /// TODO: Create exception for NSRegularExpression 24 | @_spi(Rules) 25 | public final class NeverUseForceTry: SyntaxLintRule { 26 | 27 | /// Identifies this rule as being opt-in. While force try is an unsafe pattern (i.e. it can 28 | /// crash), there are valid contexts for force try where it won't crash. This rule can't 29 | /// evaluate the context around the force try to make that determination. 30 | public override class var isOptIn: Bool { return true } 31 | 32 | public override func visit(_ node: SourceFileSyntax) -> SyntaxVisitorContinueKind { 33 | setImportsXCTest(context: context, sourceFile: node) 34 | return .visitChildren 35 | } 36 | 37 | public override func visit(_ node: TryExprSyntax) -> SyntaxVisitorContinueKind { 38 | guard context.importsXCTest == .doesNotImportXCTest else { return .skipChildren } 39 | guard let mark = node.questionOrExclamationMark else { return .visitChildren } 40 | // Allow force try if it is in a function marked with @Test attribute. 41 | if node.hasTestAncestor { return .skipChildren } 42 | if mark.tokenKind == .exclamationMark { 43 | diagnose(.doNotForceTry, on: node.tryKeyword) 44 | } 45 | return .visitChildren 46 | } 47 | } 48 | 49 | extension Finding.Message { 50 | fileprivate static let doNotForceTry: Finding.Message = "do not use force try" 51 | } 52 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Rules/NeverUseImplicitlyUnwrappedOptionals.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | /// Implicitly unwrapped optionals (e.g. `var s: String!`) are forbidden. 16 | /// 17 | /// Certain properties (e.g. `@IBOutlet`) tied to the UI lifecycle are ignored. 18 | /// 19 | /// This rule does not apply to test code, defined as code which: 20 | /// * Contains the line `import XCTest` 21 | /// * The function is marked with `@Test` attribute 22 | /// 23 | /// TODO: Create exceptions for other UI elements (ex: viewDidLoad) 24 | /// 25 | /// Lint: Declaring a property with an implicitly unwrapped type yields a lint error. 26 | @_spi(Rules) 27 | public final class NeverUseImplicitlyUnwrappedOptionals: SyntaxLintRule { 28 | 29 | /// Identifies this rule as being opt-in. While accessing implicitly unwrapped optionals is an 30 | /// unsafe pattern (i.e. it can crash), there are valid contexts for using implicitly unwrapped 31 | /// optionals where it won't crash. This rule can't evaluate the context around the usage to make 32 | /// that determination. 33 | public override class var isOptIn: Bool { return true } 34 | 35 | // Checks if "XCTest" is an import statement 36 | public override func visit(_ node: SourceFileSyntax) -> SyntaxVisitorContinueKind { 37 | setImportsXCTest(context: context, sourceFile: node) 38 | return .visitChildren 39 | } 40 | 41 | public override func visit(_ node: VariableDeclSyntax) -> SyntaxVisitorContinueKind { 42 | guard context.importsXCTest == .doesNotImportXCTest else { return .skipChildren } 43 | // Allow implicitly unwrapping if it is in a function marked with @Test attribute. 44 | if node.hasTestAncestor { return .skipChildren } 45 | // Ignores IBOutlet variables 46 | for attribute in node.attributes { 47 | if (attribute.as(AttributeSyntax.self))?.attributeName.as(IdentifierTypeSyntax.self)?.name.text == "IBOutlet" { 48 | return .skipChildren 49 | } 50 | } 51 | // Finds type annotation for variable(s) 52 | for binding in node.bindings { 53 | guard let nodeTypeAnnotation = binding.typeAnnotation else { continue } 54 | diagnoseImplicitWrapViolation(nodeTypeAnnotation.type) 55 | } 56 | return .skipChildren 57 | } 58 | 59 | private func diagnoseImplicitWrapViolation(_ type: TypeSyntax) { 60 | guard let violation = type.as(ImplicitlyUnwrappedOptionalTypeSyntax.self) else { return } 61 | diagnose( 62 | .doNotUseImplicitUnwrapping(identifier: violation.wrappedType.trimmedDescription), 63 | on: type 64 | ) 65 | } 66 | } 67 | 68 | extension Finding.Message { 69 | fileprivate static func doNotUseImplicitUnwrapping(identifier: String) -> Finding.Message { 70 | "use '\(identifier)' or '\(identifier)?' instead of '\(identifier)!'" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Rules/NoBlockComments.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | /// Block comments should be avoided in favor of line comments. 16 | /// 17 | /// Lint: If a block comment appears, a lint error is raised. 18 | @_spi(Rules) 19 | public final class NoBlockComments: SyntaxLintRule { 20 | public override func visit(_ token: TokenSyntax) -> SyntaxVisitorContinueKind { 21 | for triviaIndex in token.leadingTrivia.indices { 22 | let piece = token.leadingTrivia[triviaIndex] 23 | if case .blockComment = piece { 24 | diagnose(.avoidBlockComment, on: token, anchor: .leadingTrivia(triviaIndex)) 25 | } 26 | } 27 | for triviaIndex in token.trailingTrivia.indices { 28 | let piece = token.trailingTrivia[triviaIndex] 29 | if case .blockComment = piece { 30 | diagnose(.avoidBlockComment, on: token, anchor: .trailingTrivia(triviaIndex)) 31 | } 32 | } 33 | return .skipChildren 34 | } 35 | } 36 | 37 | extension Finding.Message { 38 | fileprivate static let avoidBlockComment: Finding.Message = 39 | "replace this block comment with line comments" 40 | } 41 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Rules/NoEmptyTrailingClosureParentheses.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | /// Function calls with no arguments and a trailing closure should not have empty parentheses. 16 | /// 17 | /// Lint: If a function call with a trailing closure has an empty argument list with parentheses, 18 | /// a lint error is raised. 19 | /// 20 | /// Format: Empty parentheses in function calls with trailing closures will be removed. 21 | @_spi(Rules) 22 | public final class NoEmptyTrailingClosureParentheses: SyntaxFormatRule { 23 | 24 | public override func visit(_ node: FunctionCallExprSyntax) -> ExprSyntax { 25 | guard node.arguments.count == 0 else { return super.visit(node) } 26 | 27 | guard 28 | let trailingClosure = node.trailingClosure, 29 | let leftParen = node.leftParen, 30 | let rightParen = node.rightParen, 31 | node.arguments.isEmpty, 32 | !leftParen.trailingTrivia.hasAnyComments, 33 | !rightParen.leadingTrivia.hasAnyComments 34 | else { 35 | return super.visit(node) 36 | } 37 | guard let name = node.calledExpression.lastToken(viewMode: .sourceAccurate) else { 38 | return super.visit(node) 39 | } 40 | 41 | diagnose(.removeEmptyTrailingParentheses(name: "\(name.trimmedDescription)"), on: leftParen) 42 | 43 | // Need to visit `calledExpression` before creating a new node so that the location data (column 44 | // and line numbers) is available. 45 | guard var rewrittenCalledExpr = ExprSyntax(rewrite(Syntax(node.calledExpression))) else { 46 | return super.visit(node) 47 | } 48 | rewrittenCalledExpr.trailingTrivia = [.spaces(1)] 49 | 50 | var result = node 51 | result.leftParen = nil 52 | result.rightParen = nil 53 | result.calledExpression = rewrittenCalledExpr 54 | result.trailingClosure = rewrite(trailingClosure).as(ClosureExprSyntax.self) 55 | return ExprSyntax(result) 56 | } 57 | } 58 | 59 | extension Finding.Message { 60 | fileprivate static func removeEmptyTrailingParentheses(name: String) -> Finding.Message { 61 | "remove the empty parentheses following '\(name)'" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Rules/NoLabelsInCasePatterns.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import Foundation 14 | import SwiftSyntax 15 | 16 | /// Redundant labels are forbidden in case patterns. 17 | /// 18 | /// In practice, *all* case pattern labels should be redundant. 19 | /// 20 | /// Lint: Using a label in a case statement yields a lint error unless the label does not match the 21 | /// binding identifier. 22 | /// 23 | /// Format: Redundant labels in case patterns are removed. 24 | @_spi(Rules) 25 | public final class NoLabelsInCasePatterns: SyntaxFormatRule { 26 | public override func visit(_ node: SwitchCaseLabelSyntax) -> SwitchCaseLabelSyntax { 27 | var newCaseItems: [SwitchCaseItemSyntax] = [] 28 | 29 | for item in node.caseItems { 30 | guard 31 | var exprPattern = item.pattern.as(ExpressionPatternSyntax.self), 32 | var funcCall = exprPattern.expression.as(FunctionCallExprSyntax.self) 33 | else { 34 | newCaseItems.append(item) 35 | continue 36 | } 37 | 38 | // Search function call argument list for violations 39 | var newArguments = LabeledExprListSyntax() 40 | for argument in funcCall.arguments { 41 | guard 42 | let label = argument.label, 43 | let unresolvedPat = argument.expression.as(PatternExprSyntax.self), 44 | let valueBinding = unresolvedPat.pattern.as(ValueBindingPatternSyntax.self) 45 | else { 46 | newArguments.append(argument) 47 | continue 48 | } 49 | 50 | // Remove label if it's the same as the value identifier 51 | let name = valueBinding.pattern.trimmedDescription 52 | guard name == label.text else { 53 | newArguments.append(argument) 54 | continue 55 | } 56 | diagnose(.removeRedundantLabel(name: name), on: label) 57 | 58 | var newArgument = argument 59 | newArgument.label = nil 60 | newArgument.colon = nil 61 | newArguments.append(newArgument) 62 | } 63 | 64 | var newItem = item 65 | funcCall.arguments = newArguments 66 | exprPattern.expression = ExprSyntax(funcCall) 67 | newItem.pattern = PatternSyntax(exprPattern) 68 | newCaseItems.append(newItem) 69 | } 70 | 71 | var result = node 72 | result.caseItems = SwitchCaseItemListSyntax(newCaseItems) 73 | return result 74 | } 75 | } 76 | 77 | extension Finding.Message { 78 | fileprivate static func removeRedundantLabel(name: String) -> Finding.Message { 79 | "remove the label '\(name)' from this 'case' pattern" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Rules/NoVoidReturnOnFunctionSignature.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | /// Functions that return `()` or `Void` should omit the return signature. 16 | /// 17 | /// Lint: Function declarations that explicitly return `()` or `Void` will yield a lint error. 18 | /// 19 | /// Format: Function declarations with explicit returns of `()` or `Void` will have their return 20 | /// signature stripped. 21 | @_spi(Rules) 22 | public final class NoVoidReturnOnFunctionSignature: SyntaxFormatRule { 23 | /// Remove the `-> Void` return type for function signatures. Do not remove 24 | /// it for closure signatures, because that may introduce an ambiguity when closure signatures 25 | /// are inferred. 26 | public override func visit(_ node: FunctionSignatureSyntax) -> FunctionSignatureSyntax { 27 | guard let returnType = node.returnClause?.type else { return node } 28 | 29 | if let identifierType = returnType.as(IdentifierTypeSyntax.self), 30 | identifierType.name.text == "Void", 31 | identifierType.genericArgumentClause?.arguments.isEmpty ?? true 32 | { 33 | diagnose(.removeRedundantReturn("Void"), on: identifierType) 34 | return removingReturnClause(from: node) 35 | } 36 | if let tupleType = returnType.as(TupleTypeSyntax.self), tupleType.elements.isEmpty { 37 | diagnose(.removeRedundantReturn("()"), on: tupleType) 38 | return removingReturnClause(from: node) 39 | } 40 | 41 | return node 42 | } 43 | 44 | /// Returns a copy of the given function signature with the return clause removed. 45 | private func removingReturnClause( 46 | from signature: FunctionSignatureSyntax 47 | ) -> FunctionSignatureSyntax { 48 | var result = signature 49 | result.returnClause = nil 50 | return result 51 | } 52 | } 53 | 54 | extension Finding.Message { 55 | fileprivate static func removeRedundantReturn(_ type: String) -> Finding.Message { 56 | "remove the explicit return type '\(type)' from this function" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Rules/OnlyOneTrailingClosureArgument.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | /// Function calls should never mix normal closure arguments and trailing closures. 16 | /// 17 | /// Lint: If a function call with a trailing closure also contains a non-trailing closure argument, 18 | /// a lint error is raised. 19 | @_spi(Rules) 20 | public final class OnlyOneTrailingClosureArgument: SyntaxLintRule { 21 | 22 | public override func visit(_ node: FunctionCallExprSyntax) -> SyntaxVisitorContinueKind { 23 | guard (node.arguments.contains { $0.expression.is(ClosureExprSyntax.self) }) else { 24 | return .skipChildren 25 | } 26 | guard node.trailingClosure != nil else { return .skipChildren } 27 | diagnose(.removeTrailingClosure, on: node) 28 | return .skipChildren 29 | } 30 | } 31 | 32 | extension Finding.Message { 33 | fileprivate static let removeTrailingClosure: Finding.Message = 34 | "revise this function call to avoid using both closure arguments and a trailing closure" 35 | } 36 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Rules/ReplaceForEachWithForLoop.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | /// Replace `forEach` with `for-in` loop unless its argument is a function reference. 16 | /// 17 | /// Lint: invalid use of `forEach` yield will yield a lint error. 18 | @_spi(Rules) 19 | public final class ReplaceForEachWithForLoop: SyntaxLintRule { 20 | public override func visit(_ node: FunctionCallExprSyntax) -> SyntaxVisitorContinueKind { 21 | // We are only interested in calls with a single trailing closure 22 | // argument. 23 | if !node.arguments.isEmpty || node.trailingClosure == nil || !node.additionalTrailingClosures.isEmpty { 24 | return .visitChildren 25 | } 26 | 27 | guard let member = node.calledExpression.as(MemberAccessExprSyntax.self) else { 28 | return .visitChildren 29 | } 30 | 31 | let memberName = member.declName.baseName 32 | guard memberName.text == "forEach" else { 33 | return .visitChildren 34 | } 35 | 36 | // If there is another chained member after `.forEach`, 37 | // let's skip the diagnostic because resulting code might 38 | // be less understandable. 39 | if node.parent?.is(MemberAccessExprSyntax.self) == true { 40 | return .visitChildren 41 | } 42 | 43 | diagnose(.replaceForEachWithLoop(), on: memberName) 44 | return .visitChildren 45 | } 46 | } 47 | 48 | extension Finding.Message { 49 | fileprivate static func replaceForEachWithLoop() -> Finding.Message { 50 | "replace use of '.forEach { ... }' with for-in loop" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Rules/TypeNamesShouldBeCapitalized.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | /// `struct`, `class`, `enum` and `protocol` declarations should have a capitalized name. 16 | /// 17 | /// Lint: Types with un-capitalized names will yield a lint error. 18 | @_spi(Rules) 19 | public final class TypeNamesShouldBeCapitalized: SyntaxLintRule { 20 | public override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind { 21 | diagnoseNameConventionMismatch(node, name: node.name, kind: "struct") 22 | return .visitChildren 23 | } 24 | 25 | public override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind { 26 | diagnoseNameConventionMismatch(node, name: node.name, kind: "class") 27 | return .visitChildren 28 | } 29 | 30 | public override func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorContinueKind { 31 | diagnoseNameConventionMismatch(node, name: node.name, kind: "enum") 32 | return .visitChildren 33 | } 34 | 35 | public override func visit(_ node: ProtocolDeclSyntax) -> SyntaxVisitorContinueKind { 36 | diagnoseNameConventionMismatch(node, name: node.name, kind: "protocol") 37 | return .visitChildren 38 | } 39 | 40 | public override func visit(_ node: ActorDeclSyntax) -> SyntaxVisitorContinueKind { 41 | diagnoseNameConventionMismatch(node, name: node.name, kind: "actor") 42 | return .visitChildren 43 | } 44 | 45 | public override func visit(_ node: AssociatedTypeDeclSyntax) -> SyntaxVisitorContinueKind { 46 | diagnoseNameConventionMismatch(node, name: node.name, kind: "associated type") 47 | return .visitChildren 48 | } 49 | 50 | public override func visit(_ node: TypeAliasDeclSyntax) -> SyntaxVisitorContinueKind { 51 | diagnoseNameConventionMismatch(node, name: node.name, kind: "type alias") 52 | return .visitChildren 53 | } 54 | 55 | private func diagnoseNameConventionMismatch( 56 | _ type: T, 57 | name: TokenSyntax, 58 | kind: String 59 | ) { 60 | let leadingUnderscores = name.text.prefix { $0 == "_" } 61 | if let firstChar = name.text[leadingUnderscores.endIndex...].first, 62 | firstChar.uppercased() != String(firstChar) 63 | { 64 | diagnose(.capitalizeTypeName(name: name.text, kind: kind), on: name) 65 | } 66 | } 67 | } 68 | 69 | extension Finding.Message { 70 | fileprivate static func capitalizeTypeName(name: String, kind: String) -> Finding.Message { 71 | var capitalized = name 72 | let leadingUnderscores = capitalized.prefix { $0 == "_" } 73 | let charAt = leadingUnderscores.endIndex 74 | capitalized.replaceSubrange(charAt...charAt, with: capitalized[charAt].uppercased()) 75 | return "rename the \(kind) '\(name)' using UpperCamelCase; for example, '\(capitalized)'" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Rules/UseSingleLinePropertyGetter.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | /// Read-only computed properties must use implicit `get` blocks. 16 | /// 17 | /// Lint: Read-only computed properties with explicit `get` blocks yield a lint error. 18 | /// 19 | /// Format: Explicit `get` blocks are rendered implicit by removing the `get`. 20 | @_spi(Rules) 21 | public final class UseSingleLinePropertyGetter: SyntaxFormatRule { 22 | 23 | public override func visit(_ node: PatternBindingSyntax) -> PatternBindingSyntax { 24 | guard 25 | let accessorBlock = node.accessorBlock, 26 | case .accessors(let accessors) = accessorBlock.accessors, 27 | let acc = accessors.first, 28 | let body = acc.body, 29 | accessors.count == 1, 30 | acc.accessorSpecifier.tokenKind == .keyword(.get), 31 | acc.modifier == nil, 32 | acc.effectSpecifiers == nil 33 | else { return node } 34 | 35 | diagnose(.removeExtraneousGetBlock, on: acc) 36 | 37 | var result = node 38 | result.accessorBlock?.accessors = .getter(body.statements) 39 | return result 40 | } 41 | } 42 | 43 | extension Finding.Message { 44 | fileprivate static let removeExtraneousGetBlock: Finding.Message = 45 | "remove 'get {...}' around the accessor and move its body directly into the computed property" 46 | } 47 | -------------------------------------------------------------------------------- /Sources/SwiftFormat/Utilities/URL+isRoot.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import Foundation 14 | 15 | #if os(Windows) 16 | import WinSDK 17 | #endif 18 | 19 | extension URL { 20 | /// Returns a `Bool` to indicate if the given `URL` leads to the root of a filesystem. 21 | /// A non-filesystem type `URL` will always return false. 22 | @_spi(Testing) public var isRoot: Bool { 23 | guard isFileURL else { return false } 24 | 25 | #if compiler(>=6.1) 26 | #if os(Windows) 27 | let filePath = self.withUnsafeFileSystemRepresentation { pointer in 28 | guard let pointer else { 29 | return "" 30 | } 31 | return String(cString: pointer) 32 | } 33 | return filePath.withCString(encodedAs: UTF16.self, PathCchIsRoot) 34 | #else // os(Windows) 35 | return self.path == "/" 36 | #endif // os(Windows) 37 | #else // compiler(>=6.1) 38 | 39 | #if os(Windows) 40 | // This is needed as the fixes from #844 aren't in the Swift 6.0 toolchain. 41 | // https://github.com/swiftlang/swift-format/issues/844 42 | var pathComponents = self.pathComponents 43 | if pathComponents.first == "/" { 44 | // Canonicalize `/C:/` to `C:/`. 45 | pathComponents = Array(pathComponents.dropFirst()) 46 | } 47 | return pathComponents.count <= 1 48 | #else // os(Windows) 49 | // On Linux, we may end up with an string for the path due to https://github.com/swiftlang/swift-foundation/issues/980 50 | // This is needed as the fixes from #980 aren't in the Swift 6.0 toolchain. 51 | return self.path == "/" || self.path == "" 52 | #endif // os(Windows) 53 | #endif // compiler(>=6.1) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/_SwiftFormatInstructionCounter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #[[ 2 | This source file is part of the swift-format open source project 3 | 4 | Copyright (c) 2024 Apple Inc. and the swift-format project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See https://swift.org/LICENSE.txt for license information 8 | #]] 9 | 10 | add_library(_SwiftFormatInstructionCounter STATIC 11 | src/InstructionsExecuted.c) 12 | target_include_directories(_SwiftFormatInstructionCounter PUBLIC 13 | include) 14 | -------------------------------------------------------------------------------- /Sources/_SwiftFormatInstructionCounter/include/InstructionsExecuted.h: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | #include 14 | 15 | /// On macOS returns the number of instructions the process has executed since 16 | /// it was launched, on all other platforms returns 0. 17 | uint64_t getInstructionsExecuted(); 18 | -------------------------------------------------------------------------------- /Sources/_SwiftFormatInstructionCounter/include/module.modulemap: -------------------------------------------------------------------------------- 1 | module _SwiftFormatInstructionCounter { 2 | header "InstructionsExecuted.h" 3 | } 4 | -------------------------------------------------------------------------------- /Sources/_SwiftFormatInstructionCounter/src/InstructionsExecuted.c: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | #if __APPLE__ 14 | #include 15 | #if TARGET_OS_MAC && !TARGET_OS_IPHONE 16 | #define TARGET_IS_MACOS 1 17 | #endif 18 | #endif 19 | 20 | #include "InstructionsExecuted.h" 21 | 22 | #ifdef TARGET_IS_MACOS 23 | #include 24 | #include 25 | #include 26 | 27 | uint64_t getInstructionsExecuted() { 28 | struct rusage_info_v4 ru; 29 | if (proc_pid_rusage(getpid(), RUSAGE_INFO_V4, (rusage_info_t *)&ru) == 0) { 30 | return ru.ri_instructions; 31 | } 32 | return 0; 33 | } 34 | #else 35 | uint64_t getInstructionsExecuted() { 36 | return 0; 37 | } 38 | #endif 39 | -------------------------------------------------------------------------------- /Sources/_SwiftFormatTestSupport/Configuration+Testing.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftFormat 14 | 15 | extension Configuration { 16 | /// The default configuration to be used during unit tests. 17 | /// 18 | /// This configuration is separate from `Configuration.init()` so that that configuration can be 19 | /// replaced without breaking tests that implicitly rely on it. Unfortunately, since this is in a 20 | /// different module than where `Configuration` is defined, we can't make this an initializer that 21 | /// would enforce that every field of `Configuration` is initialized here (we're forced to 22 | /// delegate to another initializer first, which defeats the purpose). So, users adding new 23 | /// configuration settings should be sure to supply a default here for testing, otherwise they 24 | /// will be implicitly relying on the real default. 25 | public static var forTesting: Configuration { 26 | var config = Configuration() 27 | config.rules = Configuration.defaultRuleEnablements 28 | config.maximumBlankLines = 1 29 | config.lineLength = 100 30 | config.tabWidth = 8 31 | config.indentation = .spaces(2) 32 | config.respectsExistingLineBreaks = true 33 | config.lineBreakBeforeControlFlowKeywords = false 34 | config.lineBreakBeforeEachArgument = false 35 | config.lineBreakBeforeEachGenericRequirement = false 36 | config.prioritizeKeepingFunctionOutputTogether = false 37 | config.indentConditionalCompilationBlocks = true 38 | config.lineBreakAroundMultilineExpressionChainComponents = false 39 | config.fileScopedDeclarationPrivacy = FileScopedDeclarationPrivacyConfiguration() 40 | config.indentSwitchCaseLabels = false 41 | config.spacesAroundRangeFormationOperators = false 42 | config.noAssignmentInExpressions = NoAssignmentInExpressionsConfiguration() 43 | config.multiElementCollectionTrailingCommas = true 44 | config.indentBlankLines = false 45 | return config 46 | } 47 | 48 | public static func forTesting(enabledRule: String) -> Configuration { 49 | var config = Configuration.forTesting 50 | config.rules = config.rules.mapValues({ _ in false }) 51 | config.rules[enabledRule] = true 52 | return config 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Sources/_SwiftFormatTestSupport/FindingSpec.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | /// A description of a `Finding` that can be asserted during tests. 14 | public struct FindingSpec { 15 | /// The marker that identifies the finding. 16 | public var marker: String 17 | 18 | /// The message text associated with the finding. 19 | public var message: String 20 | 21 | /// A description of a `Note` that should be associated with this finding. 22 | public var notes: [NoteSpec] 23 | 24 | /// Creates a new `FindingSpec` with the given values. 25 | public init(_ marker: String = "1️⃣", message: String, notes: [NoteSpec] = []) { 26 | self.marker = marker 27 | self.message = message 28 | self.notes = notes 29 | } 30 | } 31 | 32 | /// A description of a `Note` that can be asserted during tests. 33 | public struct NoteSpec { 34 | /// The marker that identifies the note. 35 | public var marker: String 36 | 37 | /// The message text associated with the note. 38 | public var message: String 39 | 40 | /// Creates a new `NoteSpec` with the given values. 41 | public init(_ marker: String, message: String) { 42 | self.marker = marker 43 | self.message = message 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Sources/_SwiftFormatTestSupport/Parsing.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | @_spi(ExperimentalLanguageFeatures) import SwiftParser 14 | import SwiftSyntax 15 | import XCTest 16 | 17 | extension Parser { 18 | /// Parses the given source string and returns the corresponding `SourceFileSyntax` node. 19 | /// 20 | /// - Parameters: 21 | /// - source: The source text to parse. 22 | /// - experimentalFeatures: The set of experimental features that should be enabled in the 23 | /// parser. 24 | @_spi(Testing) 25 | public static func parse( 26 | source: String, 27 | experimentalFeatures: Parser.ExperimentalFeatures 28 | ) -> SourceFileSyntax { 29 | var source = source 30 | return source.withUTF8 { sourceBytes in 31 | parse( 32 | source: sourceBytes, 33 | experimentalFeatures: experimentalFeatures 34 | ) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/generate-swift-format/FileGenerator.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import Foundation 14 | 15 | /// Common behavior used to generate source files. 16 | protocol FileGenerator { 17 | /// Types conforming to this protocol must implement this method to write their content into the 18 | /// given file handle. 19 | func write(into handle: FileHandle) throws 20 | } 21 | 22 | private struct FailedToCreateFileError: Error { 23 | let url: URL 24 | } 25 | 26 | extension FileGenerator { 27 | /// Generates a file at the given URL, overwriting it if it already exists. 28 | func generateFile(at url: URL) throws { 29 | let fm = FileManager.default 30 | if fm.fileExists(atPath: url.path) { 31 | try fm.removeItem(at: url) 32 | } 33 | 34 | if !fm.createFile(atPath: url.path, contents: nil, attributes: nil) { 35 | throw FailedToCreateFileError(url: url) 36 | } 37 | let handle = try FileHandle(forWritingTo: url) 38 | defer { handle.closeFile() } 39 | 40 | try write(into: handle) 41 | } 42 | } 43 | 44 | extension FileHandle { 45 | /// Writes the provided string as data to a file output stream. 46 | public func write(_ string: String) { 47 | guard let data = string.data(using: .utf8) else { return } 48 | write(data) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Sources/generate-swift-format/RuleDocumentationGenerator.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import Foundation 14 | import SwiftFormat 15 | 16 | /// Generates the markdown file with extended documenation on the available rules. 17 | final class RuleDocumentationGenerator: FileGenerator { 18 | 19 | /// The rules collected by scanning the formatter source code. 20 | let ruleCollector: RuleCollector 21 | 22 | /// Creates a new rule registry generator. 23 | init(ruleCollector: RuleCollector) { 24 | self.ruleCollector = ruleCollector 25 | } 26 | 27 | func write(into handle: FileHandle) throws { 28 | handle.write( 29 | """ 30 | 31 | 32 | # `swift-format` Lint and Format Rules 33 | 34 | Use the rules below in the `rules` block of your `.swift-format` 35 | configuration file, as described in 36 | [Configuration](Documentation/Configuration.md). All of these rules can be 37 | applied in the linter, but only some of them can format your source code 38 | automatically. 39 | 40 | Here's the list of available rules: 41 | 42 | 43 | """ 44 | ) 45 | 46 | for detectedRule in ruleCollector.allLinters.sorted(by: { $0.typeName < $1.typeName }) { 47 | handle.write( 48 | """ 49 | - [\(detectedRule.typeName)](#\(detectedRule.typeName)) 50 | 51 | """ 52 | ) 53 | } 54 | 55 | for detectedRule in ruleCollector.allLinters.sorted(by: { $0.typeName < $1.typeName }) { 56 | handle.write( 57 | """ 58 | 59 | ### \(detectedRule.typeName) 60 | 61 | \(detectedRule.description ?? "") 62 | \(ruleFormatSupportDescription(for: detectedRule)) 63 | 64 | """ 65 | ) 66 | } 67 | } 68 | 69 | private func ruleFormatSupportDescription(for rule: RuleCollector.DetectedRule) -> String { 70 | return rule.canFormat 71 | ? "`\(rule.typeName)` rule can format your code automatically." : "`\(rule.typeName)` is a linter-only rule." 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Sources/generate-swift-format/RuleNameCacheGenerator.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import Foundation 14 | 15 | /// Generates the rule registry file used to populate the default configuration. 16 | final class RuleNameCacheGenerator: FileGenerator { 17 | 18 | /// The rules collected by scanning the formatter source code. 19 | let ruleCollector: RuleCollector 20 | 21 | /// Creates a new rule registry generator. 22 | init(ruleCollector: RuleCollector) { 23 | self.ruleCollector = ruleCollector 24 | } 25 | 26 | func write(into handle: FileHandle) throws { 27 | handle.write( 28 | """ 29 | //===----------------------------------------------------------------------===// 30 | // 31 | // This source file is part of the Swift.org open source project 32 | // 33 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 34 | // Licensed under Apache License v2.0 with Runtime Library Exception 35 | // 36 | // See https://swift.org/LICENSE.txt for license information 37 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 38 | // 39 | //===----------------------------------------------------------------------===// 40 | 41 | // This file is automatically generated with generate-swift-format. Do not edit! 42 | 43 | /// By default, the `Rule.ruleName` should be the name of the implementing rule type. 44 | @_spi(Testing) 45 | public let ruleNameCache: [ObjectIdentifier: String] = [ 46 | 47 | """ 48 | ) 49 | 50 | for detectedRule in ruleCollector.allLinters.sorted(by: { $0.typeName < $1.typeName }) { 51 | handle.write(" ObjectIdentifier(\(detectedRule.typeName).self): \"\(detectedRule.typeName)\",\n") 52 | } 53 | handle.write("]\n") 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Sources/generate-swift-format/RuleRegistryGenerator.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import Foundation 14 | 15 | /// Generates the rule registry file used to populate the default configuration. 16 | final class RuleRegistryGenerator: FileGenerator { 17 | 18 | /// The rules collected by scanning the formatter source code. 19 | let ruleCollector: RuleCollector 20 | 21 | /// Creates a new rule registry generator. 22 | init(ruleCollector: RuleCollector) { 23 | self.ruleCollector = ruleCollector 24 | } 25 | 26 | func write(into handle: FileHandle) throws { 27 | handle.write( 28 | """ 29 | //===----------------------------------------------------------------------===// 30 | // 31 | // This source file is part of the Swift.org open source project 32 | // 33 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 34 | // Licensed under Apache License v2.0 with Runtime Library Exception 35 | // 36 | // See https://swift.org/LICENSE.txt for license information 37 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 38 | // 39 | //===----------------------------------------------------------------------===// 40 | 41 | // This file is automatically generated with generate-swift-format. Do not edit! 42 | 43 | @_spi(Internal) public enum RuleRegistry { 44 | public static let rules: [String: Bool] = [ 45 | 46 | """ 47 | ) 48 | 49 | for detectedRule in ruleCollector.allLinters.sorted(by: { $0.typeName < $1.typeName }) { 50 | handle.write(" \"\(detectedRule.typeName)\": \(!detectedRule.isOptIn),\n") 51 | } 52 | handle.write(" ]\n}\n") 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Sources/generate-swift-format/Syntax+Convenience.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftSyntax 14 | 15 | extension SyntaxCollection { 16 | /// The first element in the syntax collection if it is the *only* element, or nil otherwise. 17 | public var firstAndOnly: Element? { 18 | var iterator = makeIterator() 19 | guard let first = iterator.next() else { return nil } 20 | guard iterator.next() == nil else { return nil } 21 | return first 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/generate-swift-format/main.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import Foundation 14 | import SwiftSyntax 15 | 16 | let sourcesDirectory = URL(fileURLWithPath: #file) 17 | .deletingLastPathComponent() 18 | .deletingLastPathComponent() 19 | let rulesDirectory = 20 | sourcesDirectory 21 | .appendingPathComponent("SwiftFormat") 22 | .appendingPathComponent("Rules") 23 | let pipelineFile = 24 | sourcesDirectory 25 | .appendingPathComponent("SwiftFormat") 26 | .appendingPathComponent("Core") 27 | .appendingPathComponent("Pipelines+Generated.swift") 28 | let ruleRegistryFile = 29 | sourcesDirectory 30 | .appendingPathComponent("SwiftFormat") 31 | .appendingPathComponent("Core") 32 | .appendingPathComponent("RuleRegistry+Generated.swift") 33 | 34 | let ruleNameCacheFile = 35 | sourcesDirectory 36 | .appendingPathComponent("SwiftFormat") 37 | .appendingPathComponent("Core") 38 | .appendingPathComponent("RuleNameCache+Generated.swift") 39 | 40 | let ruleDocumentationFile = 41 | sourcesDirectory 42 | .appendingPathComponent("..") 43 | .appendingPathComponent("Documentation") 44 | .appendingPathComponent("RuleDocumentation.md") 45 | 46 | var ruleCollector = RuleCollector() 47 | try ruleCollector.collect(from: rulesDirectory) 48 | 49 | // Generate a file with extensions for the lint and format pipelines. 50 | let pipelineGenerator = PipelineGenerator(ruleCollector: ruleCollector) 51 | try pipelineGenerator.generateFile(at: pipelineFile) 52 | 53 | // Generate the rule registry dictionary for configuration. 54 | let registryGenerator = RuleRegistryGenerator(ruleCollector: ruleCollector) 55 | try registryGenerator.generateFile(at: ruleRegistryFile) 56 | 57 | // Generate the rule name cache. 58 | let ruleNameCacheGenerator = RuleNameCacheGenerator(ruleCollector: ruleCollector) 59 | try ruleNameCacheGenerator.generateFile(at: ruleNameCacheFile) 60 | 61 | // Generate the Documentation/RuleDocumentation.md file with rule descriptions. 62 | // This uses DocC comments from rule implementations. 63 | let ruleDocumentationGenerator = RuleDocumentationGenerator(ruleCollector: ruleCollector) 64 | try ruleDocumentationGenerator.generateFile(at: ruleDocumentationFile) 65 | -------------------------------------------------------------------------------- /Sources/swift-format/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #[[ 2 | This source file is part of the swift-format open source project 3 | 4 | Copyright (c) 2024 - 2025 Apple Inc. and the swift-format project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See https://swift.org/LICENSE.txt for license information 8 | #]] 9 | 10 | add_executable(swift-format 11 | PrintVersion.swift 12 | SwiftFormatCommand.swift 13 | VersionOptions.swift 14 | Frontend/ConfigurationLoader.swift 15 | Frontend/FormatFrontend.swift 16 | Frontend/Frontend.swift 17 | Frontend/LintFrontend.swift 18 | Subcommands/ConfigurationOptions.swift 19 | Subcommands/DumpConfiguration.swift 20 | Subcommands/Format.swift 21 | Subcommands/Lint.swift 22 | Subcommands/LintFormatOptions.swift 23 | Subcommands/PerformanceMeasurement.swift 24 | Utilities/Diagnostic.swift 25 | Utilities/DiagnosticsEngine.swift 26 | Utilities/FileHandleTextOutputStream.swift 27 | Utilities/StderrDiagnosticPrinter.swift 28 | Utilities/TTY.swift) 29 | target_link_libraries(swift-format PRIVATE 30 | _SwiftFormatInstructionCounter 31 | ArgumentParser 32 | SwiftFormat 33 | SwiftParser 34 | SwiftSyntax) 35 | 36 | _install_target(swift-format) 37 | -------------------------------------------------------------------------------- /Sources/swift-format/Frontend/ConfigurationLoader.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import Foundation 14 | import SwiftFormat 15 | 16 | /// Loads formatter configurations, caching them in memory so that multiple operations in the same 17 | /// directory do not repeatedly hit the file system. 18 | struct ConfigurationLoader { 19 | /// The cache of previously loaded configurations. 20 | private var cache = [String: Configuration]() 21 | 22 | /// Returns the configuration found by walking up the file tree from `url`. 23 | /// This function works for both files and directories. 24 | /// 25 | /// If no configuration file was found during the search, this method returns nil. 26 | /// 27 | /// - Throws: If a configuration file was found but an error occurred loading it. 28 | mutating func configuration(forPath url: URL) throws -> Configuration? { 29 | guard let configurationFileURL = Configuration.url(forConfigurationFileApplyingTo: url) 30 | else { 31 | return nil 32 | } 33 | return try configuration(at: configurationFileURL) 34 | } 35 | 36 | /// Returns the configuration associated with the configuration file at the given URL. 37 | /// 38 | /// - Throws: If an error occurred loading the configuration. 39 | mutating func configuration(at url: URL) throws -> Configuration { 40 | let cacheKey = url.absoluteURL.standardized.path 41 | if let cachedConfiguration = cache[cacheKey] { 42 | return cachedConfiguration 43 | } 44 | 45 | let configuration = try Configuration(contentsOf: url) 46 | cache[cacheKey] = configuration 47 | return configuration 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Sources/swift-format/Frontend/LintFrontend.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import Foundation 14 | import SwiftDiagnostics 15 | import SwiftFormat 16 | import SwiftSyntax 17 | 18 | /// The frontend for linting operations. 19 | class LintFrontend: Frontend { 20 | override func processFile(_ fileToProcess: FileToProcess) { 21 | let linter = SwiftLinter( 22 | configuration: fileToProcess.configuration, 23 | findingConsumer: diagnosticsEngine.consumeFinding 24 | ) 25 | linter.debugOptions = debugOptions 26 | 27 | let url = fileToProcess.url 28 | guard let source = fileToProcess.sourceText else { 29 | diagnosticsEngine.emitError( 30 | "Unable to lint \(url.relativePath): file is not readable or does not exist." 31 | ) 32 | return 33 | } 34 | 35 | do { 36 | try linter.lint( 37 | source: source, 38 | assumingFileURL: url, 39 | experimentalFeatures: Set(lintFormatOptions.experimentalFeatures) 40 | ) { (diagnostic, location) in 41 | guard !self.lintFormatOptions.ignoreUnparsableFiles else { 42 | // No diagnostics should be emitted in this mode. 43 | return 44 | } 45 | self.diagnosticsEngine.consumeParserDiagnostic(diagnostic, location) 46 | } 47 | } catch SwiftFormatError.fileContainsInvalidSyntax { 48 | guard !lintFormatOptions.ignoreUnparsableFiles else { 49 | // The caller wants to silently ignore this error. 50 | return 51 | } 52 | // Otherwise, relevant diagnostics about the problematic nodes have already been emitted; we 53 | // don't need to print anything else. 54 | } catch { 55 | diagnosticsEngine.emitError("Unable to lint \(url.relativePath): \(error.localizedDescription).") 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Sources/swift-format/PrintVersion.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | func printVersionInformation() { 14 | // TODO: Automate updates to this somehow. 15 | print("main") 16 | } 17 | -------------------------------------------------------------------------------- /Sources/swift-format/Subcommands/ConfigurationOptions.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import ArgumentParser 14 | 15 | /// Common arguments used by the `lint`, `format` and `dump-configuration` subcommands. 16 | struct ConfigurationOptions: ParsableArguments { 17 | /// The path to the JSON configuration file that should be loaded. 18 | /// 19 | /// If not specified, the default configuration will be used. 20 | @Option( 21 | name: .customLong("configuration"), 22 | help: """ 23 | The path to a JSON file containing the configuration of the linter/formatter or a JSON string containing the \ 24 | configuration directly. 25 | """ 26 | ) 27 | var configuration: String? 28 | } 29 | -------------------------------------------------------------------------------- /Sources/swift-format/Subcommands/DumpConfiguration.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import ArgumentParser 14 | import Foundation 15 | import SwiftFormat 16 | 17 | extension SwiftFormatCommand { 18 | /// Dumps the tool's configuration in JSON format to standard output. 19 | struct DumpConfiguration: ParsableCommand { 20 | static var configuration = CommandConfiguration( 21 | abstract: "Dump the configuration in JSON format to standard output", 22 | discussion: """ 23 | Without any options, dumps the default configuration. When '--effective' is set, dumps the configuration that \ 24 | would be used if swift-format was executed from the current working directory (cwd), incorporating \ 25 | configuration files found in the cwd or its parents, or input from the '--configuration' option. 26 | """ 27 | ) 28 | 29 | /// Whether or not to dump the effective configuration. 30 | @Flag(name: .shortAndLong, help: "Dump the effective instead of the default configuration.") 31 | var effective: Bool = false 32 | 33 | @OptionGroup() 34 | var configurationOptions: ConfigurationOptions 35 | 36 | func validate() throws { 37 | if configurationOptions.configuration != nil && !effective { 38 | throw ValidationError("'--configuration' is only valid in combination with '--effective'") 39 | } 40 | } 41 | 42 | func run() throws { 43 | let diagnosticPrinter = StderrDiagnosticPrinter(colorMode: .auto) 44 | let diagnosticsEngine = DiagnosticsEngine(diagnosticsHandlers: [diagnosticPrinter.printDiagnostic]) 45 | 46 | let configuration: Configuration 47 | if effective { 48 | var configurationProvider = Frontend.ConfigurationProvider(diagnosticsEngine: diagnosticsEngine) 49 | 50 | guard 51 | let effectiveConfiguration = configurationProvider.provide( 52 | forConfigPathOrString: configurationOptions.configuration, 53 | orForSwiftFileAt: nil 54 | ) 55 | else { 56 | // Already diagnosed in the called method through the diagnosticsEngine. 57 | throw ExitCode.failure 58 | } 59 | 60 | configuration = effectiveConfiguration 61 | } else { 62 | configuration = Configuration() 63 | } 64 | 65 | do { 66 | print(try configuration.asJsonString()) 67 | } catch { 68 | diagnosticsEngine.emitError("\(error.localizedDescription)") 69 | throw ExitCode.failure 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Sources/swift-format/Subcommands/Format.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import ArgumentParser 14 | 15 | extension SwiftFormatCommand { 16 | /// Formats one or more files containing Swift code. 17 | struct Format: ParsableCommand { 18 | static var configuration = CommandConfiguration( 19 | abstract: "Format Swift source code", 20 | discussion: "When no files are specified, it expects the source from standard input." 21 | ) 22 | 23 | /// Whether or not to format the Swift file in-place. 24 | /// 25 | /// If specified, the current file is overwritten when formatting. 26 | @Flag( 27 | name: .shortAndLong, 28 | help: "Overwrite the current file when formatting." 29 | ) 30 | var inPlace: Bool = false 31 | 32 | @OptionGroup() 33 | var configurationOptions: ConfigurationOptions 34 | 35 | @OptionGroup() 36 | var formatOptions: LintFormatOptions 37 | 38 | @OptionGroup(visibility: .hidden) 39 | var performanceMeasurementOptions: PerformanceMeasurementsOptions 40 | 41 | func validate() throws { 42 | if inPlace && formatOptions.paths.isEmpty { 43 | throw ValidationError("'--in-place' is only valid when formatting files") 44 | } 45 | } 46 | 47 | func run() throws { 48 | try performanceMeasurementOptions.printingInstructionCountIfRequested() { 49 | let frontend = FormatFrontend( 50 | configurationOptions: configurationOptions, 51 | lintFormatOptions: formatOptions, 52 | inPlace: inPlace 53 | ) 54 | frontend.run() 55 | if frontend.diagnosticsEngine.hasErrors { throw ExitCode.failure } 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Sources/swift-format/Subcommands/Lint.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import ArgumentParser 14 | 15 | extension SwiftFormatCommand { 16 | /// Emits style diagnostics for one or more files containing Swift code. 17 | struct Lint: ParsableCommand { 18 | static var configuration = CommandConfiguration( 19 | abstract: "Diagnose style issues in Swift source code", 20 | discussion: "When no files are specified, it expects the source from standard input." 21 | ) 22 | 23 | @OptionGroup() 24 | var configurationOptions: ConfigurationOptions 25 | 26 | @OptionGroup() 27 | var lintOptions: LintFormatOptions 28 | 29 | @Flag( 30 | name: .shortAndLong, 31 | help: "Treat all findings as errors instead of warnings." 32 | ) 33 | var strict: Bool = false 34 | 35 | @OptionGroup(visibility: .hidden) 36 | var performanceMeasurementOptions: PerformanceMeasurementsOptions 37 | 38 | func run() throws { 39 | try performanceMeasurementOptions.printingInstructionCountIfRequested { 40 | let frontend = LintFrontend( 41 | configurationOptions: configurationOptions, 42 | lintFormatOptions: lintOptions, 43 | treatWarningsAsErrors: strict 44 | ) 45 | frontend.run() 46 | 47 | if frontend.diagnosticsEngine.hasErrors { 48 | throw ExitCode.failure 49 | } 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Sources/swift-format/Subcommands/PerformanceMeasurement.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import ArgumentParser 14 | import _SwiftFormatInstructionCounter 15 | 16 | struct PerformanceMeasurementsOptions: ParsableArguments { 17 | @Flag(help: "Measure number of instructions executed by swift-format") 18 | var measureInstructions = false 19 | 20 | /// If `measureInstructions` is set, execute `body` and print the number of instructions 21 | /// executed by it. Otherwise, just execute `body` 22 | func printingInstructionCountIfRequested(_ body: () throws -> T) rethrows -> T { 23 | if !measureInstructions { 24 | return try body() 25 | } else { 26 | let startInstructions = getInstructionsExecuted() 27 | defer { 28 | print("Instructions executed: \(getInstructionsExecuted() - startInstructions)") 29 | } 30 | return try body() 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/swift-format/SwiftFormatCommand.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import ArgumentParser 14 | 15 | /// Collects the command line options that were passed to `swift-format` and dispatches to the 16 | /// appropriate subcommand. 17 | @main 18 | struct SwiftFormatCommand: ParsableCommand { 19 | static var configuration = CommandConfiguration( 20 | commandName: "swift-format", 21 | abstract: "Format or lint Swift source code", 22 | subcommands: [ 23 | DumpConfiguration.self, 24 | Format.self, 25 | Lint.self, 26 | ], 27 | defaultSubcommand: Format.self 28 | ) 29 | 30 | @OptionGroup() 31 | var versionOptions: VersionOptions 32 | } 33 | -------------------------------------------------------------------------------- /Sources/swift-format/Utilities/Diagnostic.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftFormat 14 | import SwiftSyntax 15 | 16 | /// Diagnostic data that retains the separation of a finding category (if present) from the rest of 17 | /// the message, allowing diagnostic printers that want to print those values separately to do so. 18 | struct Diagnostic { 19 | /// The severity of the diagnostic. 20 | enum Severity { 21 | case note 22 | case warning 23 | case error 24 | } 25 | 26 | /// Represents the location of a diagnostic. 27 | struct Location { 28 | /// The file path associated with the diagnostic. 29 | var file: String 30 | 31 | /// The 1-based line number where the diagnostic occurred. 32 | var line: Int 33 | 34 | /// The 1-based column number where the diagnostic occurred. 35 | var column: Int 36 | 37 | /// Creates a new diagnostic location from the given source location. 38 | init(_ sourceLocation: SourceLocation) { 39 | self.file = sourceLocation.file 40 | self.line = sourceLocation.line 41 | self.column = sourceLocation.column 42 | } 43 | 44 | /// Creates a new diagnostic location with the given finding location. 45 | init(_ findingLocation: Finding.Location) { 46 | self.file = findingLocation.file 47 | self.line = findingLocation.line 48 | self.column = findingLocation.column 49 | } 50 | } 51 | 52 | /// The severity of the diagnostic. 53 | var severity: Severity 54 | 55 | /// The location where the diagnostic occurred, if known. 56 | var location: Location? 57 | 58 | /// The category of the diagnostic, if any. 59 | var category: String? 60 | 61 | /// The message text associated with the diagnostic. 62 | var message: String 63 | 64 | var description: String { 65 | if let category = category { 66 | return "[\(category)] \(message)" 67 | } else { 68 | return message 69 | } 70 | } 71 | 72 | /// Creates a new diagnostic with the given severity, location, optional category, and 73 | /// message. 74 | init(severity: Severity, location: Location?, category: String? = nil, message: String) { 75 | self.severity = severity 76 | self.location = location 77 | self.category = category 78 | self.message = message 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Sources/swift-format/Utilities/FileHandleTextOutputStream.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import Foundation 14 | 15 | /// Wraps a `FileHandle` so that it can be used by APIs that take a `TextOutputStream`-conforming 16 | /// type as an input. 17 | struct FileHandleTextOutputStream: TextOutputStream { 18 | /// The underlying file handle to which the text will be written. 19 | private var fileHandle: FileHandle 20 | 21 | /// Creates a new output stream that writes to the given file handle. 22 | init(_ fileHandle: FileHandle) { 23 | self.fileHandle = fileHandle 24 | } 25 | 26 | func write(_ string: String) { 27 | fileHandle.write(string.data(using: .utf8)!) // Conversion to UTF-8 cannot fail 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/swift-format/Utilities/TTY.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import Foundation 14 | 15 | #if os(Android) 16 | import Android 17 | #endif 18 | 19 | /// Returns a value indicating whether or not the stream is a TTY. 20 | func isTTY(_ fileHandle: FileHandle) -> Bool { 21 | // The implementation of this function is adapted from `TerminalController.swift` in 22 | // swift-tools-support-core. 23 | #if os(Windows) 24 | // The TSC implementation of this function only returns `.file` or `.dumb` for Windows, 25 | // neither of which is a TTY. 26 | return false 27 | #else 28 | if ProcessInfo.processInfo.environment["TERM"] == "dumb" { 29 | return false 30 | } 31 | return isatty(fileHandle.fileDescriptor) != 0 32 | #endif 33 | } 34 | -------------------------------------------------------------------------------- /Sources/swift-format/VersionOptions.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import ArgumentParser 14 | 15 | /// Encapsulates `--version` flag behavior. 16 | struct VersionOptions: ParsableArguments { 17 | @Flag(name: .shortAndLong, help: "Print the version and exit") 18 | var version: Bool = false 19 | 20 | func validate() throws { 21 | if version { 22 | printVersionInformation() 23 | throw ExitCode.success 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/SwiftFormatPerformanceTests/WhitespaceLinterPerformanceTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | @_spi(Testing) import SwiftFormat 14 | import SwiftParser 15 | import SwiftSyntax 16 | import XCTest 17 | @_spi(Testing) import _SwiftFormatTestSupport 18 | 19 | final class WhitespaceLinterPerformanceTests: DiagnosingTestCase { 20 | /// When executing in Swift CI, run the block to make sure it doesn't hit any assertions because we don't look at 21 | /// performance numbers in CI and CI nodes can have variable performance characteristics if they are not bare-metal. 22 | /// 23 | /// Anywhere else, run XCTest's `measure` function to measure the performance of the block. 24 | private func measureIfNotInCI(_ block: () -> Void) { 25 | if ProcessInfo.processInfo.environment["SWIFTCI_USE_LOCAL_DEPS"] != nil { 26 | block() 27 | } else { 28 | measure { block() } 29 | } 30 | } 31 | 32 | func testWhitespaceLinterPerformance() throws { 33 | let input = String( 34 | repeating: """ 35 | import SomeModule 36 | public class SomeClass : SomeProtocol 37 | { 38 | var someProperty : SomeType { 39 | get{5}set{doSomething()} 40 | } 41 | public 42 | func 43 | someFunctionName 44 | ( 45 | firstArg : FirstArgument , secondArg : 46 | SecondArgument){ 47 | doSomeThings() 48 | }} 49 | 50 | """, 51 | count: 20 52 | ) 53 | let expected = String( 54 | repeating: """ 55 | import SomeModule 56 | public class SomeClass: SomeProtocol { 57 | var someProperty: SomeType { 58 | get { 5 } 59 | set { doSomething() } 60 | } 61 | public func someFunctionName( 62 | firstArg: FirstArgument, 63 | secondArg: SecondArgument 64 | ) { 65 | doSomeThings() 66 | } 67 | } 68 | 69 | """, 70 | count: 20 71 | ) 72 | 73 | measureIfNotInCI { performWhitespaceLint(input: input, expected: expected) } 74 | } 75 | 76 | /// Perform whitespace linting by comparing the input text from the user with the expected 77 | /// formatted text, using the default configuration. 78 | /// 79 | /// - Parameters: 80 | /// - input: The user's input text. 81 | /// - expected: The formatted text. 82 | private func performWhitespaceLint(input: String, expected: String) { 83 | let sourceFileSyntax = Parser.parse(source: input) 84 | let context = makeContext( 85 | sourceFileSyntax: sourceFileSyntax, 86 | selection: .infinite, 87 | findingConsumer: { _ in } 88 | ) 89 | let linter = WhitespaceLinter(user: input, formatted: expected, context: context) 90 | linter.lint() 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/PrettyPrint/AsExprTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import XCTest 14 | 15 | final class AsExprTests: PrettyPrintTestCase { 16 | func testWithoutPunctuation() throws { 17 | let input = 18 | """ 19 | func foo() { 20 | let a = b as Int 21 | a = b as Int 22 | let reallyLongVariableName = x as ReallyLongTypeName 23 | reallyLongVariableName = x as ReallyLongTypeName 24 | } 25 | """ 26 | 27 | let expected = 28 | """ 29 | func foo() { 30 | let a = b as Int 31 | a = b as Int 32 | let reallyLongVariableName = 33 | x as ReallyLongTypeName 34 | reallyLongVariableName = 35 | x as ReallyLongTypeName 36 | } 37 | 38 | """ 39 | 40 | assertPrettyPrintEqual(input: input, expected: expected, linelength: 40) 41 | } 42 | 43 | func testWithPunctuation() throws { 44 | let input = 45 | """ 46 | func foo() { 47 | let a = b as? Int 48 | a = b as? Int 49 | let c = d as! Int 50 | c = d as! Int 51 | let reallyLongVariableName = x as? ReallyLongTypeName 52 | reallyLongVariableName = x as? ReallyLongTypeName 53 | } 54 | """ 55 | 56 | let expected = 57 | """ 58 | func foo() { 59 | let a = b as? Int 60 | a = b as? Int 61 | let c = d as! Int 62 | c = d as! Int 63 | let reallyLongVariableName = 64 | x as? ReallyLongTypeName 65 | reallyLongVariableName = 66 | x as? ReallyLongTypeName 67 | } 68 | 69 | """ 70 | 71 | assertPrettyPrintEqual(input: input, expected: expected, linelength: 40) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/PrettyPrint/BackDeployAttributeTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | final class BackDeployAttributeTests: PrettyPrintTestCase { 14 | func testSpacingAndWrapping() { 15 | let input = 16 | """ 17 | @backDeployed(before:iOS 17) 18 | public func hello() {} 19 | 20 | @backDeployed(before:iOS 17,macOS 14) 21 | public func hello() {} 22 | 23 | @backDeployed(before:iOS 17,macOS 14,tvOS 17) 24 | public func hello() {} 25 | """ 26 | 27 | let expected80 = 28 | """ 29 | @backDeployed(before: iOS 17) 30 | public func hello() {} 31 | 32 | @backDeployed(before: iOS 17, macOS 14) 33 | public func hello() {} 34 | 35 | @backDeployed(before: iOS 17, macOS 14, tvOS 17) 36 | public func hello() {} 37 | 38 | """ 39 | 40 | assertPrettyPrintEqual(input: input, expected: expected80, linelength: 80) 41 | 42 | let expected28 = 43 | """ 44 | @backDeployed( 45 | before: iOS 17 46 | ) 47 | public func hello() {} 48 | 49 | @backDeployed( 50 | before: iOS 17, macOS 14 51 | ) 52 | public func hello() {} 53 | 54 | @backDeployed( 55 | before: 56 | iOS 17, macOS 14, 57 | tvOS 17 58 | ) 59 | public func hello() {} 60 | 61 | """ 62 | 63 | assertPrettyPrintEqual(input: input, expected: expected28, linelength: 28) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/PrettyPrint/BacktickTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | final class BacktickTests: PrettyPrintTestCase { 14 | func testBackticks() { 15 | let input = 16 | """ 17 | let `case` = 123 18 | enum MyEnum { 19 | case `break` 20 | case `continue` 21 | case `case`(var1: Int, Double) 22 | } 23 | 24 | """ 25 | 26 | let expected = 27 | """ 28 | let `case` = 123 29 | enum MyEnum { 30 | case `break` 31 | case `continue` 32 | case `case`(var1: Int, Double) 33 | } 34 | 35 | """ 36 | 37 | assertPrettyPrintEqual(input: input, expected: expected, linelength: 45) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/PrettyPrint/ConstrainedSugarTypeTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | final class ConstrainedSugarTypeTests: PrettyPrintTestCase { 14 | func testSomeTypes() { 15 | let input = 16 | """ 17 | var body: some View 18 | func foo() -> some Foo 19 | """ 20 | 21 | assertPrettyPrintEqual(input: input, expected: input + "\n", linelength: 25) 22 | 23 | let expected11 = 24 | """ 25 | var body: 26 | some View 27 | func foo() 28 | -> some Foo 29 | 30 | """ 31 | assertPrettyPrintEqual(input: input, expected: expected11, linelength: 11) 32 | } 33 | 34 | func testAnyTypes() { 35 | let input = 36 | """ 37 | var body: any View 38 | func foo() -> any Foo 39 | """ 40 | 41 | assertPrettyPrintEqual(input: input, expected: input + "\n", linelength: 25) 42 | 43 | let expected11 = 44 | """ 45 | var body: 46 | any View 47 | func foo() 48 | -> any Foo 49 | 50 | """ 51 | assertPrettyPrintEqual(input: input, expected: expected11, linelength: 11) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/PrettyPrint/ConsumeExprTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | final class ConsumeExprTests: PrettyPrintTestCase { 14 | func testConsume() { 15 | assertPrettyPrintEqual( 16 | input: """ 17 | let x = consume y 18 | """, 19 | expected: """ 20 | let x = 21 | consume y 22 | 23 | """, 24 | linelength: 16 25 | ) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/PrettyPrint/CopyExprSyntax.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | final class CopyExprTests: PrettyPrintTestCase { 14 | func testCopy() { 15 | assertPrettyPrintEqual( 16 | input: """ 17 | let x = copy y 18 | """, 19 | expected: """ 20 | let x = 21 | copy y 22 | 23 | """, 24 | linelength: 13 25 | ) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/PrettyPrint/DeinitializerDeclTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | final class DeinitializerDeclTests: PrettyPrintTestCase { 14 | func testBasicDeinitializerDeclarations() { 15 | let input = 16 | """ 17 | struct Struct { 18 | deinit { 19 | print("Hello World") 20 | let a = 23 21 | } 22 | deinit { let a = 23 } 23 | deinit { let a = "AAAA BBBB CCCC DDDD EEEE FFFF" } 24 | } 25 | """ 26 | 27 | let expected = 28 | """ 29 | struct Struct { 30 | deinit { 31 | print("Hello World") 32 | let a = 23 33 | } 34 | deinit { let a = 23 } 35 | deinit { 36 | let a = "AAAA BBBB CCCC DDDD EEEE FFFF" 37 | } 38 | } 39 | 40 | """ 41 | 42 | assertPrettyPrintEqual(input: input, expected: expected, linelength: 50) 43 | } 44 | 45 | func testDeinitializerAttributes() { 46 | let input = 47 | """ 48 | struct Struct { 49 | @objc deinit { 50 | let a = 123 51 | let b = "abc" 52 | } 53 | @objc @inlinable deinit { 54 | let a = 123 55 | let b = "abc" 56 | } 57 | @objc @available(swift 4.0) deinit { 58 | let a = 123 59 | let b = "abc" 60 | } 61 | } 62 | """ 63 | 64 | let expected = 65 | """ 66 | struct Struct { 67 | @objc deinit { 68 | let a = 123 69 | let b = "abc" 70 | } 71 | @objc @inlinable deinit 72 | { 73 | let a = 123 74 | let b = "abc" 75 | } 76 | @objc 77 | @available(swift 4.0) 78 | deinit { 79 | let a = 123 80 | let b = "abc" 81 | } 82 | } 83 | 84 | """ 85 | 86 | assertPrettyPrintEqual(input: input, expected: expected, linelength: 26) 87 | } 88 | 89 | func testEmptyDeinitializer() { 90 | // The comment inside the class prevents it from *also* being collapsed onto a single line. 91 | let input = """ 92 | class X { 93 | // 94 | deinit {} 95 | } 96 | """ 97 | assertPrettyPrintEqual(input: input, expected: input + "\n", linelength: 50) 98 | 99 | let wrapped = """ 100 | class X { 101 | // 102 | deinit { 103 | } 104 | } 105 | 106 | """ 107 | assertPrettyPrintEqual(input: input, expected: wrapped, linelength: 10) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/PrettyPrint/DiscardStmtTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | final class DiscardStmtTests: PrettyPrintTestCase { 14 | func testDiscard() { 15 | assertPrettyPrintEqual( 16 | input: """ 17 | discard self 18 | """, 19 | expected: """ 20 | discard self 21 | 22 | """, 23 | linelength: 9 24 | ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/PrettyPrint/ImportTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | final class ImportTests: PrettyPrintTestCase { 14 | func testImports() { 15 | let input = 16 | """ 17 | import someModule 18 | import someLongerModule.withSubmodules 19 | import class MyModule.MyClass 20 | import struct MyModule.MyStruct 21 | @testable import testModule 22 | 23 | @_spi( 24 | STP 25 | ) 26 | @testable 27 | import testModule 28 | """ 29 | 30 | let expected = 31 | """ 32 | import someModule 33 | import someLongerModule.withSubmodules 34 | import class MyModule.MyClass 35 | import struct MyModule.MyStruct 36 | @testable import testModule 37 | 38 | @_spi(STP) @testable import testModule 39 | 40 | """ 41 | 42 | // Imports should not wrap 43 | assertPrettyPrintEqual(input: input, expected: expected, linelength: 5) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/PrettyPrint/MemberTypeIdentifierTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | final class MemberTypeIdentifierTests: PrettyPrintTestCase { 14 | func testMemberTypes() { 15 | let input = 16 | """ 17 | let a: One.Two.Three.Four.Five 18 | let b: One.Two.Three 19 | """ 20 | 21 | let expected = 22 | """ 23 | let a: 24 | One.Two.Three 25 | .Four.Five 26 | let b: 27 | One.Two 28 | .Three< 29 | Four, 30 | Five 31 | > 32 | 33 | """ 34 | 35 | assertPrettyPrintEqual(input: input, expected: expected, linelength: 15) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/PrettyPrint/NewlineTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | final class NewlineTests: PrettyPrintTestCase { 14 | func testLeadingNewlines() { 15 | let input = 16 | """ 17 | 18 | 19 | let a = 123 20 | """ 21 | 22 | let expected = 23 | """ 24 | let a = 123 25 | 26 | """ 27 | 28 | assertPrettyPrintEqual(input: input, expected: expected, linelength: 45) 29 | } 30 | 31 | func testLeadingNewlinesWithComments() { 32 | let input = 33 | """ 34 | 35 | 36 | // Comment 37 | 38 | let a = 123 39 | """ 40 | 41 | let expected = 42 | """ 43 | // Comment 44 | 45 | let a = 123 46 | 47 | """ 48 | 49 | assertPrettyPrintEqual(input: input, expected: expected, linelength: 45) 50 | } 51 | 52 | func testTrailingNewlines() { 53 | let input = 54 | """ 55 | let a = 123 56 | 57 | 58 | """ 59 | 60 | let expected = 61 | """ 62 | let a = 123 63 | 64 | """ 65 | 66 | assertPrettyPrintEqual(input: input, expected: expected, linelength: 45) 67 | } 68 | 69 | func testTrailingNewlinesWithComments() { 70 | let input = 71 | """ 72 | let a = 123 73 | 74 | // Comment 75 | 76 | 77 | """ 78 | 79 | let expected = 80 | """ 81 | let a = 123 82 | 83 | // Comment 84 | 85 | """ 86 | 87 | assertPrettyPrintEqual(input: input, expected: expected, linelength: 45) 88 | } 89 | 90 | func testNewlinesBetweenMembers() { 91 | let input = 92 | """ 93 | 94 | 95 | class MyClazz { 96 | 97 | lazy var memberView: UIView = { 98 | let view = UIView() 99 | return view 100 | }() 101 | 102 | 103 | func doSomething() { 104 | print("!") 105 | } 106 | 107 | 108 | func doSomethingElse() { 109 | print("else!") 110 | } 111 | 112 | 113 | let constMember = 1 114 | 115 | 116 | 117 | } 118 | """ 119 | 120 | let expected = 121 | """ 122 | class MyClazz { 123 | 124 | lazy var memberView: UIView = { 125 | let view = UIView() 126 | return view 127 | }() 128 | 129 | func doSomething() { 130 | print("!") 131 | } 132 | 133 | func doSomethingElse() { 134 | print("else!") 135 | } 136 | 137 | let constMember = 1 138 | 139 | } 140 | 141 | """ 142 | 143 | assertPrettyPrintEqual(input: input, expected: expected, linelength: 100) 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/PrettyPrint/ParameterPackTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | final class ParameterPackTests: PrettyPrintTestCase { 14 | func testGenericPackArgument() { 15 | assertPrettyPrintEqual( 16 | input: """ 17 | func someFunction() {} 18 | struct SomeStruct {} 19 | """, 20 | expected: """ 21 | func someFunction< 22 | each P 23 | >() {} 24 | struct SomeStruct< 25 | each P 26 | > {} 27 | 28 | """, 29 | linelength: 22 30 | ) 31 | } 32 | 33 | func testPackExpansionsAndElements() { 34 | assertPrettyPrintEqual( 35 | input: """ 36 | repeat checkNilness(of: each value) 37 | """, 38 | expected: """ 39 | repeat checkNilness( 40 | of: each value) 41 | 42 | """, 43 | linelength: 25 44 | ) 45 | 46 | assertPrettyPrintEqual( 47 | input: """ 48 | repeat f(of: each v) 49 | """, 50 | expected: """ 51 | repeat 52 | f( 53 | of: 54 | each v 55 | ) 56 | 57 | """, 58 | linelength: 7 59 | ) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/PrettyPrint/PatternBindingTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftFormat 14 | 15 | final class PatternBindingTests: PrettyPrintTestCase { 16 | func testBindingIncludingTypeAnnotation() { 17 | let input = 18 | """ 19 | let someObject: Foo = object 20 | let someObject: (foo: Foo, bar: SomeVeryLongTypeNameThatDefinitelyBreaks, baz: Baz) = foo(a, b, c, d) 21 | """ 22 | 23 | let expected = 24 | """ 25 | let someObject: Foo = object 26 | let someObject: 27 | ( 28 | foo: Foo, 29 | bar: 30 | SomeVeryLongTypeNameThatDefinitelyBreaks, 31 | baz: Baz 32 | ) = foo(a, b, c, d) 33 | 34 | """ 35 | 36 | assertPrettyPrintEqual(input: input, expected: expected, linelength: 40) 37 | } 38 | 39 | func testIgnoresDiscretionaryNewlineAfterColon() { 40 | let input = 41 | """ 42 | let someObject: 43 | Foo = object 44 | let someObject: 45 | Foo = longerObjectName 46 | """ 47 | 48 | let expected = 49 | """ 50 | let someObject: Foo = object 51 | let someObject: Foo = 52 | longerObjectName 53 | 54 | """ 55 | 56 | assertPrettyPrintEqual(input: input, expected: expected, linelength: 28) 57 | } 58 | 59 | func testGroupingIncludesTrailingComma() { 60 | let input = 61 | """ 62 | let foo = veryLongCondition 63 | ? firstOption 64 | : secondOption, 65 | bar = bar() 66 | """ 67 | 68 | let expected = 69 | """ 70 | let 71 | foo = 72 | veryLongCondition 73 | ? firstOption 74 | : secondOption, 75 | bar = bar() 76 | 77 | """ 78 | 79 | assertPrettyPrintEqual(input: input, expected: expected, linelength: 18) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/PrettyPrint/SemicolonTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | final class SemiColonTypeTests: PrettyPrintTestCase { 14 | func testSemicolon() { 15 | let input = 16 | """ 17 | var foo = false 18 | guard !foo else { return }; defer { foo = true } 19 | 20 | struct Foo { 21 | var foo = false; var bar = true; var baz = false 22 | } 23 | """ 24 | 25 | assertPrettyPrintEqual(input: input, expected: input + "\n", linelength: 50) 26 | } 27 | 28 | func testNoSemicolon() { 29 | let input = 30 | """ 31 | var foo = false 32 | guard !foo else { return } 33 | defer { foo = true } 34 | 35 | struct Foo { 36 | var foo = false 37 | var bar = true 38 | var baz = false 39 | } 40 | """ 41 | 42 | assertPrettyPrintEqual(input: input, expected: input + "\n", linelength: 50) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/PrettyPrint/TypeAliasTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | final class TypeAliasTests: PrettyPrintTestCase { 14 | func testTypealias() { 15 | let input = 16 | """ 17 | typealias MyAlias = Int 18 | public typealias MyAlias = Array 19 | typealias MyAlias = (Bool, Int) 20 | typealias MyAlias = (SomeType?) -> Bool 21 | typealias MyAlias = (_ a: Int, _ b: Double) -> Bool 22 | typealias MyAlias = (_ a: Int, _ b: Double, _ c: Bool, _ d: String) -> Bool 23 | """ 24 | 25 | let expected = 26 | """ 27 | typealias MyAlias = Int 28 | public typealias MyAlias = Array 29 | typealias MyAlias = (Bool, Int) 30 | typealias MyAlias = (SomeType?) -> Bool 31 | typealias MyAlias = (_ a: Int, _ b: Double) -> Bool 32 | typealias MyAlias = ( 33 | _ a: Int, _ b: Double, _ c: Bool, _ d: String 34 | ) -> Bool 35 | 36 | """ 37 | 38 | assertPrettyPrintEqual(input: input, expected: expected, linelength: 60) 39 | } 40 | 41 | func testTypealiasAttributes() { 42 | let input = 43 | """ 44 | @objc typealias MyAlias = Int 45 | @objc @available(swift 4.0) typealias MyAlias = Int 46 | """ 47 | 48 | let expected = 49 | """ 50 | @objc typealias MyAlias = Int 51 | @objc @available(swift 4.0) typealias MyAlias = Int 52 | 53 | """ 54 | 55 | assertPrettyPrintEqual(input: input, expected: expected, linelength: 60) 56 | } 57 | 58 | func testTypealiasGenericTests() { 59 | let input = 60 | """ 61 | typealias MyDict = Dictionary 62 | typealias MyType = AnotherType 63 | """ 64 | 65 | let expected = 66 | """ 67 | typealias MyDict = Dictionary 68 | typealias MyType = AnotherType 69 | 70 | """ 71 | 72 | assertPrettyPrintEqual(input: input, expected: expected, linelength: 60) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/PrettyPrint/ValueGenericsTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | @_spi(ExperimentalLanguageFeatures) import SwiftParser 14 | 15 | final class ValueGenericsTests: PrettyPrintTestCase { 16 | func testValueGenericDeclaration() { 17 | let input = "struct Foo { static let bar = n }" 18 | let expected = """ 19 | struct Foo< 20 | let n: Int 21 | > { 22 | static let bar = n 23 | } 24 | 25 | """ 26 | assertPrettyPrintEqual( 27 | input: input, 28 | expected: expected, 29 | linelength: 20 30 | ) 31 | } 32 | 33 | func testValueGenericTypeUsage() { 34 | let input = 35 | """ 36 | let v1: Vector<100, Int> 37 | let v2 = Vector<100, Int>() 38 | """ 39 | let expected = """ 40 | let v1: 41 | Vector< 42 | 100, Int 43 | > 44 | let v2 = 45 | Vector< 46 | 100, Int 47 | >() 48 | 49 | """ 50 | assertPrettyPrintEqual( 51 | input: input, 52 | expected: expected, 53 | linelength: 15 54 | ) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/PrettyPrint/WhitespaceTestCase.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftFormat 14 | @_spi(Testing) import SwiftFormat 15 | import SwiftParser 16 | import SwiftSyntax 17 | import XCTest 18 | @_spi(Testing) import _SwiftFormatTestSupport 19 | 20 | class WhitespaceTestCase: DiagnosingTestCase { 21 | /// Perform whitespace linting by comparing the input text from the user with the expected 22 | /// formatted text. 23 | /// 24 | /// - Parameters: 25 | /// - input: The user's input text. 26 | /// - expected: The formatted text. 27 | /// - linelength: The maximum allowed line length of the output. 28 | /// - findings: A list of `FindingSpec` values that describe the findings that are expected to 29 | /// be emitted. 30 | /// - file: The file the test resides in (defaults to the current caller's file). 31 | /// - line: The line the test resides in (defaults to the current caller's line). 32 | final func assertWhitespaceLint( 33 | input: String, 34 | expected: String, 35 | linelength: Int? = nil, 36 | findings: [FindingSpec], 37 | file: StaticString = #file, 38 | line: UInt = #line 39 | ) { 40 | let markedText = MarkedText(textWithMarkers: input) 41 | 42 | let sourceFileSyntax = Parser.parse(source: markedText.textWithoutMarkers) 43 | var configuration = Configuration.forTesting 44 | if let linelength = linelength { 45 | configuration.lineLength = linelength 46 | } 47 | 48 | var emittedFindings = [Finding]() 49 | 50 | let context = makeContext( 51 | sourceFileSyntax: sourceFileSyntax, 52 | configuration: configuration, 53 | selection: .infinite, 54 | findingConsumer: { emittedFindings.append($0) } 55 | ) 56 | let linter = WhitespaceLinter( 57 | user: markedText.textWithoutMarkers, 58 | formatted: expected, 59 | context: context 60 | ) 61 | linter.lint() 62 | 63 | assertFindings( 64 | expected: findings, 65 | markerLocations: markedText.markers, 66 | emittedFindings: emittedFindings, 67 | context: context, 68 | file: file, 69 | line: line 70 | ) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/PrettyPrint/YieldStmtTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | final class YieldStmtTests: PrettyPrintTestCase { 14 | func testBasic() { 15 | let input = 16 | """ 17 | var foo: Int { 18 | _read{ 19 | yield 1234567890 20 | } 21 | _modify{ 22 | var someLongVariable = 0 23 | yield &someLongVariable 24 | } 25 | } 26 | """ 27 | 28 | let expected = 29 | """ 30 | var foo: Int { 31 | _read { 32 | yield 1234567890 33 | } 34 | _modify { 35 | var someLongVariable = 36 | 0 37 | yield &someLongVariable 38 | } 39 | } 40 | 41 | """ 42 | 43 | assertPrettyPrintEqual(input: input, expected: expected, linelength: 19) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/Rules/AmbiguousTrailingClosureOverloadTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | @_spi(Rules) import SwiftFormat 14 | import _SwiftFormatTestSupport 15 | 16 | final class AmbiguousTrailingClosureOverloadTests: LintOrFormatRuleTestCase { 17 | func testAmbiguousOverloads() { 18 | assertLint( 19 | AmbiguousTrailingClosureOverload.self, 20 | """ 21 | func 1️⃣strong(mad: () -> Int) {} 22 | func 2️⃣strong(bad: (Bool) -> Bool) {} 23 | func 3️⃣strong(sad: (String) -> Bool) {} 24 | 25 | class A { 26 | static func 4️⃣the(cheat: (Int) -> Void) {} 27 | class func 5️⃣the(sneak: (Int) -> Void) {} 28 | func 6️⃣the(kingOfTown: () -> Void) {} 29 | func 7️⃣the(cheatCommandos: (Bool) -> Void) {} 30 | func 8️⃣the(brothersStrong: (String) -> Void) {} 31 | } 32 | 33 | struct B { 34 | func 9️⃣hom(estar: () -> Int) {} 35 | func 🔟hom(sar: () -> Bool) {} 36 | 37 | static func baleeted(_ f: () -> Void) {} 38 | func baleeted(_ f: () -> Void) {} 39 | } 40 | """, 41 | findings: [ 42 | FindingSpec( 43 | "1️⃣", 44 | message: "rename 'strong(mad:)' so it is no longer ambiguous when called with a trailing closure", 45 | notes: [ 46 | NoteSpec("2️⃣", message: "ambiguous overload 'strong(bad:)' is here"), 47 | NoteSpec("3️⃣", message: "ambiguous overload 'strong(sad:)' is here"), 48 | ] 49 | ), 50 | FindingSpec( 51 | "4️⃣", 52 | message: "rename 'the(cheat:)' so it is no longer ambiguous when called with a trailing closure", 53 | notes: [ 54 | NoteSpec("5️⃣", message: "ambiguous overload 'the(sneak:)' is here") 55 | ] 56 | ), 57 | FindingSpec( 58 | "6️⃣", 59 | message: "rename 'the(kingOfTown:)' so it is no longer ambiguous when called with a trailing closure", 60 | notes: [ 61 | NoteSpec("7️⃣", message: "ambiguous overload 'the(cheatCommandos:)' is here"), 62 | NoteSpec("8️⃣", message: "ambiguous overload 'the(brothersStrong:)' is here"), 63 | ] 64 | ), 65 | FindingSpec( 66 | "9️⃣", 67 | message: "rename 'hom(estar:)' so it is no longer ambiguous when called with a trailing closure", 68 | notes: [ 69 | NoteSpec("🔟", message: "ambiguous overload 'hom(sar:)' is here") 70 | ] 71 | ), 72 | ] 73 | ) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/Rules/AvoidRetroactiveConformancesTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | @_spi(Rules) import SwiftFormat 14 | import _SwiftFormatTestSupport 15 | 16 | final class AvoidRetroactiveConformancesTests: LintOrFormatRuleTestCase { 17 | func testRetroactiveConformanceIsDiagnosed() { 18 | assertLint( 19 | AvoidRetroactiveConformances.self, 20 | """ 21 | extension Int: 1️⃣@retroactive Identifiable {} 22 | """, 23 | findings: [ 24 | FindingSpec("1️⃣", message: "do not declare retroactive conformances") 25 | ] 26 | ) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/Rules/GroupNumericLiteralsTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | @_spi(Rules) import SwiftFormat 14 | import _SwiftFormatTestSupport 15 | 16 | final class GroupNumericLiteralsTests: LintOrFormatRuleTestCase { 17 | func testNumericGrouping() { 18 | assertFormatting( 19 | GroupNumericLiterals.self, 20 | input: """ 21 | let a = 1️⃣9876543210 22 | let b = 1234 23 | let c = 2️⃣0x34950309233 24 | let d = -0x34242 25 | let e = 3️⃣0b10010010101 26 | let f = 0b101 27 | let g = 11_15_1999 28 | let h = 0o21743 29 | let i = -4️⃣53096828347 30 | let j = 5️⃣0000123 31 | let k = 6️⃣0x00000012 32 | let l = 0x0000012 33 | let m = 7️⃣0b00010010101 34 | let n = [ 35 | 8️⃣0xff00ff00, // comment 36 | 9️⃣0x00ff00ff, // comment 37 | ] 38 | """, 39 | expected: """ 40 | let a = 9_876_543_210 41 | let b = 1234 42 | let c = 0x349_5030_9233 43 | let d = -0x34242 44 | let e = 0b100_10010101 45 | let f = 0b101 46 | let g = 11_15_1999 47 | let h = 0o21743 48 | let i = -53_096_828_347 49 | let j = 0_000_123 50 | let k = 0x0000_0012 51 | let l = 0x0000012 52 | let m = 0b000_10010101 53 | let n = [ 54 | 0xff00_ff00, // comment 55 | 0x00ff_00ff, // comment 56 | ] 57 | """, 58 | findings: [ 59 | FindingSpec("1️⃣", message: "group every 3 digits in this decimal literal using a '_' separator"), 60 | FindingSpec("2️⃣", message: "group every 4 digits in this hexadecimal literal using a '_' separator"), 61 | FindingSpec("3️⃣", message: "group every 8 digits in this binary literal using a '_' separator"), 62 | FindingSpec("4️⃣", message: "group every 3 digits in this decimal literal using a '_' separator"), 63 | FindingSpec("5️⃣", message: "group every 3 digits in this decimal literal using a '_' separator"), 64 | FindingSpec("6️⃣", message: "group every 4 digits in this hexadecimal literal using a '_' separator"), 65 | FindingSpec("7️⃣", message: "group every 8 digits in this binary literal using a '_' separator"), 66 | FindingSpec("8️⃣", message: "group every 4 digits in this hexadecimal literal using a '_' separator"), 67 | FindingSpec("9️⃣", message: "group every 4 digits in this hexadecimal literal using a '_' separator"), 68 | ] 69 | ) 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/Rules/IdentifiersMustBeASCIITests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | @_spi(Rules) import SwiftFormat 14 | import _SwiftFormatTestSupport 15 | 16 | final class IdentifiersMustBeASCIITests: LintOrFormatRuleTestCase { 17 | func testInvalidIdentifiers() { 18 | assertLint( 19 | IdentifiersMustBeASCII.self, 20 | """ 21 | let Te$t = 1 22 | var 1️⃣fo😎o = 2 23 | let 2️⃣Δx = newX - previousX 24 | var 3️⃣🤩😆 = 20 25 | """, 26 | findings: [ 27 | FindingSpec("1️⃣", message: "remove non-ASCII characters from 'fo😎o': 😎"), 28 | // TODO: It would be nice to allow Δ (among other mathematically meaningful symbols) without 29 | // a lot of special cases; investigate this. 30 | FindingSpec("2️⃣", message: "remove non-ASCII characters from 'Δx': Δ"), 31 | FindingSpec("3️⃣", message: "remove non-ASCII characters from '🤩😆': 🤩, 😆"), 32 | ] 33 | ) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/Rules/ImportsXCTestVisitorTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | import SwiftFormat 14 | @_spi(Rules) @_spi(Testing) import SwiftFormat 15 | import SwiftParser 16 | import XCTest 17 | 18 | class ImportsXCTestVisitorTests: XCTestCase { 19 | func testDoesNotImportXCTest() throws { 20 | XCTAssertEqual( 21 | try makeContextAndSetImportsXCTest( 22 | source: """ 23 | import Foundation 24 | """ 25 | ), 26 | .doesNotImportXCTest 27 | ) 28 | } 29 | 30 | func testImportsXCTest() throws { 31 | XCTAssertEqual( 32 | try makeContextAndSetImportsXCTest( 33 | source: """ 34 | import Foundation 35 | import XCTest 36 | """ 37 | ), 38 | .importsXCTest 39 | ) 40 | } 41 | 42 | func testImportsSpecificXCTestDecl() throws { 43 | XCTAssertEqual( 44 | try makeContextAndSetImportsXCTest( 45 | source: """ 46 | import Foundation 47 | import class XCTest.XCTestCase 48 | """ 49 | ), 50 | .importsXCTest 51 | ) 52 | } 53 | 54 | func testImportsXCTestInsideConditional() throws { 55 | XCTAssertEqual( 56 | try makeContextAndSetImportsXCTest( 57 | source: """ 58 | import Foundation 59 | #if SOME_FEATURE_FLAG 60 | import XCTest 61 | #endif 62 | """ 63 | ), 64 | .importsXCTest 65 | ) 66 | } 67 | 68 | /// Parses the given source, makes a new `Context`, then populates and returns its `XCTest` 69 | /// import state. 70 | private func makeContextAndSetImportsXCTest(source: String) throws -> Context.XCTestImportState { 71 | let sourceFile = Parser.parse(source: source) 72 | let context = Context( 73 | configuration: Configuration(), 74 | operatorTable: .standardOperators, 75 | findingConsumer: { _ in }, 76 | fileURL: URL(fileURLWithPath: "/tmp/test.swift"), 77 | sourceFileSyntax: sourceFile, 78 | ruleNameCache: ruleNameCache 79 | ) 80 | setImportsXCTest(context: context, sourceFile: sourceFile) 81 | return context.importsXCTest 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/Rules/NeverForceUnwrapTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | @_spi(Rules) import SwiftFormat 14 | import _SwiftFormatTestSupport 15 | 16 | final class NeverForceUnwrapTests: LintOrFormatRuleTestCase { 17 | func testUnsafeUnwrap() { 18 | assertLint( 19 | NeverForceUnwrap.self, 20 | """ 21 | func someFunc() -> Int { 22 | var a = getInt() 23 | var b = 1️⃣a as! Int 24 | let c = 2️⃣(someValue())! 25 | let d = 3️⃣String(a)! 26 | let regex = try! NSRegularExpression(pattern: "a*b+c?") 27 | let e = /*comment about stuff*/ 4️⃣[1: a, 2: b, 3: c][4]! 28 | var f = 5️⃣a as! /*comment about this type*/ FooBarType 29 | return 6️⃣a! 30 | } 31 | """, 32 | findings: [ 33 | FindingSpec("1️⃣", message: "do not force cast to 'Int'"), 34 | FindingSpec("2️⃣", message: "do not force unwrap '(someValue())'"), 35 | FindingSpec("3️⃣", message: "do not force unwrap 'String(a)'"), 36 | FindingSpec("4️⃣", message: "do not force unwrap '[1: a, 2: b, 3: c][4]'"), 37 | FindingSpec("5️⃣", message: "do not force cast to 'FooBarType'"), 38 | FindingSpec("6️⃣", message: "do not force unwrap 'a'"), 39 | ] 40 | ) 41 | } 42 | 43 | func testIgnoreTestCode() { 44 | assertLint( 45 | NeverForceUnwrap.self, 46 | """ 47 | import XCTest 48 | 49 | var b = a as! Int 50 | """, 51 | findings: [] 52 | ) 53 | } 54 | 55 | func testIgnoreTestAttributeFunction() { 56 | assertLint( 57 | NeverForceUnwrap.self, 58 | """ 59 | @Test 60 | func testSomeFunc() { 61 | var b = a as! Int 62 | } 63 | @Test 64 | func testAnotherFunc() { 65 | func nestedFunc() { 66 | let c = someValue()! 67 | } 68 | } 69 | """, 70 | findings: [] 71 | ) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/Rules/NeverUseForceTryTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | @_spi(Rules) import SwiftFormat 14 | import _SwiftFormatTestSupport 15 | 16 | final class NeverUseForceTryTests: LintOrFormatRuleTestCase { 17 | func testInvalidTryExpression() { 18 | assertLint( 19 | NeverUseForceTry.self, 20 | """ 21 | let document = 1️⃣try! Document(path: "important.data") 22 | let document = try Document(path: "important.data") 23 | let x = 2️⃣try! someThrowingFunction() 24 | let x = try? someThrowingFunction( 25 | 3️⃣try! someThrowingFunction() 26 | ) 27 | let x = try someThrowingFunction( 28 | 4️⃣try! someThrowingFunction() 29 | ) 30 | if let data = try? fetchDataFromDisk() { return data } 31 | """, 32 | findings: [ 33 | FindingSpec("1️⃣", message: "do not use force try"), 34 | FindingSpec("2️⃣", message: "do not use force try"), 35 | FindingSpec("3️⃣", message: "do not use force try"), 36 | FindingSpec("4️⃣", message: "do not use force try"), 37 | ] 38 | ) 39 | } 40 | 41 | func testAllowForceTryInTestCode() { 42 | assertLint( 43 | NeverUseForceTry.self, 44 | """ 45 | import XCTest 46 | 47 | let document = try! Document(path: "important.data") 48 | """, 49 | findings: [] 50 | ) 51 | } 52 | 53 | func testAllowForceTryInTestAttributeFunction() { 54 | assertLint( 55 | NeverUseForceTry.self, 56 | """ 57 | @Test 58 | func testSomeFunc() { 59 | let document = try! Document(path: "important.data") 60 | func nestedFunc() { 61 | let x = try! someThrowingFunction() 62 | } 63 | } 64 | """, 65 | findings: [] 66 | ) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/Rules/NeverUseImplicitlyUnwrappedOptionalsTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | @_spi(Rules) import SwiftFormat 14 | import _SwiftFormatTestSupport 15 | 16 | final class NeverUseImplicitlyUnwrappedOptionalsTests: LintOrFormatRuleTestCase { 17 | func testInvalidVariableUnwrapping() { 18 | assertLint( 19 | NeverUseImplicitlyUnwrappedOptionals.self, 20 | """ 21 | import Core 22 | import Foundation 23 | import SwiftSyntax 24 | 25 | var foo: Int? 26 | var s: 1️⃣String! 27 | var f: /*this is a Foo*/2️⃣Foo! 28 | var c, d, e: Float 29 | @IBOutlet var button: UIButton! 30 | """, 31 | findings: [ 32 | FindingSpec("1️⃣", message: "use 'String' or 'String?' instead of 'String!'"), 33 | FindingSpec("2️⃣", message: "use 'Foo' or 'Foo?' instead of 'Foo!'"), 34 | ] 35 | ) 36 | } 37 | 38 | func testIgnoreTestCode() { 39 | assertLint( 40 | NeverUseImplicitlyUnwrappedOptionals.self, 41 | """ 42 | import XCTest 43 | 44 | var s: String! 45 | """, 46 | findings: [] 47 | ) 48 | } 49 | 50 | func testIgnoreTestAttrinuteFunction() { 51 | assertLint( 52 | NeverUseImplicitlyUnwrappedOptionals.self, 53 | """ 54 | @Test 55 | func testSomeFunc() { 56 | var s: String! 57 | func nestedFunc() { 58 | var f: Foo! 59 | } 60 | } 61 | """, 62 | findings: [] 63 | ) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/Rules/NoBlockCommentsTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | @_spi(Rules) import SwiftFormat 14 | import _SwiftFormatTestSupport 15 | 16 | final class NoBlockCommentsTests: LintOrFormatRuleTestCase { 17 | func testDiagnoseBlockComments() { 18 | assertLint( 19 | NoBlockComments.self, 20 | """ 21 | 1️⃣/* 22 | Lorem ipsum dolor sit amet, at nonumes adipisci sea, natum 23 | offendit vis ex. Audiam legendos expetenda ei quo, nonumes 24 | 25 | msensibus eloquentiam ex vix. 26 | */ 27 | let a = 2️⃣/*ff*/10 3️⃣/*ff*/ + 10 28 | var b = 04️⃣/*Block Comment inline with code*/ 29 | 30 | 5️⃣/* 31 | 32 | Block Comment 33 | */ 34 | let c = a + b 35 | 6️⃣/* This is the end 36 | of a file 37 | 38 | */ 39 | """, 40 | findings: [ 41 | FindingSpec("1️⃣", message: "replace this block comment with line comments"), 42 | FindingSpec("2️⃣", message: "replace this block comment with line comments"), 43 | FindingSpec("3️⃣", message: "replace this block comment with line comments"), 44 | FindingSpec("4️⃣", message: "replace this block comment with line comments"), 45 | FindingSpec("5️⃣", message: "replace this block comment with line comments"), 46 | FindingSpec("6️⃣", message: "replace this block comment with line comments"), 47 | ] 48 | ) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/Rules/NoLabelsInCasePatternsTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | @_spi(Rules) import SwiftFormat 14 | import _SwiftFormatTestSupport 15 | 16 | final class NoLabelsInCasePatternsTests: LintOrFormatRuleTestCase { 17 | func testRedundantCaseLabels() { 18 | assertFormatting( 19 | NoLabelsInCasePatterns.self, 20 | input: """ 21 | switch treeNode { 22 | case .root(let data): 23 | break 24 | case .subtree(1️⃣left: let /*hello*/left, 2️⃣right: let right): 25 | break 26 | case .leaf(3️⃣element: let element): 27 | break 28 | } 29 | """, 30 | expected: """ 31 | switch treeNode { 32 | case .root(let data): 33 | break 34 | case .subtree(let /*hello*/left, let right): 35 | break 36 | case .leaf(let element): 37 | break 38 | } 39 | """, 40 | findings: [ 41 | FindingSpec("1️⃣", message: "remove the label 'left' from this 'case' pattern"), 42 | FindingSpec("2️⃣", message: "remove the label 'right' from this 'case' pattern"), 43 | FindingSpec("3️⃣", message: "remove the label 'element' from this 'case' pattern"), 44 | ] 45 | ) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/Rules/NoVoidReturnOnFunctionSignatureTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | @_spi(Rules) import SwiftFormat 14 | import _SwiftFormatTestSupport 15 | 16 | final class NoVoidReturnOnFunctionSignatureTests: LintOrFormatRuleTestCase { 17 | func testVoidReturns() { 18 | assertFormatting( 19 | NoVoidReturnOnFunctionSignature.self, 20 | input: """ 21 | func foo() -> 1️⃣() { 22 | } 23 | 24 | func test() -> 2️⃣Void{ 25 | } 26 | 27 | func x() -> Int { return 2 } 28 | 29 | let x = { () -> Void in 30 | print("Hello, world!") 31 | } 32 | """, 33 | expected: """ 34 | func foo() { 35 | } 36 | 37 | func test() { 38 | } 39 | 40 | func x() -> Int { return 2 } 41 | 42 | let x = { () -> Void in 43 | print("Hello, world!") 44 | } 45 | """, 46 | findings: [ 47 | FindingSpec("1️⃣", message: "remove the explicit return type '()' from this function"), 48 | FindingSpec("2️⃣", message: "remove the explicit return type 'Void' from this function"), 49 | ] 50 | ) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/Rules/OnlyOneTrailingClosureArgumentTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | @_spi(Rules) import SwiftFormat 14 | import _SwiftFormatTestSupport 15 | 16 | final class OnlyOneTrailingClosureArgumentTests: LintOrFormatRuleTestCase { 17 | func testInvalidTrailingClosureCall() { 18 | assertLint( 19 | OnlyOneTrailingClosureArgument.self, 20 | """ 21 | 1️⃣callWithBoth(someClosure: {}) { 22 | // ... 23 | } 24 | callWithClosure(someClosure: {}) 25 | callWithTrailingClosure { 26 | // ... 27 | } 28 | """, 29 | findings: [ 30 | FindingSpec( 31 | "1️⃣", 32 | message: "revise this function call to avoid using both closure arguments and a trailing closure" 33 | ) 34 | ] 35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/Rules/ReplaceForEachWithForLoopTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | @_spi(Rules) import SwiftFormat 14 | import _SwiftFormatTestSupport 15 | 16 | final class ReplaceForEachWithForLoopTests: LintOrFormatRuleTestCase { 17 | func test() { 18 | assertLint( 19 | ReplaceForEachWithForLoop.self, 20 | """ 21 | values.1️⃣forEach { $0 * 2 } 22 | values.map { $0 }.2️⃣forEach { print($0) } 23 | values.forEach(callback) 24 | values.forEach { $0 }.chained() 25 | values.forEach({ $0 }).chained() 26 | values.3️⃣forEach { 27 | let arg = $0 28 | return arg + 1 29 | } 30 | values.forEach { 31 | let arg = $0 32 | return arg + 1 33 | } other: { 34 | 42 35 | } 36 | """, 37 | findings: [ 38 | FindingSpec("1️⃣", message: "replace use of '.forEach { ... }' with for-in loop"), 39 | FindingSpec("2️⃣", message: "replace use of '.forEach { ... }' with for-in loop"), 40 | FindingSpec("3️⃣", message: "replace use of '.forEach { ... }' with for-in loop"), 41 | ] 42 | ) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/Rules/UseSingleLinePropertyGetterTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | @_spi(Rules) import SwiftFormat 14 | import _SwiftFormatTestSupport 15 | 16 | final class UseSingleLinePropertyGetterTests: LintOrFormatRuleTestCase { 17 | func testMultiLinePropertyGetter() { 18 | assertFormatting( 19 | UseSingleLinePropertyGetter.self, 20 | input: """ 21 | var g: Int { return 4 } 22 | var h: Int { 23 | 1️⃣get { 24 | return 4 25 | } 26 | } 27 | var i: Int { 28 | get { return 0 } 29 | set { print("no set, only get") } 30 | } 31 | var j: Int { 32 | mutating get { return 0 } 33 | } 34 | var k: Int { 35 | get async { 36 | return 4 37 | } 38 | } 39 | var l: Int { 40 | get throws { 41 | return 4 42 | } 43 | } 44 | var m: Int { 45 | get async throws { 46 | return 4 47 | } 48 | } 49 | """, 50 | expected: """ 51 | var g: Int { return 4 } 52 | var h: Int { 53 | return 4 54 | } 55 | var i: Int { 56 | get { return 0 } 57 | set { print("no set, only get") } 58 | } 59 | var j: Int { 60 | mutating get { return 0 } 61 | } 62 | var k: Int { 63 | get async { 64 | return 4 65 | } 66 | } 67 | var l: Int { 68 | get throws { 69 | return 4 70 | } 71 | } 72 | var m: Int { 73 | get async throws { 74 | return 4 75 | } 76 | } 77 | """, 78 | findings: [ 79 | FindingSpec( 80 | "1️⃣", 81 | message: "remove 'get {...}' around the accessor and move its body directly into the computed property" 82 | ) 83 | ] 84 | ) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /Tests/SwiftFormatTests/Rules/UseWhereClausesInForLoopsTests.swift: -------------------------------------------------------------------------------- 1 | //===----------------------------------------------------------------------===// 2 | // 3 | // This source file is part of the Swift.org open source project 4 | // 5 | // Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors 6 | // Licensed under Apache License v2.0 with Runtime Library Exception 7 | // 8 | // See https://swift.org/LICENSE.txt for license information 9 | // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | // 11 | //===----------------------------------------------------------------------===// 12 | 13 | @_spi(Rules) import SwiftFormat 14 | import _SwiftFormatTestSupport 15 | 16 | final class UseWhereClausesInForLoopsTests: LintOrFormatRuleTestCase { 17 | func testForLoopWhereClauses() { 18 | assertFormatting( 19 | UseWhereClausesInForLoops.self, 20 | input: """ 21 | for i in [0, 1, 2, 3] { 22 | 1️⃣if i > 30 { 23 | print(i) 24 | } 25 | } 26 | 27 | for i in [0, 1, 2, 3] { 28 | if i > 30 { 29 | print(i) 30 | } else { 31 | print(i) 32 | } 33 | } 34 | 35 | for i in [0, 1, 2, 3] { 36 | if i > 30 { 37 | print(i) 38 | } else if i > 40 { 39 | print(i) 40 | } 41 | } 42 | 43 | for i in [0, 1, 2, 3] { 44 | if i > 30 { 45 | print(i) 46 | } 47 | print(i) 48 | } 49 | 50 | for i in [0, 1, 2, 3] { 51 | if let x = (2 as Int?) { 52 | print(i) 53 | } 54 | } 55 | 56 | for i in [0, 1, 2, 3] { 57 | 2️⃣guard i > 30 else { 58 | continue 59 | } 60 | print(i) 61 | } 62 | """, 63 | expected: """ 64 | for i in [0, 1, 2, 3] where i > 30 { 65 | print(i) 66 | } 67 | 68 | for i in [0, 1, 2, 3] { 69 | if i > 30 { 70 | print(i) 71 | } else { 72 | print(i) 73 | } 74 | } 75 | 76 | for i in [0, 1, 2, 3] { 77 | if i > 30 { 78 | print(i) 79 | } else if i > 40 { 80 | print(i) 81 | } 82 | } 83 | 84 | for i in [0, 1, 2, 3] { 85 | if i > 30 { 86 | print(i) 87 | } 88 | print(i) 89 | } 90 | 91 | for i in [0, 1, 2, 3] { 92 | if let x = (2 as Int?) { 93 | print(i) 94 | } 95 | } 96 | 97 | for i in [0, 1, 2, 3] where i > 30 { 98 | print(i) 99 | } 100 | """, 101 | findings: [ 102 | FindingSpec("1️⃣", message: "replace this 'if' statement with a 'where' clause"), 103 | FindingSpec("2️⃣", message: "replace this 'guard' statement with a 'where' clause"), 104 | ] 105 | ) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /api-breakages.txt: -------------------------------------------------------------------------------- 1 | 6.2 2 | --- 3 | 4 | API breakage: constructor FileIterator.init(urls:followSymlinks:) has been removed 5 | API breakage: enumelement SwiftFormatError.configurationDumpFailed has been added as a new enum case 6 | API breakage: enumelement SwiftFormatError.unsupportedConfigurationVersion has been added as a new enum case 7 | API breakage: class UseLetInEveryBoundCaseVariable has changed its super class from SwiftFormat.SyntaxLintRule to SwiftFormat.SyntaxFormatRule 8 | API breakage: func UseLetInEveryBoundCaseVariable.visit(_:) has return type change from SwiftSyntax.SyntaxVisitorContinueKind to SwiftSyntax.MatchingPatternConditionSyntax 9 | API breakage: func UseLetInEveryBoundCaseVariable.visit(_:) has parameter 0 type change from SwiftSyntax.ValueBindingPatternSyntax to SwiftSyntax.MatchingPatternConditionSyntax 10 | API breakage: func Rule.diagnose(_:on:severity:anchor:notes:) has parameter 2 type change from SwiftFormat.Finding.Severity? to SwiftFormat.FindingAnchor 11 | API breakage: func Rule.diagnose(_:on:severity:anchor:notes:) has parameter 3 type change from SwiftFormat.FindingAnchor to [SwiftFormat.Finding.Note] 12 | API breakage: enum Finding.Severity has been removed 13 | API breakage: var Finding.severity has been removed 14 | API breakage: var FindingCategorizing.defaultSeverity has been removed 15 | API breakage: var FindingCategorizing.defaultSeverity has been removed 16 | API breakage: func Rule.diagnose(_:on:severity:anchor:notes:) has been renamed to func diagnose(_:on:anchor:notes:) 17 | API breakage: func Configuration.MultilineStringReflowBehavior.hash(into:) has been removed 18 | API breakage: func Configuration.MultilineStringReflowBehavior.encode(to:) has been removed 19 | API breakage: var Configuration.MultilineStringReflowBehavior.hashValue has been removed 20 | API breakage: constructor Configuration.MultilineStringReflowBehavior.init(from:) has been removed 21 | API breakage: enumelement LineType.implementationOnlyImport has been added as a new enum case 22 | -------------------------------------------------------------------------------- /cmake/modules/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #[[ 2 | This source file is part of the swift-format open source project 3 | 4 | Copyright (c) 2024 Apple Inc. and the swift-format project authors 5 | Licensed under Apache License v2.0 with Runtime Library Exception 6 | 7 | See https://swift.org/LICENSE.txt for license information 8 | #]] 9 | 10 | set(SWIFT_FORMAT_EXPORTS_FILE 11 | ${CMAKE_CURRENT_BINARY_DIR}/SwiftFormatExports.cmake) 12 | 13 | configure_file(SwiftFormatConfig.cmake.in 14 | ${CMAKE_CURRENT_BINARY_DIR}/SwiftFormatConfig.cmake) 15 | 16 | get_property(SWIFT_FORMAT_EXPORTS GLOBAL PROPERTY SWIFT_FORMAT_EXPORTS) 17 | export(TARGETS ${SWIFT_FORMAT_EXPORTS} 18 | NAMESPACE SwiftFormat:: 19 | FILE ${SWIFT_FORMAT_EXPORTS_FILE} 20 | EXPORT_LINK_INTERFACE_LIBRARIES) 21 | -------------------------------------------------------------------------------- /cmake/modules/SwiftFormatConfig.cmake.in: -------------------------------------------------------------------------------- 1 | ##===----------------------------------------------------------------------===## 2 | ## 3 | ## This source file is part of the Swift.org open source project 4 | ## 5 | ## Copyright (c) 2024 Apple Inc. and the Swift project authors 6 | ## Licensed under Apache License v2.0 with Runtime Library Exception 7 | ## 8 | ## See https://swift.org/LICENSE.txt for license information 9 | ## See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors 10 | ## 11 | ##===----------------------------------------------------------------------===## 12 | 13 | if(NOT TARGET SwiftFormat) 14 | include("@SWIFT_FORMAT_EXPORTS_FILE@") 15 | endif() 16 | --------------------------------------------------------------------------------