Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

swiftlint analyze appears to stop collecting after 12 files with Swift 5.10 #5517

Closed
2 tasks done
chandlerwall opened this issue Mar 30, 2024 · 6 comments · Fixed by #5526
Closed
2 tasks done

swiftlint analyze appears to stop collecting after 12 files with Swift 5.10 #5517

chandlerwall opened this issue Mar 30, 2024 · 6 comments · Fixed by #5526
Labels

Comments

@chandlerwall
Copy link
Contributor

chandlerwall commented Mar 30, 2024

New Issue Checklist

Describe the bug

After upgrading to Xcode 15.3 and Swift 5.10, swiftlint analyze appears to stop collecting files. For my environment, the console output stops updating after collecting 12 files. If I adjust included or excluded to 12 or fewer files, I can reliably avoid the issue.

Complete output when running SwiftLint, including the stack trace and command used
MusicStacks-lint > swiftlint analyze --compiler-log-path .build/build.log
Analyzing Swift files in current working directory
Collecting 'View+hidden.swift' (2/30)
Collecting 'SomethingsWrong.swift' (1/30)
Collecting 'View+foregroundStyle.swift' (4/30)
Collecting 'View+dimensionOverlay.swift' (5/30)
Collecting 'ShowInMusicMenuButton.swift' (3/30)
Collecting 'Modules/Sources/StyleGuide/_exported.swift' (6/30)
Collecting 'View+firstTextCenterline.swift' (8/30)
Collecting 'ToolbarMenuButton.swift' (9/30)
Collecting 'ExplicitTitle.swift' (7/30)
Collecting 'View+enabled.swift' (10/30)
Collecting 'HeaderTitle.swift' (11/30)
Collecting 'LabelStyle.swift' (12/30)

Environment

  • SwiftLint version (run swiftlint version to be sure)?

    0.53.0

  • Installation method used (Homebrew, CocoaPods, building from source, etc)?

    Homebrew

  • Paste your configuration file:

    included:
    - iOS
    - Modules
    
    # only_rules:
    #   - trailing_comma
    
    disabled_rules:
      # - empty_enum_arguments
      - identifier_name # TODO: reconsider as warning to improve clarity of names
      - line_length
      - redundant_string_enum_value
      - todo
      - trailing_comma
      - type_name # TODO: reconsider as warning to improve clarity of names
    
    opt_in_rules:
      - direct_return
      # - explicit_type_interface
      - implicit_return
      - sorted_imports
    
    analyzer_rules:
      - capture_variable
      - explicit_self # --fix
      - unused_declaration
      # - unused_import # --fix
    
    cyclomatic_complexity:
      warning: 11
      error: 29
    
    force_cast:
      severity: warning
    
    force_try:
      severity: warning
    
    function_body_length:
      warning: 75
      # error: 100
    
    nesting:
      type_level:
        warning: 3
    
    type_body_length:
      warning: 300
      error: 500
    
    # trailing_comma:
    #   mandatory_comma: true
  • Are you using nested configurations?

    No

  • Which Xcode version are you using (check xcodebuild -version)?

    $ xcodebuild -version
    Xcode 15.3
    Build version 15E204a
    
    $ swiftc --version
    swift-driver version: 1.90.11.1 Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)
    Target: arm64-apple-macosx14.0
    
  • Do you have a sample that shows the issue? Run echo "[string here]" | swiftlint lint --no-cache --use-stdin --enable-all-rules
    to quickly test if your example is really demonstrating the issue. If your example is more
    complex, you can use swiftlint lint --path [file here] --no-cache --enable-all-rules.

    The issue is not related to a specific pattern of code.

@chandlerwall
Copy link
Contributor Author

chandlerwall commented Mar 30, 2024

I checked out SwiftLint's source to debug the issue a bit and found the cause: SwiftLintCore.SwiftVersion does not compare string versions reliably, preventing version-specific workarounds from being applied.

public struct SwiftVersion: RawRepresentable, Codable, Comparable {
public typealias RawValue = String
public let rawValue: String
public init(rawValue: String) {
self.rawValue = rawValue
}
public static func < (lhs: SwiftVersion, rhs: SwiftVersion) -> Bool {
return lhs.rawValue < rhs.rawValue
}
}

For Swift 5.10, version-specific workarounds are not applied correctly. Specifically, LintableFilesVisitor.parallel is assigned true instead of false when SwiftVersion.current < .fiveDotSix returns an incorrect value.

if let compilerInvocations {
self.mode = .analyze(allCompilerInvocations: compilerInvocations)
// SourceKit had some changes in 5.6 that makes it ~100x more expensive
// to process files concurrently. By processing files serially, it's
// only 2x slower than before.
self.parallel = SwiftVersion.current < .fiveDotSix
} else {
self.mode = .lint
self.parallel = true
}

I replaced SwiftVersion with the following:

/// A value describing the version of the Swift compiler.
public struct SwiftVersion: RawRepresentable, Codable, Comparable, Sendable {
    public typealias RawValue = String

    public let rawValue: String

    public init(rawValue: String) {
        self.rawValue = rawValue
    }

  public static func < (lhs: SwiftVersion, rhs: SwiftVersion) -> Bool {
      func versionComponents(from version: String) -> [Int] {
          return version.components(separatedBy: ".").compactMap { Int($0) }
      }

      let lhsComponents = versionComponents(from: lhs.rawValue)
      let rhsComponents = versionComponents(from: rhs.rawValue)

      for (lhsComponent, rhsComponent) in zip(lhsComponents, rhsComponents) {
          if lhsComponent < rhsComponent {
              return true
          } else if lhsComponent > rhsComponent {
              return false
          }
      }

      return lhsComponents.count < rhsComponents.count
  }
}

