Add filter for methods annotated with @lombok.Generated (#513)

Based on initial contribution by RĂ¼diger zu Dohna.
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/LombokGeneratedFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/LombokGeneratedFilterTest.java
new file mode 100644
index 0000000..a1fdcae
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/LombokGeneratedFilterTest.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.internal.analysis.filter;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.Arrays;
+
+import org.jacoco.core.internal.instr.InstrSupport;
+import org.junit.Test;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.AnnotationNode;
+import org.objectweb.asm.tree.MethodNode;
+
+public class LombokGeneratedFilterTest implements IFilterOutput {
+
+	private final IFilter filter = new LombokGeneratedFilter();
+
+	private AbstractInsnNode fromInclusive;
+	private AbstractInsnNode toInclusive;
+
+	@Test
+	public void testNoAnnotations() {
+		final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
+				"hashCode", "()I", null, null);
+
+		m.visitInsn(Opcodes.ICONST_0);
+		m.visitInsn(Opcodes.IRETURN);
+
+		filter.filter(m, this);
+
+		assertNull(fromInclusive);
+		assertNull(toInclusive);
+	}
+
+	@Test
+	public void testOtherAnnotation() {
+		final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
+				"hashCode", "()I", null, null);
+		m.visitAnnotation("Lother/Annotation;", false);
+
+		m.visitInsn(Opcodes.ICONST_0);
+		m.visitInsn(Opcodes.IRETURN);
+
+		filter.filter(m, this);
+
+		assertNull(fromInclusive);
+		assertNull(toInclusive);
+	}
+
+	@Test
+	public void testLombokGeneratedAnnotation() {
+		final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
+				"hashCode", "()I", null, null);
+		m.visitAnnotation("Llombok/Generated;", false);
+
+		m.visitInsn(Opcodes.ICONST_0);
+		m.visitInsn(Opcodes.IRETURN);
+
+		filter.filter(m, this);
+
+		assertEquals(m.instructions.getFirst(), fromInclusive);
+		assertEquals(m.instructions.getLast(), toInclusive);
+	}
+
+	public void ignore(final AbstractInsnNode fromInclusive,
+			final AbstractInsnNode toInclusive) {
+		assertNull(this.fromInclusive);
+		this.fromInclusive = fromInclusive;
+		this.toInclusive = toInclusive;
+	}
+
+}
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodAnalyzer.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodAnalyzer.java
index a912e33..e5dde70 100644
--- a/org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodAnalyzer.java
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/MethodAnalyzer.java
@@ -21,6 +21,7 @@
 import org.jacoco.core.analysis.ISourceNode;
 import org.jacoco.core.internal.analysis.filter.IFilter;
 import org.jacoco.core.internal.analysis.filter.IFilterOutput;
+import org.jacoco.core.internal.analysis.filter.LombokGeneratedFilter;
 import org.jacoco.core.internal.analysis.filter.SynchronizedFilter;
 import org.jacoco.core.internal.analysis.filter.SyntheticFilter;
 import org.jacoco.core.internal.flow.IFrame;
@@ -42,7 +43,8 @@
 		implements IFilterOutput {
 
 	private static final IFilter[] FILTERS = new IFilter[] {
-			new SyntheticFilter(), new SynchronizedFilter() };
+			new SyntheticFilter(), new SynchronizedFilter(),
+			new LombokGeneratedFilter() };
 
 	private final boolean[] probes;
 
@@ -328,8 +330,9 @@
 			final int covered = i.getCoveredBranches();
 			final ICounter instrCounter = covered == 0 ? CounterImpl.COUNTER_1_0
 					: CounterImpl.COUNTER_0_1;
-			final ICounter branchCounter = total > 1 ? CounterImpl.getInstance(
-					total - covered, covered) : CounterImpl.COUNTER_0_0;
+			final ICounter branchCounter = total > 1
+					? CounterImpl.getInstance(total - covered, covered)
+					: CounterImpl.COUNTER_0_0;
 			coverage.increment(instrCounter, branchCounter, i.getLine());
 		}
 		coverage.incrementMethodCounter();
diff --git a/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/LombokGeneratedFilter.java b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/LombokGeneratedFilter.java
new file mode 100644
index 0000000..9f23ecd
--- /dev/null
+++ b/org.jacoco.core/src/org/jacoco/core/internal/analysis/filter/LombokGeneratedFilter.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *    Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.core.internal.analysis.filter;
+
+import java.util.List;
+
+import org.objectweb.asm.tree.AnnotationNode;
+import org.objectweb.asm.tree.MethodNode;
+
+/**
+ * Filters methods annotated with <code>@lombok.Generated</code>.
+ */
+public class LombokGeneratedFilter implements IFilter {
+
+	public void filter(final MethodNode methodNode,
+			final IFilterOutput output) {
+		if (hasLombokGeneratedAnnotation(methodNode)) {
+			output.ignore(methodNode.instructions.getFirst(),
+					methodNode.instructions.getLast());
+		}
+	}
+
+	private boolean hasLombokGeneratedAnnotation(final MethodNode methodNode) {
+		final List<AnnotationNode> runtimeInvisibleAnnotations = methodNode.invisibleAnnotations;
+		if (runtimeInvisibleAnnotations != null) {
+			for (final AnnotationNode annotation : runtimeInvisibleAnnotations) {
+				if ("Llombok/Generated;".equals(annotation.desc)) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+
+}
diff --git a/org.jacoco.doc/docroot/doc/changes.html b/org.jacoco.doc/docroot/doc/changes.html
index c73b155..43e42aa 100644
--- a/org.jacoco.doc/docroot/doc/changes.html
+++ b/org.jacoco.doc/docroot/doc/changes.html
@@ -28,6 +28,9 @@
   <li>Exclude from a report a part of bytecode that compiler generates for a
       synchronized statement
       (GitHub <a href="https://github.com/jacoco/jacoco/issues/501">#501</a>).</li>
+  <li>Exclude from a report methods which are annotated with <code>@lombok.Generated</code>.
+      Initial analysis and contribution by Rüdiger zu Dohna.
+      (GitHub <a href="https://github.com/jacoco/jacoco/issues/513">#513</a>).</li>
   <li>Maven aggregated reports will now also include modules of runtime dependencies
       (GitHub <a href="https://github.com/jacoco/jacoco/issues/498">#498</a>).</li>
 </ul>