Uploaded image for project: 'Groovy'
  1. Groovy
  2. GROOVY-7218

&& operator is not handled correctly in some cases when it starts a new line

    Details

    • Type: Bug
    • Status: Resolved
    • Priority: Minor
    • Resolution: Fixed
    • Affects Version/s: 2.3.8
    • Fix Version/s: 2.6.0-alpha-1
    • Component/s: Compiler
    • Labels:
      None
    • Environment:
      CentOS 7

      Description

      Repro

      A.groovy
      def files = new File(".").list(
          [ accept: { d, f ->
              (f ==~ ".*") && !f.endsWith(".tmp")
          }] as FilenameFilter
      ).toList()
      

      Result: works fine

      B.groovy
      def files = new File(".").list(
          [ accept: { d, f ->
              (f ==~ ".*")
                  && !f.endsWith(".tmp")
          }] as FilenameFilter
      ).toList()
      

      Result:

      org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
      /home/csbubbles/B.groovy: 5: unexpected token: && @ line 5, column 3.
         		&& !f.endsWith(".tmp")
           ^
      
      1 error
      

      Expected results

      B.groovy should get executed successfully the same way as A.groovy, as its two lines in a closure is the only expression.

      PS "return" statement doesn't help if you put it before "(f ==~ ".*")" either.

        Activity

        Hide
        csbubbles Maxim Novikov added a comment -

        PS It might be an issue with "return" operator (and the last statement in a block) in fact, because the following method also fails to compile:

        def a() {
            return true
                && false
                && true
        }
        

        Result:

        /home/csbubbles/X.groovy: 4: unexpected token: && @ line 4, column 3.
           		&& false
             ^
        
        1 error
        

        If you move all the conditions to the only line, it will work. The failure is pretty inconvenient as when you have complex conditions, you may want to split them to multiple lines just like I showed. And some projects have code style guidelines to start a new line with an operator. In Java it works fine since the beginning as I remember.

        Show
        csbubbles Maxim Novikov added a comment - PS It might be an issue with "return" operator (and the last statement in a block) in fact, because the following method also fails to compile: def a() { return true && false && true } Result: /home/csbubbles/X.groovy: 4: unexpected token: && @ line 4, column 3. && false ^ 1 error If you move all the conditions to the only line, it will work. The failure is pretty inconvenient as when you have complex conditions, you may want to split them to multiple lines just like I showed. And some projects have code style guidelines to start a new line with an operator. In Java it works fine since the beginning as I remember.
        Hide
        melix Cédric Champeau added a comment -

        I am lowering the priority because there are at leas two easy workarounds for this parsing issue:

        1. put the && operator at the end of the line instead of the beginning

        def files = new File(".").list(
            [ accept: { d, f ->
                (f ==~ ".*") &&
                    !f.endsWith(".tmp")
            }] as FilenameFilter
        ).toList()
        

        2. put a \ sign at the end of the line to tell the parser that the instruction continues at the following line

        def files = new File(".").list(
            [ accept: { d, f ->
                (f ==~ ".*") \
                    && !f.endsWith(".tmp")
            }] as FilenameFilter
        ).toList()
        
        Show
        melix Cédric Champeau added a comment - I am lowering the priority because there are at leas two easy workarounds for this parsing issue: 1. put the && operator at the end of the line instead of the beginning def files = new File( "." ).list( [ accept: { d, f -> (f ==~ ".*" ) && !f.endsWith( ".tmp" ) }] as FilenameFilter ).toList() 2. put a \ sign at the end of the line to tell the parser that the instruction continues at the following line def files = new File( "." ).list( [ accept: { d, f -> (f ==~ ".*" ) \ && !f.endsWith( ".tmp" ) }] as FilenameFilter ).toList()
        Hide
        tkruse Thibault Kruse added a comment -

        Also additional parentheses help.

        Show
        tkruse Thibault Kruse added a comment - Also additional parentheses help.
        Hide
        csbubbles Maxim Novikov added a comment -

        Workarounds suggested by Cédric are no good. First, for example, we start a line with an operator in such cases to clearly show that this is an operation continuing an expression, not a new expression (so, we don't put operators at the end of lines; sort of against the code style). The backslash also shouldn't be a required part of the language to write multiline expressions that can be separated by operators. The user of Groovy shall not care about where to put "&&" - at the end of a line or at the beginning of a new line in a complex expression. Introducing workarounds you essentially make the language (its syntax) more complicated and worse, because you encourage its users to use something very specific or not use at all (black magic / hidden knowledge).

        Show
        csbubbles Maxim Novikov added a comment - Workarounds suggested by Cédric are no good. First, for example, we start a line with an operator in such cases to clearly show that this is an operation continuing an expression, not a new expression (so, we don't put operators at the end of lines; sort of against the code style). The backslash also shouldn't be a required part of the language to write multiline expressions that can be separated by operators. The user of Groovy shall not care about where to put "&&" - at the end of a line or at the beginning of a new line in a complex expression. Introducing workarounds you essentially make the language (its syntax) more complicated and worse, because you encourage its users to use something very specific or not use at all (black magic / hidden knowledge).
        Hide
        jwagenleitner John Wagenleitner added a comment -
        Show
        jwagenleitner John Wagenleitner added a comment - I tested the repro with the antlr4 parser and verified issue is fixed and that test coverage exists: https://github.com/apache/groovy/blob/734bff6f3336d5b2607f2749d1f986434711486b/subprojects/parser-antlr4/src/test/resources/core/Expression_23x.groovy#L94-L96

          People

          • Assignee:
            jwagenleitner John Wagenleitner
            Reporter:
            csbubbles Maxim Novikov
          • Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development