Note: I used Claude to implement static func < (lhs:rhs:). I wanted to save time while troubleshooting. The implementation may or may not handle edge cases correctly. I only verified the implementation for my environment.

With the changes in place, I'm able to run swiftlint analyze on a larger set of files. The run below was limited to 30 files. I also verified the result on a run with 350+ files.

Analyzing Swift files in current working directory
Collecting 'Modules/Sources/StyleGuide/_exported.swift' (1/30)
Collecting 'SomethingsWrong.swift' (2/30)
Collecting 'ToolbarMenuButton.swift' (3/30)
Collecting 'ShowInMusicMenuButton.swift' (4/30)
Collecting 'View+hidden.swift' (5/30)
Collecting 'View+foregroundStyle.swift' (6/30)
Collecting 'View+dimensionOverlay.swift' (7/30)
Collecting 'View+enabled.swift' (8/30)
Collecting 'View+firstTextCenterline.swift' (9/30)
Collecting 'HeaderTitle.swift' (10/30)
Collecting 'ExplicitTitle.swift' (11/30)
Collecting 'LabelStyle.swift' (12/30)
Collecting 'StyleGuide.swift' (13/30)
Collecting 'ActiveTrackIcon.swift' (14/30)
Collecting 'FontPreview.swift' (15/30)
Collecting 'CountsView.swift' (16/30)
Collecting 'NoSearchResults.swift' (17/30)
Collecting 'LibraryIcon.swift' (18/30)
Collecting 'PresentationState.swift' (19/30)
Collecting 'AlertPresentation.swift' (20/30)
Collecting 'ConfirmationDialogPresentation.swift' (21/30)
Collecting 'Symbol.swift' (22/30)
Collecting 'SearchControllerProxy.swift' (23/30)
Collecting 'EnvironmentAction.swift' (24/30)
Collecting 'Resolvable.swift' (25/30)
Collecting 'SearchableUIHostingController.swift' (26/30)
Collecting 'SearchScope.swift' (27/30)
Collecting 'RelativeTimeframe.swift' (28/30)
Collecting 'SimilarityResult.swift' (29/30)
Collecting 'Counts.swift' (30/30)
Analyzing 'Modules/Sources/StyleGuide/_exported.swift' (1/30)
Analyzing 'SomethingsWrong.swift' (2/30)
Analyzing 'ToolbarMenuButton.swift' (3/30)
Analyzing 'ShowInMusicMenuButton.swift' (4/30)
Analyzing 'View+hidden.swift' (5/30)
Analyzing 'View+foregroundStyle.swift' (6/30)
Analyzing 'View+dimensionOverlay.swift' (7/30)
Analyzing 'View+enabled.swift' (8/30)
Analyzing 'View+firstTextCenterline.swift' (9/30)
Analyzing 'HeaderTitle.swift' (10/30)
Analyzing 'ExplicitTitle.swift' (11/30)
Analyzing 'LabelStyle.swift' (12/30)
Analyzing 'StyleGuide.swift' (13/30)
Analyzing 'ActiveTrackIcon.swift' (14/30)
Analyzing 'FontPreview.swift' (15/30)
Analyzing 'CountsView.swift' (16/30)
Analyzing 'NoSearchResults.swift' (17/30)
Analyzing 'LibraryIcon.swift' (18/30)
Analyzing 'PresentationState.swift' (19/30)
Analyzing 'AlertPresentation.swift' (20/30)
Analyzing 'ConfirmationDialogPresentation.swift' (21/30)
Analyzing 'Symbol.swift' (22/30)
Analyzing 'SearchControllerProxy.swift' (23/30)
Analyzing 'EnvironmentAction.swift' (24/30)
Analyzing 'Resolvable.swift' (25/30)
Analyzing 'SearchableUIHostingController.swift' (26/30)
Analyzing 'SearchScope.swift' (27/30)
Analyzing 'RelativeTimeframe.swift' (28/30)
Analyzing 'SimilarityResult.swift' (29/30)
Analyzing 'Counts.swift' (30/30)
Done analyzing! Found 0 violations, 0 serious in 30 files.

@chandlerwall chandlerwall changed the title swiftlint analyze appears to stop collecting files with Swift 5.10 swiftlint analyze appears to stop collecting after 12 files with Swift 5.10 Mar 30, 2024
@SimplyDanny
Copy link
Collaborator

Can the stop of analysis be associated with a specific file maybe?

@chandlerwall
Copy link
Contributor Author

Can the stop of analysis be associated with a specific file maybe?

I started my troubleshooting by adjusting included and excluded to find a problematic file. I started with a small number of files and gradually included more files until the issue occurred. I thought I found the problematic file. I added more files and noticed the issue again. I tested different combinations and realized I could move "problematic" files from excluded to included without triggering the issue. As long as I keep the file count very low, the command finished. As soon as I added an extra file, the command chokes.

@SimplyDanny
Copy link
Collaborator

Forget my previous question. You were obviously a little faster with the detailed report of your investigation. I didn't notice it while crafting my message.

Your reasoning makes a lot of sense. Looks like SwiftVersion wasn't developed with version components >9 in mind or the implementors didn't expect such high numbers.

Would you like to open a PR to fix the comparison algorithm?

@chandlerwall
Copy link
Contributor Author

Yes, I will prepare a PR with a fix for the comparison algorithm. Appreciate the quick responses!

@NachoSoto
Copy link
Contributor

I'm seeing the exact same thing. For me it stops after 14 files, no matter what file that is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants