coverage: separate options for implicit & empty constructors

This commit is contained in:
Anna Kozlova
2017-09-15 20:56:04 +03:00
parent a1b1a9352d
commit 32dc05bf38
5 changed files with 64 additions and 35 deletions
@@ -654,10 +654,7 @@ public class JavaCoverageEngine extends CoverageEngine {
@Override
public boolean isGeneratedCode(Project project, String qualifiedName, Object lineData) {
if (JavaCoverageOptionsProvider.getInstance(project).ignoreEmptyPrivateConstructors()) {
PsiClass psiClass = ReadAction.compute(() -> ClassUtil.findPsiClassByJVMName(PsiManager.getInstance(project), qualifiedName));
return PackageAnnotator.isGeneratedDefaultConstructor(psiClass, ((LineData)lineData).getMethodSignature());
}
if (JavaCoverageOptionsProvider.getInstance(project).isGeneratedConstructor(qualifiedName, ((LineData)lineData).getMethodSignature())) return true;
return super.isGeneratedCode(project, qualifiedName, lineData);
}
@@ -15,10 +15,10 @@
*/
package com.intellij.coverage;
import com.intellij.openapi.ui.VerticalFlowLayout;
import com.intellij.ui.IdeBorderFactory;
import javax.swing.*;
import java.awt.*;
public class JavaCoverageOptions extends CoverageOptions {
@@ -57,12 +57,14 @@ public class JavaCoverageOptions extends CoverageOptions {
private static class JavaCoverageOptionsEditor {
private JPanel myPanel = new JPanel(new BorderLayout(0, 10));
private JCheckBox myCheckBox = new JCheckBox("Ignore empty private and implicit constructors", true);
private JPanel myPanel = new JPanel(new VerticalFlowLayout());
private JCheckBox myImplicitCheckBox = new JCheckBox("Ignore implicit constructors", true);
private JCheckBox myEmptyCheckBox = new JCheckBox("Ignore empty private constructors of utility classes", true);
public JavaCoverageOptionsEditor() {
myPanel.setBorder(IdeBorderFactory.createTitledBorder("Java coverage"));
myPanel.add(myCheckBox, BorderLayout.NORTH);
myPanel.add(myImplicitCheckBox);
myPanel.add(myEmptyCheckBox);
}
public JPanel getComponent() {
@@ -70,15 +72,18 @@ public class JavaCoverageOptions extends CoverageOptions {
}
public boolean isModified(JavaCoverageOptionsProvider provider) {
return myCheckBox.isSelected() != provider.ignoreEmptyPrivateConstructors();
return myImplicitCheckBox.isSelected() != provider.ignoreImplicitConstructors() ||
myEmptyCheckBox.isSelected() != provider.ignoreEmptyPrivateConstructors();
}
public void apply(JavaCoverageOptionsProvider provider) {
provider.setIgnoreEmptyPrivateConstructors(myCheckBox.isSelected());
provider.setIgnoreImplicitConstructors(myImplicitCheckBox.isSelected());
provider.setIgnoreEmptyPrivateConstructors(myEmptyCheckBox.isSelected());
}
public void reset(JavaCoverageOptionsProvider provider) {
myCheckBox.setSelected(provider.ignoreEmptyPrivateConstructors());
myImplicitCheckBox.setSelected(provider.ignoreImplicitConstructors());
myEmptyCheckBox.setSelected(provider.ignoreEmptyPrivateConstructors());
}
}
}
@@ -15,8 +15,12 @@
*/
package com.intellij.coverage;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.components.*;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiManager;
import com.intellij.psi.util.ClassUtil;
import org.jetbrains.annotations.Nullable;
@State(
@@ -27,11 +31,23 @@ import org.jetbrains.annotations.Nullable;
)
public class JavaCoverageOptionsProvider implements PersistentStateComponent<JavaCoverageOptionsProvider.State> {
private State myState = new State();
private Project myProject;
public JavaCoverageOptionsProvider(Project project) {
myProject = project;
}
public boolean ignoreImplicitConstructors() {
return myState.myIgnoreImplicitConstructors;
}
public static JavaCoverageOptionsProvider getInstance(Project project) {
return ServiceManager.getService(project, JavaCoverageOptionsProvider.class);
}
public void setIgnoreImplicitConstructors(boolean state) {
myState.myIgnoreImplicitConstructors = state;
}
public void setIgnoreEmptyPrivateConstructors(boolean state) {
myState.myIgnoreEmptyPrivateConstructors = state;
@@ -41,6 +57,14 @@ public class JavaCoverageOptionsProvider implements PersistentStateComponent<Jav
return myState.myIgnoreEmptyPrivateConstructors;
}
public boolean isGeneratedConstructor(String qualifiedName, String methodSignature) {
if (myState.myIgnoreImplicitConstructors || myState.myIgnoreEmptyPrivateConstructors) {
PsiClass psiClass = ReadAction.compute(() -> ClassUtil.findPsiClassByJVMName(PsiManager.getInstance(myProject), qualifiedName));
return PackageAnnotator.isGeneratedDefaultConstructor(psiClass, methodSignature, myState.myIgnoreImplicitConstructors, myState.myIgnoreEmptyPrivateConstructors);
}
return false;
}
@Nullable
@Override
public JavaCoverageOptionsProvider.State getState() {
@@ -50,11 +74,13 @@ public class JavaCoverageOptionsProvider implements PersistentStateComponent<Jav
@Override
public void loadState(JavaCoverageOptionsProvider.State state) {
myState.myIgnoreEmptyPrivateConstructors = state.myIgnoreEmptyPrivateConstructors;
myState.myIgnoreImplicitConstructors = state.myIgnoreImplicitConstructors;
}
public static class State {
public boolean myIgnoreEmptyPrivateConstructors = true;
public boolean myIgnoreImplicitConstructors = true;
}
}
@@ -60,13 +60,16 @@ public class PackageAnnotator {
private final PsiManager myManager;
private final CoverageDataManager myCoverageManager;
private final boolean myIgnoreEmptyPrivateConstructors;
private final boolean myIgnoreImplicitConstructor;
public PackageAnnotator(final PsiPackage aPackage) {
myPackage = aPackage;
myProject = myPackage.getProject();
myManager = PsiManager.getInstance(myProject);
myCoverageManager = CoverageDataManager.getInstance(myProject);
myIgnoreEmptyPrivateConstructors = JavaCoverageOptionsProvider.getInstance(myProject).ignoreEmptyPrivateConstructors();
JavaCoverageOptionsProvider optionsProvider = JavaCoverageOptionsProvider.getInstance(myProject);
myIgnoreEmptyPrivateConstructors = optionsProvider.ignoreEmptyPrivateConstructors();
myIgnoreImplicitConstructor = optionsProvider.ignoreImplicitConstructors();
}
public interface Annotator {
@@ -447,6 +450,10 @@ public class PackageAnnotator {
else if (lineData.getStatus() == LineCoverage.PARTIAL) {
toplevelClassCoverageInfo.partiallyCoveredLineCount++;
}
else if ((myIgnoreEmptyPrivateConstructors || myIgnoreImplicitConstructor) &&
isGeneratedDefaultConstructor(psiClass, lineData.getMethodSignature(), myIgnoreImplicitConstructor, myIgnoreEmptyPrivateConstructors)) {
continue;
}
toplevelClassCoverageInfo.totalLineCount++;
packageCoverageInfo.totalLineCount++;
}
@@ -459,7 +466,8 @@ public class PackageAnnotator {
touchedClass = true;
}
if (myIgnoreEmptyPrivateConstructors && isGeneratedDefaultConstructor(psiClass, (String)nameAndSig)) {
if ((myIgnoreEmptyPrivateConstructors || myIgnoreImplicitConstructor) &&
isGeneratedDefaultConstructor(psiClass, (String)nameAndSig, myIgnoreImplicitConstructor, myIgnoreEmptyPrivateConstructors)) {
continue;
}
@@ -508,24 +516,26 @@ public class PackageAnnotator {
* Checks if the method is a default constructor generated by the compiler. Such constructors are not marked as synthetic
* in the bytecode, so we need to look at the PSI to see if the class defines such a constructor.
*/
public static boolean isGeneratedDefaultConstructor(@Nullable final PsiClass aClass, String nameAndSig) {
if (aClass == null) {
return false;
}
public static boolean isGeneratedDefaultConstructor(@Nullable final PsiClass aClass, String nameAndSig,
boolean implicitConstructor, boolean privateEmpty) {
if (aClass == null || !implicitConstructor && !privateEmpty) return false;
if (DEFAULT_CONSTRUCTOR_NAME_SIGNATURE.equals(nameAndSig)) {
return hasGeneratedOrEmptyPrivateConstructor(aClass);
return hasGeneratedOrEmptyPrivateConstructor(aClass, implicitConstructor, privateEmpty);
}
return false;
}
private static boolean hasGeneratedOrEmptyPrivateConstructor(@NotNull final PsiClass aClass) {
private static boolean hasGeneratedOrEmptyPrivateConstructor(@NotNull final PsiClass aClass,
boolean implicitConstructor,
boolean privateEmpty) {
return ReadAction.compute(() -> {
PsiMethod[] constructors = aClass.getConstructors();
if (constructors.length == 1 && constructors[0].hasModifierProperty(PsiModifier.PRIVATE)) {
if (privateEmpty && constructors.length == 1 && constructors[0].hasModifierProperty(PsiModifier.PRIVATE)) {
PsiCodeBlock body = constructors[0].getBody();
return body != null && body.getStatements().length == 0;
return body != null && body.getStatements().length == 0 &&
Arrays.stream(aClass.getMethods()).allMatch(method -> method.hasModifierProperty(PsiModifier.STATIC));
}
return constructors.length == 0;
return implicitConstructor && constructors.length == 0;
});
}
@@ -567,6 +577,7 @@ public class PackageAnnotator {
if (coverageSuite == null) return false;
return SourceLineCounterUtil
.collectNonCoveredClassInfo(classCoverageInfo, packageCoverageInfo, content, coverageSuite.isTracingEnabled(),
myIgnoreEmptyPrivateConstructors ? description -> !isGeneratedDefaultConstructor(psiClass, description) : Condition.TRUE);
myIgnoreEmptyPrivateConstructors || myIgnoreImplicitConstructor
? description -> !isGeneratedDefaultConstructor(psiClass, description, myIgnoreImplicitConstructor, myIgnoreEmptyPrivateConstructors) : Condition.TRUE);
}
}
@@ -15,12 +15,8 @@
*/
package com.intellij.coverage;
import com.intellij.openapi.application.ReadAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiManager;
import com.intellij.psi.util.ClassUtil;
import com.intellij.rt.coverage.instrumentation.SourceLineCounter;
import com.intellij.util.containers.HashSet;
import gnu.trove.TIntObjectHashMap;
@@ -68,13 +64,7 @@ public class SourceLineCounterUtil {
reader.accept(collector, 0);
String qualifiedName = reader.getClassName();
boolean ignoreEmptyPrivateConstructors = JavaCoverageOptionsProvider.getInstance(project).ignoreEmptyPrivateConstructors();
PsiClass psiClass = ignoreEmptyPrivateConstructors
? ReadAction.compute(() -> ClassUtil.findPsiClassByJVMName(PsiManager.getInstance(project), qualifiedName))
: null;
Condition<String> includeDescriptionCondition = ignoreEmptyPrivateConstructors
? description -> !PackageAnnotator.isGeneratedDefaultConstructor(psiClass, description)
: Condition.TRUE;
Condition<String> includeDescriptionCondition = description -> !JavaCoverageOptionsProvider.getInstance(project).isGeneratedConstructor(qualifiedName, description);
TIntObjectHashMap<?> lines = collector.getSourceLines();
lines.forEachEntry((line, description) -> {
if (includeDescriptionCondition.value((String)description)) {