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

[SR-5869] trailing closure not parsed correctly in if-statement conditional #48439

Closed
swift-ci opened this issue Sep 11, 2017 · 8 comments
Closed
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself parser Area → compiler: The legacy C++ parser

Comments

@swift-ci
Copy link
Collaborator

Previous ID SR-5869
Radar https://bugreport.apple.com/web/?problemID=31489013
Original Reporter tncowart (JIRA User)
Type Bug
Status Resolved
Resolution Done
Environment

Xcode 8.3, Xcode 9 beta 6, macOS 10.12.6

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, Parser
Assignee owenvoorhees (JIRA)
Priority Medium

md5: ad36f443386a825f7e352edd3209ab5c

is duplicated by:

  • SR-10509 Optional map with nil coalesce fails to compile in if-statement
  • SR-11031 bad diagnostics with trailing closure in condition expression
  • SR-8736 Bad diagnostic for use of trailing closures in an if

Issue Description:

Summary: When using a function that takes a trailing closure as part of an if-statement conditional, writing the closure in the idiomatic `function { %0.thing }` style causes a compiler error. Writing the closure in the non-idiomatic `function({ $0.thing })` style works, though.

Steps to Reproduce:

  1. Add the following to a file (`closure.swift`):
let nums = [1, 3, 5, 9]
if nums.filter { $0 % 2 == 0 }.isEmpty { 
    print("contains no even numbers") 
}
  1. compile with swift closure.swift

Expected Results: I expect the code to compile and "contains no even numbers" to be printed to the console

Actual Results: the compiler throws multiple errors due to being unable to parse the conditional correctly. Specifically:

swift_bug.swift:5:18: error: anonymous closure argument not contained in a closure
if nums.filter { $0 % 2 == 0 }.isEmpty {
                 ^
swift_bug.swift:5:31: error: consecutive statements on a line must be separated by ';'
if nums.filter { $0 % 2 == 0 }.isEmpty {
                              ^
                              ;
swift_bug.swift:5:9: error: '((Int) throws -> Bool) throws -> [Int]' is not convertible to 'Bool'
if nums.filter { $0 % 2 == 0 }.isEmpty {
   ~~~~~^~~~~~
swift_bug.swift:6:5: error: ambiguous reference to member 'print(_:separator:terminator:)'
    print("no even numbers")
    ^~~~~

Notes:
This conditional works as expected:

if nums.filter({ $0 % 2 == 0}).isEmpty { 
    print("contains no even numbers") 
}

This gives a slightly different compilation error as above (error: missing argument for parameter #​1 in call):

if nums.filter() { $0 % 2 == 0}.isEmpty { 
    print("contains no even numbers") 
}

{noformat}
swift_bug.swift:5:20: error: anonymous closure argument not contained in a closure
if nums.filter() { $0 % 2 == 0 }.isEmpty {
                   ^
swift_bug.swift:5:33: error: consecutive statements on a line must be separated by ';'
if nums.filter() { $0 % 2 == 0 }.isEmpty {
                                ^
                                ;
swift_bug.swift:5:16: error: missing argument for parameter #​1 in call
if nums.filter() { $0 % 2 == 0 }.isEmpty {
               ^
               <#(Int) throws -> Bool#>
Swift._ArrayProtocol:2:17: note: 'filter' declared here
    public func filter(_ isIncluded: (Self.Element) throws -> Bool) rethrows -> [Self.Element]
                ^
swift_bug.swift:6:5: error: ambiguous reference to member 'print(_:separator:terminator:)'
    print("no even numbers")
    ^~~~~
{noformat}
@belkadan
Copy link
Contributor

We don't allow trailing closures in if-statements (for this sort of reason), but this is pretty poor recovery. @rintaro, @nkcsgexi, do you remember if there are any other bugs about improving the recovery?

@rintaro
Copy link
Mannequin

rintaro mannequin commented Sep 13, 2017

I don't think we have other bugs about this problem.

We currently recover only if closure is the outer most expression:

error: MyPlayground.playground:5:11: error: trailing closure requires parentheses for disambiguation in this context
if foo(1) { $0 + 42 } {  }
        ~~^
        , handler:   )

error: trailing closure requires parentheses for disambiguation in this context
if bar { $0 + 42 }, baz() {  }
      ~^
      (           )

We should expand our recovery logic, say "if } is immediately followed by [ . or ( on the same line, it must be a trailing closure."

@belkadan
Copy link
Contributor

That makes sense. We could even look for the absence of a space, since people conventionally don't put spaces in for any of those.

@belkadan
Copy link
Contributor

(but just checking the same line is probably sufficient)

@nicklockwood
Copy link
Contributor

I see this bug hasn't been updated in a while, but just FYI I've stumbled upon a case where trailing closures are actually allowed in an if statement, but probably shouldn't be:

func someFuncWithClosure(closure: (Int) -> Bool) -> Bool {
    return closure(4)
}

// this prints "true", with no warnings
if true || someFuncWithClosure { $0 == 4 } {
    print("true")
}

@mattneub
Copy link

Still getting very unhelpful diagnostics from the compiler on this. I just encountered it as follows:

if let stat = stats.filter{$0.dateFinished == date}.first {}

Result is error messages as follows:

  • Anonymous closure argument not contained in a closure

  • Consecutive statements on a line must be separated by ';'

  • Initializer for conditional binding must have Optional type, not '((Stat) throws -> Bool) throws -> [Stat]'

But none of these gets at the heart of the matter, which is merely that we need to surround `stats.filter{$0.dateFinished == date}` with parentheses.

@rintaro
Copy link
Mannequin

rintaro mannequin commented Apr 18, 2019

I had PR for this here #12457 .
Need to revisit.

@swift-ci
Copy link
Collaborator Author

Comment by Owen Voorhees (JIRA)

Resolved in #28362

If possible, please verify and close using the next available master snapshot.

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself parser Area → compiler: The legacy C++ parser
Projects
None yet
Development

No branches or pull requests

4 participants