You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@groovy.apache.org by Andres Almiray <aa...@gmail.com> on 2018/08/14 20:42:52 UTC

Updates on JaCoCo support

Hello everyone,

I've spent a couple of hours with JaCoCo team members at the Hackergarten
Bern this evening.
The goal of the session was to get started with an integration test for the
@Generated feature
added in Groovy 2.5.0.

You can see the outcome at https://github.com/jacoco/jacoco/pull/733

The good news is that Groovy applies @Generated on constructors added by
@Cannonical as well
as methods defined by the GroovyObject interface. The bad news is that the
test still fails
because the expectation is that *every* method generated by the compiler
that does not map
to a particular source line *should* be annotated with @Generated. The
following source

----
// This annotation generates the following
// - a constructor that takes an int as argument
// - a suitable implementation of toString()
// - a suitable implementation of hashCode()
// - a suitable implementation of equals(Object)
// - a public method named canEqual(Object)
// - a getter & setter for the valRead property
@groovy.transform.Canonical
class GroovyDataClassTarget { // assertFullyCovered()

    int valRead // assertNotCovered()

    static void main(String[] args) {
        new GroovyDataClassTarget() // assertFullyCovered()
    }
}
----

Generates bytecode equivalent to (decompiled with IntelliJ)

----
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.jacoco.core.test.validation.groovy.targets;

import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import groovy.transform.EqualsAndHashCode;
import groovy.transform.Generated;
import groovy.transform.ToString;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
import org.codehaus.groovy.runtime.typehandling.ShortTypeHandling;
import org.codehaus.groovy.util.HashCodeHelper;

@ToString
@EqualsAndHashCode
public class GroovyDataClassTarget implements GroovyObject {
    private int valRead;

    @Generated
    public GroovyDataClassTarget(int valRead) {
        CallSite[] var2 = $getCallSiteArray();
        super();
        MetaClass var3 = this.$getStaticMetaClass();
        this.metaClass = var3;
        this.valRead = DefaultTypeTransformation.intUnbox(valRead);
    }

    public GroovyDataClassTarget() {
        CallSite[] var1 = $getCallSiteArray();
        this(Integer.valueOf(0));
    }

    public static void main(String... args) {
        CallSite[] var1 = $getCallSiteArray();
        var1[0].callConstructor(GroovyDataClassTarget.class);
    }

    public String toString() {
        CallSite[] var1 = $getCallSiteArray();
        Object _result = var1[1].callConstructor(StringBuilder.class);
        Object $toStringFirst = Boolean.TRUE;
        var1[2].call(_result,
"org.jacoco.core.test.validation.groovy.targets.GroovyDataClassTarget(");
        if (DefaultTypeTransformation.booleanUnbox($toStringFirst)) {
            Boolean var4 = Boolean.FALSE;
        } else {
            var1[3].call(_result, ", ");
        }

        var1[4].call(_result, var1[5].callStatic(InvokerHelper.class,
var1[6].callCurrent(this)));
        var1[7].call(_result, ")");
        return
(String)ShortTypeHandling.castToString(var1[8].call(_result));
    }

    public int hashCode() {
        CallSite[] var1 = $getCallSiteArray();
        Object _result = var1[9].callStatic(HashCodeHelper.class);
        if
(!DefaultTypeTransformation.booleanUnbox(var1[10].call(var1[11].callCurrent(this),
this))) {
            Object var3 = var1[12].callStatic(HashCodeHelper.class,
_result, var1[13].callCurrent(this));
            _result = var3;
        }

        return DefaultTypeTransformation.intUnbox(_result);
    }

    public boolean canEqual(Object other) {
        CallSite[] var2 = $getCallSiteArray();
        return other instanceof GroovyDataClassTarget;
    }

    public boolean equals(Object other) {
        CallSite[] var2 = $getCallSiteArray();
        if (ScriptBytecodeAdapter.compareEqual(other, (Object)null)) {
            return false;
        } else if
(DefaultTypeTransformation.booleanUnbox(var2[14].callCurrent(this, other)))
{
            return true;
        } else if (!(other instanceof GroovyDataClassTarget)) {
            return false;
        } else {
            GroovyDataClassTarget otherTyped = (GroovyDataClassTarget)other;
            if
(!DefaultTypeTransformation.booleanUnbox(var2[15].call(otherTyped, this))) {
                return false;
            } else {
                return
ScriptBytecodeAdapter.compareEqual(var2[16].callCurrent(this),
var2[17].call(otherTyped));
            }
        }
    }

    public int getValRead() {
        return this.valRead;
    }

    public void setValRead(int var1) {
        this.valRead = var1;
    }
}
----

We can appreciate that the methods added by @ToString, @EqualsAndHashcode,
and the property getter/setter are not
annotated with @Generated, which will prompt JaCoCo to mark them as not
covered. The rationale from the JaCoCo team
is that these methods should be annotated as the compiler is "trusted",
only those methods explicitly added to the
source should be covered.

Thus, here comes the call to action and the reason why I wanted to start
this conversation in the first place:
 - modify the Groovy compiler to add @Generated on property getters and
setters.
 - modify core AST xforms to add @Generated where it makes sense.

Related to the original @Generated issue (as commented by Evgeny at
https://github.com/jacoco/jacoco/pull/610) fields
do not have line numbers, would be good to have them.

What do you think?

Cheers,
Andres

-------------------------------------------
Java Champion; Groovy Enthusiast
JCP EC Associate Seat
http://andresalmiray.com
http://www.linkedin.com/in/aalmiray
--
What goes up, must come down. Ask any system administrator.
There are 10 types of people in the world: Those who understand binary, and
those who don't.
To understand recursion, we must first understand recursion.

Re: Updates on JaCoCo support

Posted by "Milles, Eric (TR Technology & Ops)" <er...@thomsonreuters.com>.
There is a modifier synthetic and there is a separate boolean kept in AnnotatedNode.


    private boolean synthetic;


    /**
     * returns true if this node is added by the compiler.
     * <b>NOTE</b>:
     * This method has nothing to do with the synthetic flag
     * for fields, methods or classes.
     * @return true if this node is added by the compiler
     */
    public boolean isSynthetic() {
        return synthetic;
    }

    /**
     * sets this node as a node added by the compiler.
     * <b>NOTE</b>:
     * This method has nothing to do with the synthetic flag
     * for fields, methods or classes.
     * @param synthetic - if true this node is marked as
     *                    added by the compiler
     */
    public void setSynthetic(boolean synthetic) {
        this.synthetic = synthetic;
    }


________________________________
From: Andres Almiray <aa...@gmail.com>
Sent: Wednesday, August 15, 2018 3:53 PM
To: dev@groovy.apache.org
Subject: Re: Updates on JaCoCo support

There’s a difference between synthetic and having the @Generated annotation, they are not equivalent. Synthetic signals tools to ignore the method altogether (JaCoCo honors this behavior). @Generated should be applied to non-synthetic methods/classes; what a particular tool decides to do with that information is up to the tool itself, in the case of JaCoCo it’ll skip the method/class from coverage.

The reason I raises this issue is to see if there are any objections in adding @Generated to most (if not all) compiler generated methods (property getter/setter, core AST xforms) as this change touches lots of files, however it has no impact of Groovy behavior, rather it impacts tools that may parse Groovy bytecode (like JaCoCo).

Cheers
Andres (with no extra a)

Sent from my primitive Tricorder

On 15 Aug 2018, at 22:43, Milles, Eric (TR Technology & Ops) <er...@thomsonreuters.com>> wrote:


Andreas,


One place to start is everywhere that "AnnotatedNode.setSynthetic(true)" is currently called.  I think this misses the methods added for a property.  But it does cover several AST transforms.  And maybe the transforms that add methods and don't call this method could do so in addition to the modification to add "@Generated".  Maybe the call to setSynthetic could actually add the annotation.  Or you could create a utility method that sets synthetic and adds "@Generated".


________________________________
From: Andres Almiray <aa...@gmail.com>>
Sent: Tuesday, August 14, 2018 3:42 PM
To: dev@groovy.apache.org<ma...@groovy.apache.org>
Subject: Updates on JaCoCo support

Hello everyone,

I've spent a couple of hours with JaCoCo team members at the Hackergarten Bern this evening.
The goal of the session was to get started with an integration test for the @Generated feature
added in Groovy 2.5.0.

You can see the outcome at https://github.com/jacoco/jacoco/pull/733<https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_jacoco_jacoco_pull_733&d=DwMFaQ&c=4ZIZThykDLcoWk-GVjSLmy8-1Cr1I4FWIvbLFebwKgY&r=tPJuIuL_GkTEazjQW7vvl7mNWVGXn3yJD5LGBHYYHww&m=hAfmXOD5R2AP8VF69wcL-UqYZEv2tXAbyBRfDJh0lfg&s=kxj4hAyP-_6cuc12J6CSMgl_xJRqfEjVcnMHfxzID8w&e=>

The good news is that Groovy applies @Generated on constructors added by @Cannonical as well
as methods defined by the GroovyObject interface. The bad news is that the test still fails
because the expectation is that *every* method generated by the compiler that does not map
to a particular source line *should* be annotated with @Generated. The following source

----
// This annotation generates the following
// - a constructor that takes an int as argument
// - a suitable implementation of toString()
// - a suitable implementation of hashCode()
// - a suitable implementation of equals(Object)
// - a public method named canEqual(Object)
// - a getter & setter for the valRead property
@groovy.transform.Canonical
class GroovyDataClassTarget { // assertFullyCovered()

    int valRead // assertNotCovered()

    static void main(String[] args) {
        new GroovyDataClassTarget() // assertFullyCovered()
    }
}
----

Generates bytecode equivalent to (decompiled with IntelliJ)

----
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.jacoco.core.test.validation.groovy.targets;

import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import groovy.transform.EqualsAndHashCode;
import groovy.transform.Generated;
import groovy.transform.ToString;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
import org.codehaus.groovy.runtime.typehandling.ShortTypeHandling;
import org.codehaus.groovy.util.HashCodeHelper;

@ToString
@EqualsAndHashCode
public class GroovyDataClassTarget implements GroovyObject {
    private int valRead;

    @Generated
    public GroovyDataClassTarget(int valRead) {
        CallSite[] var2 = $getCallSiteArray();
        super();
        MetaClass var3 = this.$getStaticMetaClass();
        this.metaClass = var3;
        this.valRead = DefaultTypeTransformation.intUnbox(valRead);
    }

    public GroovyDataClassTarget() {
        CallSite[] var1 = $getCallSiteArray();
        this(Integer.valueOf(0));
    }

    public static void main(String... args) {
        CallSite[] var1 = $getCallSiteArray();
        var1[0].callConstructor(GroovyDataClassTarget.class);
    }

    public String toString() {
        CallSite[] var1 = $getCallSiteArray();
        Object _result = var1[1].callConstructor(StringBuilder.class);
        Object $toStringFirst = Boolean.TRUE;
        var1[2].call(_result, "org.jacoco.core.test.validation.groovy.targets.GroovyDataClassTarget(");
        if (DefaultTypeTransformation.booleanUnbox($toStringFirst)) {
            Boolean var4 = Boolean.FALSE;
        } else {
            var1[3].call(_result, ", ");
        }

        var1[4].call(_result, var1[5].callStatic(InvokerHelper.class, var1[6].callCurrent(this)));
        var1[7].call(_result, ")");
        return (String)ShortTypeHandling.castToString(var1[8].call(_result));
    }

    public int hashCode() {
        CallSite[] var1 = $getCallSiteArray();
        Object _result = var1[9].callStatic(HashCodeHelper.class);
        if (!DefaultTypeTransformation.booleanUnbox(var1[10].call(var1[11].callCurrent(this), this))) {
            Object var3 = var1[12].callStatic(HashCodeHelper.class, _result, var1[13].callCurrent(this));
            _result = var3;
        }

        return DefaultTypeTransformation.intUnbox(_result);
    }

    public boolean canEqual(Object other) {
        CallSite[] var2 = $getCallSiteArray();
        return other instanceof GroovyDataClassTarget;
    }

    public boolean equals(Object other) {
        CallSite[] var2 = $getCallSiteArray();
        if (ScriptBytecodeAdapter.compareEqual(other, (Object)null)) {
            return false;
        } else if (DefaultTypeTransformation.booleanUnbox(var2[14].callCurrent(this, other))) {
            return true;
        } else if (!(other instanceof GroovyDataClassTarget)) {
            return false;
        } else {
            GroovyDataClassTarget otherTyped = (GroovyDataClassTarget)other;
            if (!DefaultTypeTransformation.booleanUnbox(var2[15].call(otherTyped, this))) {
                return false;
            } else {
                return ScriptBytecodeAdapter.compareEqual(var2[16].callCurrent(this), var2[17].call(otherTyped));
            }
        }
    }

    public int getValRead() {
        return this.valRead;
    }

    public void setValRead(int var1) {
        this.valRead = var1;
    }
}
----

We can appreciate that the methods added by @ToString, @EqualsAndHashcode, and the property getter/setter are not
annotated with @Generated, which will prompt JaCoCo to mark them as not covered. The rationale from the JaCoCo team
is that these methods should be annotated as the compiler is "trusted", only those methods explicitly added to the
source should be covered.

Thus, here comes the call to action and the reason why I wanted to start this conversation in the first place:
 - modify the Groovy compiler to add @Generated on property getters and setters.
 - modify core AST xforms to add @Generated where it makes sense.

Related to the original @Generated issue (as commented by Evgeny at https://github.com/jacoco/jacoco/pull/610<https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_jacoco_jacoco_pull_610&d=DwMFaQ&c=4ZIZThykDLcoWk-GVjSLmy8-1Cr1I4FWIvbLFebwKgY&r=tPJuIuL_GkTEazjQW7vvl7mNWVGXn3yJD5LGBHYYHww&m=hAfmXOD5R2AP8VF69wcL-UqYZEv2tXAbyBRfDJh0lfg&s=44Nu_515VJUtJ8wIVcQJF2ow_axTotUprpo9KwRmQMY&e=>) fields
do not have line numbers, would be good to have them.

What do you think?

Cheers,
Andres

-------------------------------------------
Java Champion; Groovy Enthusiast
JCP EC Associate Seat
http://andresalmiray.com<https://urldefense.proofpoint.com/v2/url?u=http-3A__andresalmiray.com&d=DwMFaQ&c=4ZIZThykDLcoWk-GVjSLmy8-1Cr1I4FWIvbLFebwKgY&r=tPJuIuL_GkTEazjQW7vvl7mNWVGXn3yJD5LGBHYYHww&m=hAfmXOD5R2AP8VF69wcL-UqYZEv2tXAbyBRfDJh0lfg&s=ynGizu7RDzXxq8Mp65SEUYPVyKHTo7kc14uNurHMLRM&e=>
http://www.linkedin.com/in/aalmiray<https://urldefense.proofpoint.com/v2/url?u=http-3A__www.linkedin.com_in_aalmiray&d=DwMFaQ&c=4ZIZThykDLcoWk-GVjSLmy8-1Cr1I4FWIvbLFebwKgY&r=tPJuIuL_GkTEazjQW7vvl7mNWVGXn3yJD5LGBHYYHww&m=hAfmXOD5R2AP8VF69wcL-UqYZEv2tXAbyBRfDJh0lfg&s=jMgNE8ncoIZmIOY8tu6FwSSs_8KA4Vh6-7GpRqDNEd4&e=>
--
What goes up, must come down. Ask any system administrator.
There are 10 types of people in the world: Those who understand binary, and those who don't.
To understand recursion, we must first understand recursion.

Re: Updates on JaCoCo support

Posted by Paul King <pa...@asert.com.au>.
I'm +1 for adding whatever we need to make coverage work.

Having said that, we shouldn't need to add it for methods (classes?)
already marked synthetic. I also noticed that the kotlin support looks for
a lack of line number information. Could we do something similar? We leave
line number info as -1 in many cases and could do that check just for
classes implementing GroovyObject. Or was this approach ruled out earlier?
The line number approach is more of a hack I guess but we could leave that
as the fall back. That could enable things to almost totally work now and
we can improve incrementally by adding synthetic or @Generated in more
places over time.

Thoughts?

Paul.


On Thu, Aug 16, 2018 at 6:53 AM Andres Almiray <aa...@gmail.com> wrote:

> There’s a difference between synthetic and having the @Generated
> annotation, they are not equivalent. Synthetic signals tools to ignore the
> method altogether (JaCoCo honors this behavior). @Generated should be
> applied to non-synthetic methods/classes; what a particular tool decides to
> do with that information is up to the tool itself, in the case of JaCoCo
> it’ll skip the method/class from coverage.
>
> The reason I raises this issue is to see if there are any objections in
> adding @Generated to most (if not all) compiler generated methods (property
> getter/setter, core AST xforms) as this change touches lots of files,
> however it has no impact of Groovy behavior, rather it impacts tools that
> may parse Groovy bytecode (like JaCoCo).
>
> Cheers
> Andres (with no extra a)
>
> Sent from my primitive Tricorder
>
> On 15 Aug 2018, at 22:43, Milles, Eric (TR Technology & Ops) <
> eric.milles@thomsonreuters.com> wrote:
>
> Andreas,
>
>
> One place to start is everywhere that "AnnotatedNode.setSynthetic(true)"
> is currently called.  I think this misses the methods added for a
> property.  But it does cover several AST transforms.  And maybe the
> transforms that add methods and don't call this method could do so in
> addition to the modification to add "@Generated".  Maybe the call to
> setSynthetic could actually add the annotation.  Or you could create a
> utility method that sets synthetic and adds "@Generated".
>
>
> ------------------------------
> *From:* Andres Almiray <aa...@gmail.com>
> *Sent:* Tuesday, August 14, 2018 3:42 PM
> *To:* dev@groovy.apache.org
> *Subject:* Updates on JaCoCo support
>
> Hello everyone,
>
> I've spent a couple of hours with JaCoCo team members at the Hackergarten
> Bern this evening.
> The goal of the session was to get started with an integration test for
> the @Generated feature
> added in Groovy 2.5.0.
>
> You can see the outcome at https://github.com/jacoco/jacoco/pull/733
> <https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_jacoco_jacoco_pull_733&d=DwMFaQ&c=4ZIZThykDLcoWk-GVjSLmy8-1Cr1I4FWIvbLFebwKgY&r=tPJuIuL_GkTEazjQW7vvl7mNWVGXn3yJD5LGBHYYHww&m=hAfmXOD5R2AP8VF69wcL-UqYZEv2tXAbyBRfDJh0lfg&s=kxj4hAyP-_6cuc12J6CSMgl_xJRqfEjVcnMHfxzID8w&e=>
>
> The good news is that Groovy applies @Generated on constructors added by
> @Cannonical as well
> as methods defined by the GroovyObject interface. The bad news is that the
> test still fails
> because the expectation is that *every* method generated by the compiler
> that does not map
> to a particular source line *should* be annotated with @Generated. The
> following source
>
> ----
> // This annotation generates the following
> // - a constructor that takes an int as argument
> // - a suitable implementation of toString()
> // - a suitable implementation of hashCode()
> // - a suitable implementation of equals(Object)
> // - a public method named canEqual(Object)
> // - a getter & setter for the valRead property
> @groovy.transform.Canonical
> class GroovyDataClassTarget { // assertFullyCovered()
>
>     int valRead // assertNotCovered()
>
>     static void main(String[] args) {
>         new GroovyDataClassTarget() // assertFullyCovered()
>     }
> }
> ----
>
> Generates bytecode equivalent to (decompiled with IntelliJ)
>
> ----
> //
> // Source code recreated from a .class file by IntelliJ IDEA
> // (powered by Fernflower decompiler)
> //
>
> package org.jacoco.core.test.validation.groovy.targets;
>
> import groovy.lang.GroovyObject;
> import groovy.lang.MetaClass;
> import groovy.transform.EqualsAndHashCode;
> import groovy.transform.Generated;
> import groovy.transform.ToString;
> import org.codehaus.groovy.runtime.InvokerHelper;
> import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
> import org.codehaus.groovy.runtime.callsite.CallSite;
> import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
> import org.codehaus.groovy.runtime.typehandling.ShortTypeHandling;
> import org.codehaus.groovy.util.HashCodeHelper;
>
> @ToString
> @EqualsAndHashCode
> public class GroovyDataClassTarget implements GroovyObject {
>     private int valRead;
>
>     @Generated
>     public GroovyDataClassTarget(int valRead) {
>         CallSite[] var2 = $getCallSiteArray();
>         super();
>         MetaClass var3 = this.$getStaticMetaClass();
>         this.metaClass = var3;
>         this.valRead = DefaultTypeTransformation.intUnbox(valRead);
>     }
>
>     public GroovyDataClassTarget() {
>         CallSite[] var1 = $getCallSiteArray();
>         this(Integer.valueOf(0));
>     }
>
>     public static void main(String... args) {
>         CallSite[] var1 = $getCallSiteArray();
>         var1[0].callConstructor(GroovyDataClassTarget.class);
>     }
>
>     public String toString() {
>         CallSite[] var1 = $getCallSiteArray();
>         Object _result = var1[1].callConstructor(StringBuilder.class);
>         Object $toStringFirst = Boolean.TRUE;
>         var1[2].call(_result,
> "org.jacoco.core.test.validation.groovy.targets.GroovyDataClassTarget(");
>         if (DefaultTypeTransformation.booleanUnbox($toStringFirst)) {
>             Boolean var4 = Boolean.FALSE;
>         } else {
>             var1[3].call(_result, ", ");
>         }
>
>         var1[4].call(_result, var1[5].callStatic(InvokerHelper.class,
> var1[6].callCurrent(this)));
>         var1[7].call(_result, ")");
>         return
> (String)ShortTypeHandling.castToString(var1[8].call(_result));
>     }
>
>     public int hashCode() {
>         CallSite[] var1 = $getCallSiteArray();
>         Object _result = var1[9].callStatic(HashCodeHelper.class);
>         if
> (!DefaultTypeTransformation.booleanUnbox(var1[10].call(var1[11].callCurrent(this),
> this))) {
>             Object var3 = var1[12].callStatic(HashCodeHelper.class,
> _result, var1[13].callCurrent(this));
>             _result = var3;
>         }
>
>         return DefaultTypeTransformation.intUnbox(_result);
>     }
>
>     public boolean canEqual(Object other) {
>         CallSite[] var2 = $getCallSiteArray();
>         return other instanceof GroovyDataClassTarget;
>     }
>
>     public boolean equals(Object other) {
>         CallSite[] var2 = $getCallSiteArray();
>         if (ScriptBytecodeAdapter.compareEqual(other, (Object)null)) {
>             return false;
>         } else if
> (DefaultTypeTransformation.booleanUnbox(var2[14].callCurrent(this, other)))
> {
>             return true;
>         } else if (!(other instanceof GroovyDataClassTarget)) {
>             return false;
>         } else {
>             GroovyDataClassTarget otherTyped =
> (GroovyDataClassTarget)other;
>             if
> (!DefaultTypeTransformation.booleanUnbox(var2[15].call(otherTyped, this))) {
>                 return false;
>             } else {
>                 return
> ScriptBytecodeAdapter.compareEqual(var2[16].callCurrent(this),
> var2[17].call(otherTyped));
>             }
>         }
>     }
>
>     public int getValRead() {
>         return this.valRead;
>     }
>
>     public void setValRead(int var1) {
>         this.valRead = var1;
>     }
> }
> ----
>
> We can appreciate that the methods added by @ToString, @EqualsAndHashcode,
> and the property getter/setter are not
> annotated with @Generated, which will prompt JaCoCo to mark them as not
> covered. The rationale from the JaCoCo team
> is that these methods should be annotated as the compiler is "trusted",
> only those methods explicitly added to the
> source should be covered.
>
> Thus, here comes the call to action and the reason why I wanted to start
> this conversation in the first place:
>  - modify the Groovy compiler to add @Generated on property getters and
> setters.
>  - modify core AST xforms to add @Generated where it makes sense.
>
> Related to the original @Generated issue (as commented by Evgeny at
> https://github.com/jacoco/jacoco/pull/610
> <https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_jacoco_jacoco_pull_610&d=DwMFaQ&c=4ZIZThykDLcoWk-GVjSLmy8-1Cr1I4FWIvbLFebwKgY&r=tPJuIuL_GkTEazjQW7vvl7mNWVGXn3yJD5LGBHYYHww&m=hAfmXOD5R2AP8VF69wcL-UqYZEv2tXAbyBRfDJh0lfg&s=44Nu_515VJUtJ8wIVcQJF2ow_axTotUprpo9KwRmQMY&e=>)
> fields
> do not have line numbers, would be good to have them.
>
> What do you think?
>
> Cheers,
> Andres
>
> -------------------------------------------
> Java Champion; Groovy Enthusiast
> JCP EC Associate Seat
> http://andresalmiray.com
> <https://urldefense.proofpoint.com/v2/url?u=http-3A__andresalmiray.com&d=DwMFaQ&c=4ZIZThykDLcoWk-GVjSLmy8-1Cr1I4FWIvbLFebwKgY&r=tPJuIuL_GkTEazjQW7vvl7mNWVGXn3yJD5LGBHYYHww&m=hAfmXOD5R2AP8VF69wcL-UqYZEv2tXAbyBRfDJh0lfg&s=ynGizu7RDzXxq8Mp65SEUYPVyKHTo7kc14uNurHMLRM&e=>
> http://www.linkedin.com/in/aalmiray
> <https://urldefense.proofpoint.com/v2/url?u=http-3A__www.linkedin.com_in_aalmiray&d=DwMFaQ&c=4ZIZThykDLcoWk-GVjSLmy8-1Cr1I4FWIvbLFebwKgY&r=tPJuIuL_GkTEazjQW7vvl7mNWVGXn3yJD5LGBHYYHww&m=hAfmXOD5R2AP8VF69wcL-UqYZEv2tXAbyBRfDJh0lfg&s=jMgNE8ncoIZmIOY8tu6FwSSs_8KA4Vh6-7GpRqDNEd4&e=>
> --
> What goes up, must come down. Ask any system administrator.
> There are 10 types of people in the world: Those who understand binary,
> and those who don't.
> To understand recursion, we must first understand recursion.
>
>

Re: Updates on JaCoCo support

Posted by Andres Almiray <aa...@gmail.com>.
There’s a difference between synthetic and having the @Generated annotation, they are not equivalent. Synthetic signals tools to ignore the method altogether (JaCoCo honors this behavior). @Generated should be applied to non-synthetic methods/classes; what a particular tool decides to do with that information is up to the tool itself, in the case of JaCoCo it’ll skip the method/class from coverage. 

The reason I raises this issue is to see if there are any objections in adding @Generated to most (if not all) compiler generated methods (property getter/setter, core AST xforms) as this change touches lots of files, however it has no impact of Groovy behavior, rather it impacts tools that may parse Groovy bytecode (like JaCoCo). 

Cheers
Andres (with no extra a)

Sent from my primitive Tricorder

> On 15 Aug 2018, at 22:43, Milles, Eric (TR Technology & Ops) <er...@thomsonreuters.com> wrote:
> 
> Andreas,
> 
> One place to start is everywhere that "AnnotatedNode.setSynthetic(true)" is currently called.  I think this misses the methods added for a property.  But it does cover several AST transforms.  And maybe the transforms that add methods and don't call this method could do so in addition to the modification to add "@Generated".  Maybe the call to setSynthetic could actually add the annotation.  Or you could create a utility method that sets synthetic and adds "@Generated".
> 
> From: Andres Almiray <aa...@gmail.com>
> Sent: Tuesday, August 14, 2018 3:42 PM
> To: dev@groovy.apache.org
> Subject: Updates on JaCoCo support
>  
> Hello everyone,
> 
> I've spent a couple of hours with JaCoCo team members at the Hackergarten Bern this evening.
> The goal of the session was to get started with an integration test for the @Generated feature
> added in Groovy 2.5.0.
> 
> You can see the outcome at https://github.com/jacoco/jacoco/pull/733
> 
> The good news is that Groovy applies @Generated on constructors added by @Cannonical as well
> as methods defined by the GroovyObject interface. The bad news is that the test still fails
> because the expectation is that *every* method generated by the compiler that does not map
> to a particular source line *should* be annotated with @Generated. The following source
> 
> ----
> // This annotation generates the following
> // - a constructor that takes an int as argument
> // - a suitable implementation of toString()
> // - a suitable implementation of hashCode()
> // - a suitable implementation of equals(Object)
> // - a public method named canEqual(Object)
> // - a getter & setter for the valRead property
> @groovy.transform.Canonical
> class GroovyDataClassTarget { // assertFullyCovered()
> 
>     int valRead // assertNotCovered()
> 
>     static void main(String[] args) {
>         new GroovyDataClassTarget() // assertFullyCovered()
>     }
> }
> ----
> 
> Generates bytecode equivalent to (decompiled with IntelliJ)
> 
> ----
> //
> // Source code recreated from a .class file by IntelliJ IDEA
> // (powered by Fernflower decompiler)
> //
> 
> package org.jacoco.core.test.validation.groovy.targets;
> 
> import groovy.lang.GroovyObject;
> import groovy.lang.MetaClass;
> import groovy.transform.EqualsAndHashCode;
> import groovy.transform.Generated;
> import groovy.transform.ToString;
> import org.codehaus.groovy.runtime.InvokerHelper;
> import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
> import org.codehaus.groovy.runtime.callsite.CallSite;
> import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
> import org.codehaus.groovy.runtime.typehandling.ShortTypeHandling;
> import org.codehaus.groovy.util.HashCodeHelper;
> 
> @ToString
> @EqualsAndHashCode
> public class GroovyDataClassTarget implements GroovyObject {
>     private int valRead;
> 
>     @Generated
>     public GroovyDataClassTarget(int valRead) {
>         CallSite[] var2 = $getCallSiteArray();
>         super();
>         MetaClass var3 = this.$getStaticMetaClass();
>         this.metaClass = var3;
>         this.valRead = DefaultTypeTransformation.intUnbox(valRead);
>     }
> 
>     public GroovyDataClassTarget() {
>         CallSite[] var1 = $getCallSiteArray();
>         this(Integer.valueOf(0));
>     }
> 
>     public static void main(String... args) {
>         CallSite[] var1 = $getCallSiteArray();
>         var1[0].callConstructor(GroovyDataClassTarget.class);
>     }
> 
>     public String toString() {
>         CallSite[] var1 = $getCallSiteArray();
>         Object _result = var1[1].callConstructor(StringBuilder.class);
>         Object $toStringFirst = Boolean.TRUE;
>         var1[2].call(_result, "org.jacoco.core.test.validation.groovy.targets.GroovyDataClassTarget(");
>         if (DefaultTypeTransformation.booleanUnbox($toStringFirst)) {
>             Boolean var4 = Boolean.FALSE;
>         } else {
>             var1[3].call(_result, ", ");
>         }
> 
>         var1[4].call(_result, var1[5].callStatic(InvokerHelper.class, var1[6].callCurrent(this)));
>         var1[7].call(_result, ")");
>         return (String)ShortTypeHandling.castToString(var1[8].call(_result));
>     }
> 
>     public int hashCode() {
>         CallSite[] var1 = $getCallSiteArray();
>         Object _result = var1[9].callStatic(HashCodeHelper.class);
>         if (!DefaultTypeTransformation.booleanUnbox(var1[10].call(var1[11].callCurrent(this), this))) {
>             Object var3 = var1[12].callStatic(HashCodeHelper.class, _result, var1[13].callCurrent(this));
>             _result = var3;
>         }
> 
>         return DefaultTypeTransformation.intUnbox(_result);
>     }
> 
>     public boolean canEqual(Object other) {
>         CallSite[] var2 = $getCallSiteArray();
>         return other instanceof GroovyDataClassTarget;
>     }
> 
>     public boolean equals(Object other) {
>         CallSite[] var2 = $getCallSiteArray();
>         if (ScriptBytecodeAdapter.compareEqual(other, (Object)null)) {
>             return false;
>         } else if (DefaultTypeTransformation.booleanUnbox(var2[14].callCurrent(this, other))) {
>             return true;
>         } else if (!(other instanceof GroovyDataClassTarget)) {
>             return false;
>         } else {
>             GroovyDataClassTarget otherTyped = (GroovyDataClassTarget)other;
>             if (!DefaultTypeTransformation.booleanUnbox(var2[15].call(otherTyped, this))) {
>                 return false;
>             } else {
>                 return ScriptBytecodeAdapter.compareEqual(var2[16].callCurrent(this), var2[17].call(otherTyped));
>             }
>         }
>     }
> 
>     public int getValRead() {
>         return this.valRead;
>     }
> 
>     public void setValRead(int var1) {
>         this.valRead = var1;
>     }
> }
> ----
> 
> We can appreciate that the methods added by @ToString, @EqualsAndHashcode, and the property getter/setter are not
> annotated with @Generated, which will prompt JaCoCo to mark them as not covered. The rationale from the JaCoCo team
> is that these methods should be annotated as the compiler is "trusted", only those methods explicitly added to the
> source should be covered.
> 
> Thus, here comes the call to action and the reason why I wanted to start this conversation in the first place:
>  - modify the Groovy compiler to add @Generated on property getters and setters.
>  - modify core AST xforms to add @Generated where it makes sense.
> 
> Related to the original @Generated issue (as commented by Evgeny at https://github.com/jacoco/jacoco/pull/610) fields
> do not have line numbers, would be good to have them.
> 
> What do you think?
> 
> Cheers,
> Andres
> 
> -------------------------------------------
> Java Champion; Groovy Enthusiast
> JCP EC Associate Seat
> http://andresalmiray.com
> http://www.linkedin.com/in/aalmiray
> --
> What goes up, must come down. Ask any system administrator.
> There are 10 types of people in the world: Those who understand binary, and those who don't.
> To understand recursion, we must first understand recursion.

Re: Updates on JaCoCo support

Posted by "Milles, Eric (TR Technology & Ops)" <er...@thomsonreuters.com>.
Actually, Verifier.visitProperty(PropertyNode) does call setSynthetic(true).

________________________________
From: Milles, Eric (TR Technology & Ops) <er...@thomsonreuters.com>
Sent: Wednesday, August 15, 2018 3:43 PM
To: dev@groovy.apache.org
Subject: Re: Updates on JaCoCo support


Andreas,


One place to start is everywhere that "AnnotatedNode.setSynthetic(true)" is currently called.  I think this misses the methods added for a property.  But it does cover several AST transforms.  And maybe the transforms that add methods and don't call this method could do so in addition to the modification to add "@Generated".  Maybe the call to setSynthetic could actually add the annotation.  Or you could create a utility method that sets synthetic and adds "@Generated".


________________________________
From: Andres Almiray <aa...@gmail.com>
Sent: Tuesday, August 14, 2018 3:42 PM
To: dev@groovy.apache.org
Subject: Updates on JaCoCo support

Hello everyone,

I've spent a couple of hours with JaCoCo team members at the Hackergarten Bern this evening.
The goal of the session was to get started with an integration test for the @Generated feature
added in Groovy 2.5.0.

You can see the outcome at https://github.com/jacoco/jacoco/pull/733<https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_jacoco_jacoco_pull_733&d=DwMFaQ&c=4ZIZThykDLcoWk-GVjSLmy8-1Cr1I4FWIvbLFebwKgY&r=tPJuIuL_GkTEazjQW7vvl7mNWVGXn3yJD5LGBHYYHww&m=hAfmXOD5R2AP8VF69wcL-UqYZEv2tXAbyBRfDJh0lfg&s=kxj4hAyP-_6cuc12J6CSMgl_xJRqfEjVcnMHfxzID8w&e=>

The good news is that Groovy applies @Generated on constructors added by @Cannonical as well
as methods defined by the GroovyObject interface. The bad news is that the test still fails
because the expectation is that *every* method generated by the compiler that does not map
to a particular source line *should* be annotated with @Generated. The following source

----
// This annotation generates the following
// - a constructor that takes an int as argument
// - a suitable implementation of toString()
// - a suitable implementation of hashCode()
// - a suitable implementation of equals(Object)
// - a public method named canEqual(Object)
// - a getter & setter for the valRead property
@groovy.transform.Canonical
class GroovyDataClassTarget { // assertFullyCovered()

    int valRead // assertNotCovered()

    static void main(String[] args) {
        new GroovyDataClassTarget() // assertFullyCovered()
    }
}
----

Generates bytecode equivalent to (decompiled with IntelliJ)

----
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.jacoco.core.test.validation.groovy.targets;

import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import groovy.transform.EqualsAndHashCode;
import groovy.transform.Generated;
import groovy.transform.ToString;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
import org.codehaus.groovy.runtime.typehandling.ShortTypeHandling;
import org.codehaus.groovy.util.HashCodeHelper;

@ToString
@EqualsAndHashCode
public class GroovyDataClassTarget implements GroovyObject {
    private int valRead;

    @Generated
    public GroovyDataClassTarget(int valRead) {
        CallSite[] var2 = $getCallSiteArray();
        super();
        MetaClass var3 = this.$getStaticMetaClass();
        this.metaClass = var3;
        this.valRead = DefaultTypeTransformation.intUnbox(valRead);
    }

    public GroovyDataClassTarget() {
        CallSite[] var1 = $getCallSiteArray();
        this(Integer.valueOf(0));
    }

    public static void main(String... args) {
        CallSite[] var1 = $getCallSiteArray();
        var1[0].callConstructor(GroovyDataClassTarget.class);
    }

    public String toString() {
        CallSite[] var1 = $getCallSiteArray();
        Object _result = var1[1].callConstructor(StringBuilder.class);
        Object $toStringFirst = Boolean.TRUE;
        var1[2].call(_result, "org.jacoco.core.test.validation.groovy.targets.GroovyDataClassTarget(");
        if (DefaultTypeTransformation.booleanUnbox($toStringFirst)) {
            Boolean var4 = Boolean.FALSE;
        } else {
            var1[3].call(_result, ", ");
        }

        var1[4].call(_result, var1[5].callStatic(InvokerHelper.class, var1[6].callCurrent(this)));
        var1[7].call(_result, ")");
        return (String)ShortTypeHandling.castToString(var1[8].call(_result));
    }

    public int hashCode() {
        CallSite[] var1 = $getCallSiteArray();
        Object _result = var1[9].callStatic(HashCodeHelper.class);
        if (!DefaultTypeTransformation.booleanUnbox(var1[10].call(var1[11].callCurrent(this), this))) {
            Object var3 = var1[12].callStatic(HashCodeHelper.class, _result, var1[13].callCurrent(this));
            _result = var3;
        }

        return DefaultTypeTransformation.intUnbox(_result);
    }

    public boolean canEqual(Object other) {
        CallSite[] var2 = $getCallSiteArray();
        return other instanceof GroovyDataClassTarget;
    }

    public boolean equals(Object other) {
        CallSite[] var2 = $getCallSiteArray();
        if (ScriptBytecodeAdapter.compareEqual(other, (Object)null)) {
            return false;
        } else if (DefaultTypeTransformation.booleanUnbox(var2[14].callCurrent(this, other))) {
            return true;
        } else if (!(other instanceof GroovyDataClassTarget)) {
            return false;
        } else {
            GroovyDataClassTarget otherTyped = (GroovyDataClassTarget)other;
            if (!DefaultTypeTransformation.booleanUnbox(var2[15].call(otherTyped, this))) {
                return false;
            } else {
                return ScriptBytecodeAdapter.compareEqual(var2[16].callCurrent(this), var2[17].call(otherTyped));
            }
        }
    }

    public int getValRead() {
        return this.valRead;
    }

    public void setValRead(int var1) {
        this.valRead = var1;
    }
}
----

We can appreciate that the methods added by @ToString, @EqualsAndHashcode, and the property getter/setter are not
annotated with @Generated, which will prompt JaCoCo to mark them as not covered. The rationale from the JaCoCo team
is that these methods should be annotated as the compiler is "trusted", only those methods explicitly added to the
source should be covered.

Thus, here comes the call to action and the reason why I wanted to start this conversation in the first place:
 - modify the Groovy compiler to add @Generated on property getters and setters.
 - modify core AST xforms to add @Generated where it makes sense.

Related to the original @Generated issue (as commented by Evgeny at https://github.com/jacoco/jacoco/pull/610<https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_jacoco_jacoco_pull_610&d=DwMFaQ&c=4ZIZThykDLcoWk-GVjSLmy8-1Cr1I4FWIvbLFebwKgY&r=tPJuIuL_GkTEazjQW7vvl7mNWVGXn3yJD5LGBHYYHww&m=hAfmXOD5R2AP8VF69wcL-UqYZEv2tXAbyBRfDJh0lfg&s=44Nu_515VJUtJ8wIVcQJF2ow_axTotUprpo9KwRmQMY&e=>) fields
do not have line numbers, would be good to have them.

What do you think?

Cheers,
Andres

-------------------------------------------
Java Champion; Groovy Enthusiast
JCP EC Associate Seat
http://andresalmiray.com<https://urldefense.proofpoint.com/v2/url?u=http-3A__andresalmiray.com&d=DwMFaQ&c=4ZIZThykDLcoWk-GVjSLmy8-1Cr1I4FWIvbLFebwKgY&r=tPJuIuL_GkTEazjQW7vvl7mNWVGXn3yJD5LGBHYYHww&m=hAfmXOD5R2AP8VF69wcL-UqYZEv2tXAbyBRfDJh0lfg&s=ynGizu7RDzXxq8Mp65SEUYPVyKHTo7kc14uNurHMLRM&e=>
http://www.linkedin.com/in/aalmiray<https://urldefense.proofpoint.com/v2/url?u=http-3A__www.linkedin.com_in_aalmiray&d=DwMFaQ&c=4ZIZThykDLcoWk-GVjSLmy8-1Cr1I4FWIvbLFebwKgY&r=tPJuIuL_GkTEazjQW7vvl7mNWVGXn3yJD5LGBHYYHww&m=hAfmXOD5R2AP8VF69wcL-UqYZEv2tXAbyBRfDJh0lfg&s=jMgNE8ncoIZmIOY8tu6FwSSs_8KA4Vh6-7GpRqDNEd4&e=>
--
What goes up, must come down. Ask any system administrator.
There are 10 types of people in the world: Those who understand binary, and those who don't.
To understand recursion, we must first understand recursion.

Re: Updates on JaCoCo support

Posted by "Milles, Eric (TR Technology & Ops)" <er...@thomsonreuters.com>.
Andreas,


One place to start is everywhere that "AnnotatedNode.setSynthetic(true)" is currently called.  I think this misses the methods added for a property.  But it does cover several AST transforms.  And maybe the transforms that add methods and don't call this method could do so in addition to the modification to add "@Generated".  Maybe the call to setSynthetic could actually add the annotation.  Or you could create a utility method that sets synthetic and adds "@Generated".


________________________________
From: Andres Almiray <aa...@gmail.com>
Sent: Tuesday, August 14, 2018 3:42 PM
To: dev@groovy.apache.org
Subject: Updates on JaCoCo support

Hello everyone,

I've spent a couple of hours with JaCoCo team members at the Hackergarten Bern this evening.
The goal of the session was to get started with an integration test for the @Generated feature
added in Groovy 2.5.0.

You can see the outcome at https://github.com/jacoco/jacoco/pull/733<https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_jacoco_jacoco_pull_733&d=DwMFaQ&c=4ZIZThykDLcoWk-GVjSLmy8-1Cr1I4FWIvbLFebwKgY&r=tPJuIuL_GkTEazjQW7vvl7mNWVGXn3yJD5LGBHYYHww&m=hAfmXOD5R2AP8VF69wcL-UqYZEv2tXAbyBRfDJh0lfg&s=kxj4hAyP-_6cuc12J6CSMgl_xJRqfEjVcnMHfxzID8w&e=>

The good news is that Groovy applies @Generated on constructors added by @Cannonical as well
as methods defined by the GroovyObject interface. The bad news is that the test still fails
because the expectation is that *every* method generated by the compiler that does not map
to a particular source line *should* be annotated with @Generated. The following source

----
// This annotation generates the following
// - a constructor that takes an int as argument
// - a suitable implementation of toString()
// - a suitable implementation of hashCode()
// - a suitable implementation of equals(Object)
// - a public method named canEqual(Object)
// - a getter & setter for the valRead property
@groovy.transform.Canonical
class GroovyDataClassTarget { // assertFullyCovered()

    int valRead // assertNotCovered()

    static void main(String[] args) {
        new GroovyDataClassTarget() // assertFullyCovered()
    }
}
----

Generates bytecode equivalent to (decompiled with IntelliJ)

----
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.jacoco.core.test.validation.groovy.targets;

import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import groovy.transform.EqualsAndHashCode;
import groovy.transform.Generated;
import groovy.transform.ToString;
import org.codehaus.groovy.runtime.InvokerHelper;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
import org.codehaus.groovy.runtime.typehandling.ShortTypeHandling;
import org.codehaus.groovy.util.HashCodeHelper;

@ToString
@EqualsAndHashCode
public class GroovyDataClassTarget implements GroovyObject {
    private int valRead;

    @Generated
    public GroovyDataClassTarget(int valRead) {
        CallSite[] var2 = $getCallSiteArray();
        super();
        MetaClass var3 = this.$getStaticMetaClass();
        this.metaClass = var3;
        this.valRead = DefaultTypeTransformation.intUnbox(valRead);
    }

    public GroovyDataClassTarget() {
        CallSite[] var1 = $getCallSiteArray();
        this(Integer.valueOf(0));
    }

    public static void main(String... args) {
        CallSite[] var1 = $getCallSiteArray();
        var1[0].callConstructor(GroovyDataClassTarget.class);
    }

    public String toString() {
        CallSite[] var1 = $getCallSiteArray();
        Object _result = var1[1].callConstructor(StringBuilder.class);
        Object $toStringFirst = Boolean.TRUE;
        var1[2].call(_result, "org.jacoco.core.test.validation.groovy.targets.GroovyDataClassTarget(");
        if (DefaultTypeTransformation.booleanUnbox($toStringFirst)) {
            Boolean var4 = Boolean.FALSE;
        } else {
            var1[3].call(_result, ", ");
        }

        var1[4].call(_result, var1[5].callStatic(InvokerHelper.class, var1[6].callCurrent(this)));
        var1[7].call(_result, ")");
        return (String)ShortTypeHandling.castToString(var1[8].call(_result));
    }

    public int hashCode() {
        CallSite[] var1 = $getCallSiteArray();
        Object _result = var1[9].callStatic(HashCodeHelper.class);
        if (!DefaultTypeTransformation.booleanUnbox(var1[10].call(var1[11].callCurrent(this), this))) {
            Object var3 = var1[12].callStatic(HashCodeHelper.class, _result, var1[13].callCurrent(this));
            _result = var3;
        }

        return DefaultTypeTransformation.intUnbox(_result);
    }

    public boolean canEqual(Object other) {
        CallSite[] var2 = $getCallSiteArray();
        return other instanceof GroovyDataClassTarget;
    }

    public boolean equals(Object other) {
        CallSite[] var2 = $getCallSiteArray();
        if (ScriptBytecodeAdapter.compareEqual(other, (Object)null)) {
            return false;
        } else if (DefaultTypeTransformation.booleanUnbox(var2[14].callCurrent(this, other))) {
            return true;
        } else if (!(other instanceof GroovyDataClassTarget)) {
            return false;
        } else {
            GroovyDataClassTarget otherTyped = (GroovyDataClassTarget)other;
            if (!DefaultTypeTransformation.booleanUnbox(var2[15].call(otherTyped, this))) {
                return false;
            } else {
                return ScriptBytecodeAdapter.compareEqual(var2[16].callCurrent(this), var2[17].call(otherTyped));
            }
        }
    }

    public int getValRead() {
        return this.valRead;
    }

    public void setValRead(int var1) {
        this.valRead = var1;
    }
}
----

We can appreciate that the methods added by @ToString, @EqualsAndHashcode, and the property getter/setter are not
annotated with @Generated, which will prompt JaCoCo to mark them as not covered. The rationale from the JaCoCo team
is that these methods should be annotated as the compiler is "trusted", only those methods explicitly added to the
source should be covered.

Thus, here comes the call to action and the reason why I wanted to start this conversation in the first place:
 - modify the Groovy compiler to add @Generated on property getters and setters.
 - modify core AST xforms to add @Generated where it makes sense.

Related to the original @Generated issue (as commented by Evgeny at https://github.com/jacoco/jacoco/pull/610<https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_jacoco_jacoco_pull_610&d=DwMFaQ&c=4ZIZThykDLcoWk-GVjSLmy8-1Cr1I4FWIvbLFebwKgY&r=tPJuIuL_GkTEazjQW7vvl7mNWVGXn3yJD5LGBHYYHww&m=hAfmXOD5R2AP8VF69wcL-UqYZEv2tXAbyBRfDJh0lfg&s=44Nu_515VJUtJ8wIVcQJF2ow_axTotUprpo9KwRmQMY&e=>) fields
do not have line numbers, would be good to have them.

What do you think?

Cheers,
Andres

-------------------------------------------
Java Champion; Groovy Enthusiast
JCP EC Associate Seat
http://andresalmiray.com<https://urldefense.proofpoint.com/v2/url?u=http-3A__andresalmiray.com&d=DwMFaQ&c=4ZIZThykDLcoWk-GVjSLmy8-1Cr1I4FWIvbLFebwKgY&r=tPJuIuL_GkTEazjQW7vvl7mNWVGXn3yJD5LGBHYYHww&m=hAfmXOD5R2AP8VF69wcL-UqYZEv2tXAbyBRfDJh0lfg&s=ynGizu7RDzXxq8Mp65SEUYPVyKHTo7kc14uNurHMLRM&e=>
http://www.linkedin.com/in/aalmiray<https://urldefense.proofpoint.com/v2/url?u=http-3A__www.linkedin.com_in_aalmiray&d=DwMFaQ&c=4ZIZThykDLcoWk-GVjSLmy8-1Cr1I4FWIvbLFebwKgY&r=tPJuIuL_GkTEazjQW7vvl7mNWVGXn3yJD5LGBHYYHww&m=hAfmXOD5R2AP8VF69wcL-UqYZEv2tXAbyBRfDJh0lfg&s=jMgNE8ncoIZmIOY8tu6FwSSs_8KA4Vh6-7GpRqDNEd4&e=>
--
What goes up, must come down. Ask any system administrator.
There are 10 types of people in the world: Those who understand binary, and those who don't.
To understand recursion, we must first understand recursion.