nicklockwood / SwiftFormat

A command-line tool and Xcode Extension for formatting Swift code
MIT License
7.9k stars 639 forks source link

Formatted to wrong indent #1216

Closed akadateppei closed 2 years ago

akadateppei commented 2 years ago

I updated swiftformat 0.49.12, then caught (indent) Indent code in accordance with the scope level.

Until then, there was no warning in the same code like below:

timer = Timer.scheduledTimer(withTimeInterval: timeInterval, repeats: false, block: { [weak self] _ in
    guard let self = self else {
        return
    }
    for vc in self.navigationController.viewControllers.reversed() {
        if let vc = vc as? ExampleViewController {
            // some code
        } else {
            break
        }
    }
    self.invalidateTimer()
})

It formatted to be

timer = Timer.scheduledTimer(withTimeInterval: timeInterval, repeats: false, block: { [weak self] _ in
    guard let self = self else {
        return
    }
    for vc in self.navigationController.viewControllers.reversed() {
        if let vc = vc as? ExampleViewController {
                // some code
            } else {
                break
            }
        }
        self.invalidateTimer()
})

.swiftformat file

# rules
--disable trailingClosures, wrapMultilineStatementBraces, redundantType, redundantClosure
--stripunusedargs closure-only

Is this bug?

akadateppei commented 2 years ago

It seems like to occur in for inside closure.

michalsrutek commented 2 years ago

Same problem here.

I've got a closure

        ... { [weak self] point, event in
             ...
                for subview in self.view.subviews.reversed() {
                    let convertedPoint = subview.convert(point, from: self.view)
                    if let candidate = subview.hitTest(convertedPoint, with: event) {
                        return candidate
                    }
                }

                return self.view
            }

which gets formatted to

         ... { [weak self] point, event in
             ...
                for subview in self.view.subviews.reversed() {
                        let convertedPoint = subview.convert(point, from: self.view)
                        if let candidate = subview.hitTest(convertedPoint, with: event) {
                            return candidate
                        }
                    }

                    return self.view
            }
itaiferber commented 2 years ago

It looks like this might be related to text following the open brace of a function/closure and the actual body. I can reproduce with the following minimal definition (note the comment at the start of ƒ() {:

struct Foo {
    func ƒ() { // comment here
        for idx in 0 ..< 100 {
            print(idx) // ⚠️ (indent) Indent code in accordance with the scope level
        } // ⚠️ (indent) Indent code in accordance with the scope level
    }
}

If I remove the comment, everything works as expected:

struct Foo {
    func ƒ() {
        for idx in 0 ..< 100 {
            print(idx)
        }
    }
}

So this isn't unique to closures, but happens for functions too.

(In my specific case, the comment I've got is a swiftlint directive to disable a warning for something unrelated, but it seems to trigger this.)

krin-san commented 2 years ago

In my case there were no in-line comments, but I have a guard in for loop. I've simplified code to this example:

class Request {
    private func processResponse() {
        queue.async { [weak self] in
            for field in fields {
                guard condition else { continue } // warning: (indent) Indent code in accordance with the scope level.
                takeAction() // warning: (indent) Indent code in accordance with the scope level.
            } // warning: (indent) Indent code in accordance with the scope level.
        }
    }
}
krin-san commented 2 years ago

Might be cause by https://github.com/nicklockwood/SwiftFormat/commit/a51a305c8fbdbc9af113bc8659bf95d838cec067 commit – that's the only one related to indentation and scopes in 0.49.11...0.49.12 diff.

jshier commented 2 years ago

@itaiferber's hypothesis seems correct but too narrow. I saw this triggered when I added a closure return definition: Task { () -> [Int] in.

itaiferber commented 2 years ago

@jshier Yeah, sorry, I may have used the wrong terminology. What I meant was: it seems that any text between the open brace and the body of the function/closure seems to trigger this. Edited my comment to clarify.

jshier commented 2 years ago

No problem, and I see @krin-san said basically the same thing. I was just thrown off by the use of trivia.

nicklockwood commented 2 years ago

Fixed in 0.49.13