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

Autocorrect for "colon" corrupts file when preprocessor macros are present #2099

Closed
jszumski opened this Issue Mar 16, 2018 · 3 comments

Comments

Projects
None yet
2 participants
@jszumski
Contributor

jszumski commented Mar 16, 2018

New Issue Checklist

Bug Report

Complete output when running SwiftLint, including the stack trace and command used
$ swiftlint autocorrect --path Foo.swift
Correcting Swift files at path Foo.swift
Correcting 'Foo.swift' (1/1)
Foo.swift:4:21 Corrected Colon
Foo.swift:4:21 Corrected Colon
Done correcting 1 files!

Environment

  • SwiftLint version (run swiftlint version to be sure)? 0.25.0 does not find anything to autocorrect (due to #2050), however anything after dddb0f6 shows the issue
  • Installation method used (Homebrew, CocoaPods, building from source, etc)? Homebrew for 0.25.0, built from source at 6584b4f
  • Paste your configuration file:
whitelist_rules:
  - colon
  • Are you using nested configurations? If so, paste their relative paths and respective contents. no
  • Which Xcode version are you using (check xcode-select -p)? 9.2

Steps to Reproduce

Source File

class Foo {
    #if false
    #else
    let bar = ["key"   : "value"]
    #endif
}

Run autocorrect

$ swiftlint autocorrect --path Foo.swift
Correcting Swift files at path Foo.swift
Correcting 'Foo.swift' (1/1)
Foo.swift:4:21 Corrected Colon
Foo.swift:4:21 Corrected Colon
Done correcting 1 files!

Corrected File

The duplicate correction ends up truncating the dictionary such that it doesn't compile. The amount of truncation is always equal to the number of extra spaces between the end of the key and the colon.

class Foo {
    #if false
    #else
    let bar = ["key": lue"]
    #endif
}

Variations Attempted

The issue is only reproducible with a dictionary literal, replacing the 4th line with

let bar   :   String = ""

or

func bar(arg   :  String) {}

works as expected.

Removing the preprocessor macros entirely also fixes the issue, but this isn't possible in the real world case that uncovered this bug.


As best I can tell SourceKitten is getting confused by the preprocessor macros but only in the dictionary literal case. Examining file.structure in ColonRule.correctionRanges(in:) shows two identical entries, which causes swiftlint to emit two corrections at the same location.

structure output
{
  "key.diagnostic_stage" : "source.diagnostic.stage.swift.parse",
  "key.length" : 84,
  "key.offset" : 0,
  "key.substructure" : [
    {
      "key.accessibility" : "source.lang.swift.accessibility.internal",
      "key.bodylength" : 70,
      "key.bodyoffset" : 11,
      "key.kind" : "source.lang.swift.decl.class",
      "key.length" : 82,
      "key.name" : "Foo",
      "key.namelength" : 3,
      "key.nameoffset" : 6,
      "key.offset" : 0,
      "key.runtime_name" : "_TtC8__main__3Foo",
      "key.substructure" : [
        {
          "key.accessibility" : "source.lang.swift.accessibility.internal",
          "key.kind" : "source.lang.swift.decl.var.instance",
          "key.length" : 29,
          "key.name" : "bar",
          "key.namelength" : 3,
          "key.nameoffset" : 44,
          "key.offset" : 40
        },
        {
          "key.bodylength" : 17,
          "key.bodyoffset" : 51,
          "key.elements" : [
            {
              "key.kind" : "source.lang.swift.structure.elem.expr",
              "key.length" : 5,
              "key.offset" : 51
            },
            {
              "key.kind" : "source.lang.swift.structure.elem.expr",
              "key.length" : 7,
              "key.offset" : 61
            }
          ],
          "key.kind" : "source.lang.swift.expr.dictionary",
          "key.length" : 19,
          "key.namelength" : 0,
          "key.nameoffset" : 0,
          "key.offset" : 50
        },
        {
          "key.accessibility" : "source.lang.swift.accessibility.internal",
          "key.kind" : "source.lang.swift.decl.var.instance",
          "key.length" : 29,
          "key.name" : "bar",
          "key.namelength" : 3,
          "key.nameoffset" : 44,
          "key.offset" : 40
        },
        {
          "key.bodylength" : 17,
          "key.bodyoffset" : 51,
          "key.elements" : [
            {
              "key.kind" : "source.lang.swift.structure.elem.expr",
              "key.length" : 5,
              "key.offset" : 51
            },
            {
              "key.kind" : "source.lang.swift.structure.elem.expr",
              "key.length" : 7,
              "key.offset" : 61
            }
          ],
          "key.kind" : "source.lang.swift.expr.dictionary",
          "key.length" : 19,
          "key.namelength" : 0,
          "key.nameoffset" : 0,
          "key.offset" : 50
        }
      ]
    }
  ]
}

@marcelofabri marcelofabri added the bug label Mar 18, 2018

@marcelofabri

This comment has been minimized.

Collaborator

marcelofabri commented Mar 18, 2018

@jszumski It looks like we can workaround this by uniquing the violations before correcting.

But SourceKit shouldn't return two identical entries as well. Can you please file a bug in Swift's JIRA?

@marcelofabri

This comment has been minimized.

Collaborator

marcelofabri commented Mar 18, 2018

Actually, this looks fixed already on Swift 4.1, so no need to file a bug 💯

@jszumski

This comment has been minimized.

Contributor

jszumski commented Mar 18, 2018

Thanks for the quick turn around!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment