Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.idea/
out/
out/
*.jar
12 changes: 7 additions & 5 deletions META-INF/plugin.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<idea-plugin version="2">
<idea-plugin>
<id>de.u-mass.idea.copyConstructor</id>
<name>GenerateCopyConstructor</name>
<version>1.2</version>
<version>1.3</version>
<vendor email="me@jkovacs.de" url="http://www.jkovacs.de">Johann Kovacs</vendor>

<description><![CDATA[
Expand All @@ -12,9 +12,11 @@

<p>Also adds a number of inspections that generate warnings if a copy constructor might be faulty
(e.g. not all fields copied or superclass constructor not invoked).</p>
]]></description>
]]></description>

<change-notes><![CDATA[]]></change-notes>
<change-notes><![CDATA[
Dealing also with complex field initializations, like conditional expression for example
]]></change-notes>

<!-- please see http://confluence.jetbrains.net/display/IDEADEV/Build+Number+Ranges for description -->
<idea-version since-build="123.72"/>
Expand All @@ -32,7 +34,7 @@
<actions>
<!-- Add your actions here -->
<action id="GenerateCopyConstructor" class="de.umass.idea.copyConstructor.GenerateCopyConstructorAction" text="Copy Constructor"
description="Generates a copy constructor">
description="Generates a copy constructor">
<add-to-group group-id="JavaGenerateGroup1" anchor="after" relative-to-action="GenerateConstructor"/>
</action>
</actions>
Expand Down
4 changes: 2 additions & 2 deletions src/de/umass/idea/copyConstructor/ConstructorUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public static PsiMethod findCopyConstructor(@Nullable PsiClass psiClass) {
return null;
}

public static boolean hasCopyConstructor(@Nullable PsiClass psiClass) {
static boolean hasCopyConstructor(@Nullable PsiClass psiClass) {
return findCopyConstructor(psiClass) != null;
}

Expand Down Expand Up @@ -72,7 +72,7 @@ public static PsiMethod findConstructorCall(PsiMethod constructor) {
* This excludes, for example, static fields.
*/
public static List<PsiField> getAllCopyableFields(PsiClass psiClass) {
List<PsiField> copyableFields = new ArrayList<PsiField>();
List<PsiField> copyableFields = new ArrayList<>();
PsiField[] fields = psiClass.getFields();
for (PsiField field : fields) {
if (isCopyableField(field)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ protected ClassMember[] chooseMembers(ClassMember[] members, boolean allowEmptyS
@Override
protected List<? extends GenerationInfo> generateMemberPrototypes(PsiClass aClass, ClassMember[] members) throws IncorrectOperationException {
PsiMethod copyConstructor = generateCopyConstructor(aClass, members);
return Collections.singletonList(new PsiGenerationInfo<PsiMethod>(copyConstructor));
return Collections.singletonList(new PsiGenerationInfo<>(copyConstructor));
}

@Override
protected GenerationInfo[] generateMemberPrototypes(PsiClass aClass, ClassMember originalMember) throws IncorrectOperationException {
return null;
return new GenerationInfo[0];
}

private PsiMethod generateCopyConstructor(PsiClass psiClass, ClassMember[] copyableFields) {
Expand All @@ -72,8 +72,7 @@ private PsiMethod generateCopyConstructor(PsiClass psiClass, ClassMember[] copya
code.append("}");

PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(psiClass.getProject());
PsiMethod constructor = elementFactory.createMethodFromText(code.toString(), psiClass);
return constructor;
return elementFactory.createMethodFromText(code.toString(), psiClass);
}

private ClassMember[] toMembers(List<PsiField> allCopyableFields) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,7 @@
import com.intellij.codeInspection.LocalInspectionToolSession;
import com.intellij.codeInspection.ProblemHighlightType;
import com.intellij.codeInspection.ProblemsHolder;
import com.intellij.psi.JavaElementVisitor;
import com.intellij.psi.JavaRecursiveElementVisitor;
import com.intellij.psi.PsiAssignmentExpression;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiIdentifier;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiParameter;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiReferenceExpression;
import com.intellij.psi.PsiThisExpression;
import com.intellij.psi.PsiVariable;

import com.intellij.psi.*;
import de.umass.idea.copyConstructor.ConstructorUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -61,7 +46,7 @@ public void visitMethod(PsiMethod method) {
}

private boolean constructorAssignsAllFields(final PsiMethod constructor, List<PsiField> allFields) {
final Set<PsiField> unassignedFields = new HashSet<PsiField>(allFields);
final Set<PsiField> unassignedFields = new HashSet<>(allFields);
final PsiParameter copyParameter = constructor.getParameterList().getParameters()[0];
constructor.accept(new JavaRecursiveElementVisitor() {
@Override
Expand All @@ -71,7 +56,7 @@ public void visitAssignmentExpression(PsiAssignmentExpression expression) {
PsiReference assignee = left.getReference();
if (assignee != null) {
PsiElement leftReference = assignee.resolve();
if (leftReference != null && leftReference instanceof PsiField) {
if (leftReference instanceof PsiField) {
PsiField referencedField = (PsiField) leftReference;
if (isReferenceToFieldInInstance(left, referencedField, null)) {
if (isReferenceToFieldInInstance(right, referencedField, copyParameter)) {
Expand All @@ -91,10 +76,60 @@ public void visitAssignmentExpression(PsiAssignmentExpression expression) {
}

private boolean isReferenceToFieldInInstance(@Nullable PsiExpression expression, @NotNull PsiField field, @Nullable PsiVariable instance) {
if (expression != null && expression instanceof PsiReferenceExpression) {
boolean isReference = false;
if (expression instanceof PsiReferenceExpression) {
PsiReferenceExpression referenceExpression = (PsiReferenceExpression) expression;
PsiExpression qualifierExpression = referenceExpression.getQualifierExpression();
return referenceExpression.isReferenceTo(field) && qualifierReferencesVariable(qualifierExpression, instance);
isReference = referenceExpression.isReferenceTo(field) && qualifierReferencesVariable(qualifierExpression, instance);
if (!isReference && qualifierExpression instanceof PsiReferenceExpression) {
isReference = isReferenceToFieldInInstance(qualifierExpression, field, instance);
}
} else if (expression instanceof PsiCallExpression) {
PsiCallExpression callExpression = (PsiCallExpression) expression;
isReference = isCallExpressionReferencingToFieldInstance(callExpression, field, instance);
} else if (expression instanceof PsiPolyadicExpression) {
PsiPolyadicExpression polyadicExpression = (PsiPolyadicExpression) expression;
isReference = isPolyadicExpressionReferencingToFieldInstance(polyadicExpression, field, instance);
} else if (expression instanceof PsiConditionalExpression) {
PsiConditionalExpression conditionalExpression = (PsiConditionalExpression) expression;
isReference = isConditionalExpressionReferencingToFieldInstance(conditionalExpression, field, instance);
} else if (expression instanceof PsiParenthesizedExpression) {
PsiParenthesizedExpression parenthesizedExpression = (PsiParenthesizedExpression) expression;
isReference = isReferenceToFieldInInstance(parenthesizedExpression.getExpression(), field, instance);
}
return isReference;
}

private boolean isConditionalExpressionReferencingToFieldInstance(
final PsiConditionalExpression conditionalExpression, @NotNull final PsiField field,
@Nullable final PsiVariable instance) {
return isReferenceToFieldInInstance(conditionalExpression.getCondition(), field, instance)
&& (isReferenceToFieldInInstance(conditionalExpression.getThenExpression(), field, instance)
|| isReferenceToFieldInInstance(conditionalExpression.getElseExpression(), field, instance));
}

private boolean isPolyadicExpressionReferencingToFieldInstance(
final PsiPolyadicExpression polyadicExpression, @NotNull final PsiField field,
@Nullable final PsiVariable instance) {
for(PsiExpression operandExpression : polyadicExpression.getOperands()) {
if (isReferenceToFieldInInstance(operandExpression, field, instance)) {
return true;
}
}
return false;
}

private boolean isCallExpressionReferencingToFieldInstance(final PsiCallExpression callExpression,
@NotNull final PsiField field, @Nullable final PsiVariable instance) {
if (callExpression instanceof PsiMethodCallExpression) {
PsiMethodCallExpression methodExpression = (PsiMethodCallExpression) callExpression;
return isReferenceToFieldInInstance(methodExpression.getMethodExpression(), field, instance);
} else if (callExpression.getArgumentList() != null) {
for(PsiExpression argumentExpression : callExpression.getArgumentList().getExpressions()) {
if (isReferenceToFieldInInstance(argumentExpression, field, instance)) {
return true;
}
}
}
return false;
}
Expand All @@ -105,7 +140,7 @@ private boolean qualifierReferencesVariable(@Nullable PsiExpression qualifier, @

if (qualifier != null) {
PsiReference reference = qualifier.getReference();
return reference != null && reference.isReferenceTo(instance);
return reference != null && instance != null && reference.isReferenceTo(instance);
}

return false;
Expand Down
3 changes: 1 addition & 2 deletions src/inspectionDescriptions/IncompleteCopyConstructor.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<html>
<body>
Reports a warinng if not all fields are assigned correctly in a copy constructor.
Reports a warning if not all fields are assigned correctly in a copy constructor.
<!-- tooltip end -->

</body>
</html>