Swiftlint: Autocorrect for "colon" corrupts file when preprocessor macros are present

Created on 16 Mar 2018  ·  3Comments  ·  Source: realm/SwiftLint

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 6584b4f5
  • 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
        }
      ]
    }
  ]
}

bug

All 3 comments

@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?

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

Thanks for the quick turn around!

Was this page helpful?
0 / 5 - 0 ratings