diff --git a/src/com/goide/completion/GoStructLiteralCompletion.java b/src/com/goide/completion/GoStructLiteralCompletion.java index 6fd6b483f1..2f622cb3cc 100644 --- a/src/com/goide/completion/GoStructLiteralCompletion.java +++ b/src/com/goide/completion/GoStructLiteralCompletion.java @@ -19,9 +19,7 @@ import com.goide.psi.*; import com.goide.psi.impl.GoPsiImplUtil; import com.intellij.psi.PsiElement; -import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -61,8 +59,8 @@ enum Variants { @NotNull static Variants allowedVariants(@Nullable GoReferenceExpression structFieldReference) { - GoValue value = parent(structFieldReference, GoValue.class); - GoElement element = parent(value, GoElement.class); + GoValue value = GoPsiTreeUtil.getDirectParentOfType(structFieldReference, GoValue.class); + GoElement element = GoPsiTreeUtil.getDirectParentOfType(value, GoElement.class); if (element != null && element.getKey() != null) { return Variants.NONE; } @@ -75,7 +73,7 @@ static Variants allowedVariants(@Nullable GoReferenceExpression structFieldRefer boolean hasValueInitializers = false; boolean hasFieldValueInitializers = false; - GoLiteralValue literalValue = parent(element, GoLiteralValue.class); + GoLiteralValue literalValue = GoPsiTreeUtil.getDirectParentOfType(element, GoLiteralValue.class); List fieldInitializers = literalValue != null ? literalValue.getElementList() : Collections.emptyList(); for (GoElement initializer : fieldInitializers) { if (initializer == element) { @@ -105,9 +103,4 @@ static Set alreadyAssignedFields(@Nullable GoLiteralValue literal) { return identifier != null ? identifier.getText() : null; }); } - - @Contract("null,_->null") - private static T parent(@Nullable PsiElement of, @NotNull Class parentClass) { - return ObjectUtils.tryCast(of != null ? of.getParent() : null, parentClass); - } } diff --git a/src/com/goide/inspections/GoStructInitializationInspection.java b/src/com/goide/inspections/GoStructInitializationInspection.java index 67ffd12833..083294bc76 100644 --- a/src/com/goide/inspections/GoStructInitializationInspection.java +++ b/src/com/goide/inspections/GoStructInitializationInspection.java @@ -22,25 +22,32 @@ import com.goide.util.GoUtil; import com.intellij.codeInspection.*; import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel; -import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.ObjectUtils; import org.jdom.Element; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.List; +import static com.intellij.util.containers.ContainerUtil.*; +import static java.lang.Math.min; +import static java.util.stream.Collectors.toList; +import static java.util.stream.IntStream.range; + public class GoStructInitializationInspection extends GoInspectionBase { - public static final String REPLACE_WITH_NAMED_STRUCT_FIELD_FIX_NAME = "Replace with named struct field"; + public static final String REPLACE_WITH_NAMED_STRUCT_FIELD_FIX_NAME = "Replace with named struct fields"; + private static final GoReplaceWithNamedStructFieldQuickFix QUICK_FIX = new GoReplaceWithNamedStructFieldQuickFix(); public boolean reportLocalStructs; /** - * @deprecated use reportLocalStructs + * @deprecated use {@link #reportLocalStructs} */ @SuppressWarnings("WeakerAccess") public Boolean reportImportedStructs; @@ -49,67 +56,117 @@ public class GoStructInitializationInspection extends GoInspectionBase { protected GoVisitor buildGoVisitor(@NotNull ProblemsHolder holder, @NotNull LocalInspectionToolSession session) { return new GoVisitor() { @Override - public void visitLiteralValue(@NotNull GoLiteralValue o) { - if (PsiTreeUtil.getParentOfType(o, GoReturnStatement.class, GoShortVarDeclaration.class, GoAssignmentStatement.class) == null) { - return; - } - PsiElement parent = o.getParent(); - GoType refType = GoPsiImplUtil.getLiteralType(parent, false); - if (refType instanceof GoStructType) { - processStructType(holder, o, (GoStructType)refType); + public void visitLiteralValue(@NotNull GoLiteralValue literalValue) { + GoStructType structType = getLiteralStructType(literalValue); + if (structType == null || !isStructImportedOrLocalAllowed(structType, literalValue)) return; + + List elements = literalValue.getElementList(); + List definitions = getFieldDefinitions(structType); + + if (!areElementsKeysMatchesDefinitions(elements, definitions)) return; + registerProblemsForElementsWithoutKeys(elements, definitions.size()); + } + + private void registerProblemsForElementsWithoutKeys(@NotNull List elements, int definitionsCount) { + for (int i = 0; i < min(elements.size(), definitionsCount); i++) { + if (elements.get(i).getKey() != null) continue; + holder.registerProblem(elements.get(i), "Unnamed field initialization", ProblemHighlightType.WEAK_WARNING, QUICK_FIX); } } }; } - @Override - public JComponent createOptionsPanel() { - return new SingleCheckboxOptionsPanel("Report for local type definitions as well", this, "reportLocalStructs"); - } + @Contract("null -> null") + private static GoStructType getLiteralStructType(@Nullable GoLiteralValue literalValue) { + GoCompositeLit parentLit = GoPsiTreeUtil.getDirectParentOfType(literalValue, GoCompositeLit.class); + if (parentLit != null && !isStructLit(parentLit)) return null; - private void processStructType(@NotNull ProblemsHolder holder, @NotNull GoLiteralValue element, @NotNull GoStructType structType) { - if (reportLocalStructs || !GoUtil.inSamePackage(structType.getContainingFile(), element.getContainingFile())) { - processLiteralValue(holder, element, structType.getFieldDeclarationList()); - } + GoStructType litType = ObjectUtils.tryCast(GoPsiImplUtil.getLiteralType(literalValue, parentLit == null), GoStructType.class); + GoNamedElement definition = getFieldDefinition(GoPsiTreeUtil.getDirectParentOfType(literalValue, GoValue.class)); + return definition != null && litType != null ? getUnderlyingStructType(definition.getGoType(null)) : litType; } - private static void processLiteralValue(@NotNull ProblemsHolder holder, - @NotNull GoLiteralValue o, - @NotNull List fields) { - List vals = o.getElementList(); - for (int elemId = 0; elemId < vals.size(); elemId++) { - ProgressManager.checkCanceled(); - GoElement element = vals.get(elemId); - if (element.getKey() == null && elemId < fields.size()) { - String structFieldName = getFieldName(fields.get(elemId)); - LocalQuickFix[] fixes = structFieldName != null ? new LocalQuickFix[]{new GoReplaceWithNamedStructFieldQuickFix(structFieldName)} - : LocalQuickFix.EMPTY_ARRAY; - holder.registerProblem(element, "Unnamed field initialization", ProblemHighlightType.GENERIC_ERROR_OR_WARNING, fixes); - } - } + @Nullable + private static GoNamedElement getFieldDefinition(@Nullable GoValue value) { + GoKey key = PsiTreeUtil.getPrevSiblingOfType(value, GoKey.class); + GoFieldName fieldName = key != null ? key.getFieldName() : null; + PsiElement field = fieldName != null ? fieldName.resolve() : null; + return field instanceof GoAnonymousFieldDefinition || field instanceof GoFieldDefinition ? ObjectUtils + .tryCast(field, GoNamedElement.class) : null; } @Nullable - private static String getFieldName(@NotNull GoFieldDeclaration declaration) { - List list = declaration.getFieldDefinitionList(); - GoFieldDefinition fieldDefinition = ContainerUtil.getFirstItem(list); - return fieldDefinition != null ? fieldDefinition.getIdentifier().getText() : null; + @Contract("null -> null") + private static GoStructType getUnderlyingStructType(@Nullable GoType type) { + return type != null ? ObjectUtils.tryCast(type.getUnderlyingType(), GoStructType.class) : null; + } + + private static boolean isStructLit(@NotNull GoCompositeLit parentLit) { + return getUnderlyingStructType(parentLit.getGoType(null)) != null; + } + + private boolean isStructImportedOrLocalAllowed(@NotNull GoStructType structType, @NotNull GoLiteralValue literalValue) { + return reportLocalStructs || !GoUtil.inSamePackage(structType.getContainingFile(), literalValue.getContainingFile()); + } + + private static boolean areElementsKeysMatchesDefinitions(@NotNull List elements, + @NotNull List fieldDefinitions) { + return range(0, elements.size()) + .allMatch(i -> isNullOrNamesEqual(elements.get(i).getKey(), GoPsiImplUtil.getByIndex(fieldDefinitions, i))); + } + + + @Contract("null, _ -> true") + private static boolean isNullOrNamesEqual(@Nullable GoKey key, @Nullable GoNamedElement elementToCompare) { + return key == null || elementToCompare != null && Comparing.equal(key.getText(), elementToCompare.getName()); + } + + @NotNull + private static List getFieldDefinitions(@Nullable GoStructType type) { + return type != null ? type.getFieldDeclarationList().stream() + .flatMap(declaration -> getFieldDefinitions(declaration).stream()) + .collect(toList()) : emptyList(); + } + + @NotNull + private static List getFieldDefinitions(@NotNull GoFieldDeclaration declaration) { + GoNamedElement anonymousDefinition = ObjectUtils.tryCast(declaration.getAnonymousFieldDefinition(), GoNamedElement.class); + return anonymousDefinition != null + ? list(anonymousDefinition) + : map(declaration.getFieldDefinitionList(), definition -> ObjectUtils.tryCast(definition, GoNamedElement.class)); + } + + @Override + public JComponent createOptionsPanel() { + return new SingleCheckboxOptionsPanel("Report for local type definitions as well", this, "reportLocalStructs"); } private static class GoReplaceWithNamedStructFieldQuickFix extends LocalQuickFixBase { - private String myStructField; - public GoReplaceWithNamedStructFieldQuickFix(@NotNull String structField) { + public GoReplaceWithNamedStructFieldQuickFix() { super(REPLACE_WITH_NAMED_STRUCT_FIELD_FIX_NAME); - myStructField = structField; } @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { - PsiElement startElement = descriptor.getStartElement(); - if (startElement instanceof GoElement) { - startElement.replace(GoElementFactory.createLiteralValueElement(project, myStructField, startElement.getText())); - } + PsiElement element = ObjectUtils.tryCast(descriptor.getStartElement(), GoElement.class); + GoLiteralValue literal = element != null && element.isValid() ? PsiTreeUtil.getParentOfType(element, GoLiteralValue.class) : null; + + List elements = literal != null ? literal.getElementList() : emptyList(); + List fieldDefinitionNames = getFieldDefinitions(getLiteralStructType(literal)); + if (!areElementsKeysMatchesDefinitions(elements, fieldDefinitionNames)) return; + addKeysToElements(project, elements, fieldDefinitionNames); + } + } + + private static void addKeysToElements(@NotNull Project project, + @NotNull List elements, + @NotNull List fieldDefinitions) { + for (int i = 0; i < min(elements.size(), fieldDefinitions.size()); i++) { + GoElement element = elements.get(i); + String fieldDefinitionName = fieldDefinitions.get(i).getName(); + GoValue value = fieldDefinitionName != null && element.getKey() == null ? element.getValue() : null; + if (value != null) element.replace(GoElementFactory.createLiteralValueElement(project, fieldDefinitionName, value.getText())); } } diff --git a/src/com/goide/psi/GoPsiTreeUtil.java b/src/com/goide/psi/GoPsiTreeUtil.java index 04cb2176d9..5352aaf557 100644 --- a/src/com/goide/psi/GoPsiTreeUtil.java +++ b/src/com/goide/psi/GoPsiTreeUtil.java @@ -26,8 +26,10 @@ import com.intellij.psi.stubs.StubElement; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtilCore; +import com.intellij.util.ObjectUtils; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -155,5 +157,10 @@ private static PsiElement findNotWhiteSpaceElementAtOffset(@NotNull GoFile file, } return element; } + + @Contract("null,_->null") + public static T getDirectParentOfType(@Nullable PsiElement element, @NotNull Class aClass) { + return element != null ? ObjectUtils.tryCast(element.getParent(), aClass) : null; + } } \ No newline at end of file diff --git a/src/com/goide/psi/impl/GoElementFactory.java b/src/com/goide/psi/impl/GoElementFactory.java index a9c4939ad2..4e32ab6afb 100644 --- a/src/com/goide/psi/impl/GoElementFactory.java +++ b/src/com/goide/psi/impl/GoElementFactory.java @@ -256,7 +256,7 @@ public static GoType createType(@NotNull Project project, @NotNull String text) return PsiTreeUtil.findChildOfType(file, GoType.class); } - public static PsiElement createLiteralValueElement(@NotNull Project project, @NotNull String key, @NotNull String value) { + public static GoElement createLiteralValueElement(@NotNull Project project, @NotNull String key, @NotNull String value) { GoFile file = createFileFromText(project, "package a; var _ = struct { a string } { " + key + ": " + value + " }"); return PsiTreeUtil.findChildOfType(file, GoElement.class); } diff --git a/src/com/goide/psi/impl/GoPsiImplUtil.java b/src/com/goide/psi/impl/GoPsiImplUtil.java index 9a64f17293..e6951eebf1 100644 --- a/src/com/goide/psi/impl/GoPsiImplUtil.java +++ b/src/com/goide/psi/impl/GoPsiImplUtil.java @@ -569,11 +569,12 @@ public static GoType getLiteralType(@Nullable PsiElement context, boolean consid @Nullable public static GoValue getParentGoValue(@NotNull PsiElement element) { PsiElement place = element; - while ((place = PsiTreeUtil.getParentOfType(place, GoLiteralValue.class)) != null) { + do { if (place.getParent() instanceof GoValue) { return (GoValue)place.getParent(); } } + while ((place = PsiTreeUtil.getParentOfType(place, GoLiteralValue.class)) != null); return null; } @@ -1468,7 +1469,8 @@ public static GoExpression getValue(@NotNull GoConstDefinition definition) { return getByIndex(((GoConstSpec)parent).getExpressionList(), index); } - private static T getByIndex(@NotNull List list, int index) { + @Nullable + public static T getByIndex(@NotNull List list, int index) { return 0 <= index && index < list.size() ? list.get(index) : null; } diff --git a/testData/inspections/struct-initialization/anonField-after.go b/testData/inspections/struct-initialization/anonField-after.go new file mode 100644 index 0000000000..b601fee168 --- /dev/null +++ b/testData/inspections/struct-initialization/anonField-after.go @@ -0,0 +1,11 @@ +package foo + +type S struct { + X string + string + Y int +} +func main() { + var s S + s = S{X: "X", string: "a", Y: 1} +} diff --git a/testData/inspections/struct-initialization/anonField.go b/testData/inspections/struct-initialization/anonField.go new file mode 100644 index 0000000000..f2cad49a97 --- /dev/null +++ b/testData/inspections/struct-initialization/anonField.go @@ -0,0 +1,11 @@ +package foo + +type S struct { + X string + string + Y int +} +func main() { + var s S + s = S{"X", "a", Y: 1} +} diff --git a/testData/inspections/struct-initialization/exceedElementsCountOnlyUnnamed-after.go b/testData/inspections/struct-initialization/exceedElementsCountOnlyUnnamed-after.go new file mode 100644 index 0000000000..95980557c1 --- /dev/null +++ b/testData/inspections/struct-initialization/exceedElementsCountOnlyUnnamed-after.go @@ -0,0 +1,8 @@ +package foo + +type S struct { + X, Y int +} +func main() { + s := S{X: 1, Y: 0, 2} +} diff --git a/testData/inspections/struct-initialization/exceedElementsCountOnlyUnnamed.go b/testData/inspections/struct-initialization/exceedElementsCountOnlyUnnamed.go new file mode 100644 index 0000000000..9ddbdde0c1 --- /dev/null +++ b/testData/inspections/struct-initialization/exceedElementsCountOnlyUnnamed.go @@ -0,0 +1,8 @@ +package foo + +type S struct { + X, Y int +} +func main() { + s := S{1, 0, 2} +} diff --git a/testData/inspections/struct-initialization/exceedElementsCountWithNamed.go b/testData/inspections/struct-initialization/exceedElementsCountWithNamed.go new file mode 100644 index 0000000000..f5d31d432f --- /dev/null +++ b/testData/inspections/struct-initialization/exceedElementsCountWithNamed.go @@ -0,0 +1,9 @@ +package foo + +type S struct { + X, Y int +} +func main() { + s := S{1, 0, X: 2} + +} diff --git a/testData/inspections/struct-initialization/innerAnonLiteral-after.go b/testData/inspections/struct-initialization/innerAnonLiteral-after.go new file mode 100644 index 0000000000..fad6615e96 --- /dev/null +++ b/testData/inspections/struct-initialization/innerAnonLiteral-after.go @@ -0,0 +1,9 @@ +package foo + +type S struct { + t int +} + +func main() { + var _ = []S{ {t: 1} } +} diff --git a/testData/inspections/struct-initialization/innerAnonLiteral.go b/testData/inspections/struct-initialization/innerAnonLiteral.go new file mode 100644 index 0000000000..99446529cd --- /dev/null +++ b/testData/inspections/struct-initialization/innerAnonLiteral.go @@ -0,0 +1,9 @@ +package foo + +type S struct { + t int +} + +func main() { + var _ = []S{ {1} } +} diff --git a/testData/inspections/struct-initialization/innerAnonStruct-after.go b/testData/inspections/struct-initialization/innerAnonStruct-after.go new file mode 100644 index 0000000000..4ff9967fc1 --- /dev/null +++ b/testData/inspections/struct-initialization/innerAnonStruct-after.go @@ -0,0 +1,16 @@ +package foo + +func main() { + type B struct { + Y int + } + + type S struct { + X int + B + Z int + } + + s := S{X: 1, B: B{Y: 2}, Z: 3} + print(s.B.Y) +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/innerAnonStruct.go b/testData/inspections/struct-initialization/innerAnonStruct.go new file mode 100644 index 0000000000..9fb0e47791 --- /dev/null +++ b/testData/inspections/struct-initialization/innerAnonStruct.go @@ -0,0 +1,16 @@ +package foo + +func main() { + type B struct { + Y int + } + + type S struct { + X int + B + Z int + } + + s := S{1, B{Y: 2}, 3} + print(s.B.Y) +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/innerLiteral-after.go b/testData/inspections/struct-initialization/innerLiteral-after.go new file mode 100644 index 0000000000..16a807f550 --- /dev/null +++ b/testData/inspections/struct-initialization/innerLiteral-after.go @@ -0,0 +1,13 @@ +package foo + +type S struct { + r, t int +} + +func main() { + var _ = [][]S{ + { + {r: 1, t: 1}, + }, + } +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/innerLiteral.go b/testData/inspections/struct-initialization/innerLiteral.go new file mode 100644 index 0000000000..6dfd9d34ae --- /dev/null +++ b/testData/inspections/struct-initialization/innerLiteral.go @@ -0,0 +1,13 @@ +package foo + +type S struct { + r, t int +} + +func main() { + var _ = [][]S{ + { + {1, 1}, + }, + } +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/innerLiteralFieldWithKey-after.go b/testData/inspections/struct-initialization/innerLiteralFieldWithKey-after.go new file mode 100644 index 0000000000..15eb44ca9e --- /dev/null +++ b/testData/inspections/struct-initialization/innerLiteralFieldWithKey-after.go @@ -0,0 +1,13 @@ +package foo + +type S struct { + t int +} + +type B struct{ + s S +} + +func main() { + var _ = []B{ s: {s: 1}} +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/innerLiteralFieldWithKey.go b/testData/inspections/struct-initialization/innerLiteralFieldWithKey.go new file mode 100644 index 0000000000..e1006c38fa --- /dev/null +++ b/testData/inspections/struct-initialization/innerLiteralFieldWithKey.go @@ -0,0 +1,13 @@ +package foo + +type S struct { + t int +} + +type B struct{ + s S +} + +func main() { + var _ = []B{ s: {1}} +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/innerLiteralFieldWithoutKey-after.go b/testData/inspections/struct-initialization/innerLiteralFieldWithoutKey-after.go new file mode 100644 index 0000000000..ed08b7eccd --- /dev/null +++ b/testData/inspections/struct-initialization/innerLiteralFieldWithoutKey-after.go @@ -0,0 +1,13 @@ +package foo + +type S struct { + t int +} + +type B struct{ + s S +} + +func main() { + var _ = []B{ B{s: S{t: 1}} } +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/innerLiteralFieldWithoutKey.go b/testData/inspections/struct-initialization/innerLiteralFieldWithoutKey.go new file mode 100644 index 0000000000..c7be5b7d08 --- /dev/null +++ b/testData/inspections/struct-initialization/innerLiteralFieldWithoutKey.go @@ -0,0 +1,13 @@ +package foo + +type S struct { + t int +} + +type B struct{ + s S +} + +func main() { + var _ = []B{ B{S{t: 1}} } +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/innerLiteralMap-after.go b/testData/inspections/struct-initialization/innerLiteralMap-after.go new file mode 100644 index 0000000000..ad25c2bddd --- /dev/null +++ b/testData/inspections/struct-initialization/innerLiteralMap-after.go @@ -0,0 +1,13 @@ +package foo + +type S struct { + r, t int +} + +func main() { + var _ = map[string] []S{ + "s": { + {r: 1, t: 1}, + }, +} +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/innerLiteralMap.go b/testData/inspections/struct-initialization/innerLiteralMap.go new file mode 100644 index 0000000000..64f93437b4 --- /dev/null +++ b/testData/inspections/struct-initialization/innerLiteralMap.go @@ -0,0 +1,13 @@ +package foo + +type S struct { + r, t int +} + +func main() { + var _ = map[string] []S{ + "s": { + {1, 1}, + }, +} +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/innerLiteralWithoutType-after.go b/testData/inspections/struct-initialization/innerLiteralWithoutType-after.go new file mode 100644 index 0000000000..89578194d1 --- /dev/null +++ b/testData/inspections/struct-initialization/innerLiteralWithoutType-after.go @@ -0,0 +1,16 @@ +package main + +type B struct { + t int +} + +type S struct { + b B + a int +} + +func main() { + _ = map[string]S{ + "key": {b: B{t: 1} }, + } +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/innerLiteralWithoutType.go b/testData/inspections/struct-initialization/innerLiteralWithoutType.go new file mode 100644 index 0000000000..679f964c6e --- /dev/null +++ b/testData/inspections/struct-initialization/innerLiteralWithoutType.go @@ -0,0 +1,16 @@ +package main + +type B struct { + t int +} + +type S struct { + b B + a int +} + +func main() { + _ = map[string]S{ + "key": { B{t: 1} }, + } +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/innerStruct-after.go b/testData/inspections/struct-initialization/innerStruct-after.go new file mode 100644 index 0000000000..b01d8832e5 --- /dev/null +++ b/testData/inspections/struct-initialization/innerStruct-after.go @@ -0,0 +1,16 @@ +package foo + +func main() { + type B struct { + Y int + } + + type S struct { + X int + b B + Z int + } + + s := S{X: 1, b: B{Y: 2}} + print(s.b.Y) +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/innerStruct.go b/testData/inspections/struct-initialization/innerStruct.go new file mode 100644 index 0000000000..541536c646 --- /dev/null +++ b/testData/inspections/struct-initialization/innerStruct.go @@ -0,0 +1,16 @@ +package foo + +func main() { + type B struct { + Y int + } + + type S struct { + X int + b B + Z int + } + + s := S{1, B{Y: 2}} + print(s.b.Y) +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/invalidKeysNumber.go b/testData/inspections/struct-initialization/invalidKeysNumber.go new file mode 100644 index 0000000000..6056203b09 --- /dev/null +++ b/testData/inspections/struct-initialization/invalidKeysNumber.go @@ -0,0 +1,12 @@ +package main + +type S struct { + r, t int +} + +type B struct{ + S +} +func main() { + var _ = []B{ S: {S: 2, 3}} +} diff --git a/testData/inspections/struct-initialization/literalValue-after.go b/testData/inspections/struct-initialization/literalValue-after.go new file mode 100644 index 0000000000..8d5e2445c4 --- /dev/null +++ b/testData/inspections/struct-initialization/literalValue-after.go @@ -0,0 +1,12 @@ +package foo + +func main() { + type B struct { + string + } + type S struct { + int + B + } + _ = []S{ {int: 1, B: {string: "a"}}} +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/literalValue.go b/testData/inspections/struct-initialization/literalValue.go new file mode 100644 index 0000000000..319af5494e --- /dev/null +++ b/testData/inspections/struct-initialization/literalValue.go @@ -0,0 +1,12 @@ +package foo + +func main() { + type B struct { + string + } + type S struct { + int + B + } + _ = []S{ {1, B: {string: "a"}}} +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/literalValueWithoutCompositeLit-after.go b/testData/inspections/struct-initialization/literalValueWithoutCompositeLit-after.go new file mode 100644 index 0000000000..bee9a62c5c --- /dev/null +++ b/testData/inspections/struct-initialization/literalValueWithoutCompositeLit-after.go @@ -0,0 +1,13 @@ +package foo + +type S struct { + t int +} + +type B struct{ + S +} + +func main() { + var _ = []B{ S: {S: 1}} +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/literalValueWithoutCompositeLit.go b/testData/inspections/struct-initialization/literalValueWithoutCompositeLit.go new file mode 100644 index 0000000000..c4552af17a --- /dev/null +++ b/testData/inspections/struct-initialization/literalValueWithoutCompositeLit.go @@ -0,0 +1,13 @@ +package foo + +type S struct { + t int +} + +type B struct{ + S +} + +func main() { + var _ = []B{ S: {1}} +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/literalWithoutCompositeLit.go b/testData/inspections/struct-initialization/literalWithoutCompositeLit.go new file mode 100644 index 0000000000..beace17d84 --- /dev/null +++ b/testData/inspections/struct-initialization/literalWithoutCompositeLit.go @@ -0,0 +1,11 @@ +package foo + +func main() { + type B struct { + string + } + type S struct { + B + } + _ = []S{ { {"a"} } } +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/multipleFieldsOneline.go b/testData/inspections/struct-initialization/multipleFieldsOneline.go new file mode 100644 index 0000000000..9b736c78f1 --- /dev/null +++ b/testData/inspections/struct-initialization/multipleFieldsOneline.go @@ -0,0 +1,11 @@ +package foo + +type S struct { + X, Y int +} +func main() { + var s S + s = S{0, 0} + s = S{X: 0, 0} + s = S{0, Y: 0} +} diff --git a/testData/inspections/struct-initialization/multipleInnerLiterals-after.go b/testData/inspections/struct-initialization/multipleInnerLiterals-after.go new file mode 100644 index 0000000000..e18cc27513 --- /dev/null +++ b/testData/inspections/struct-initialization/multipleInnerLiterals-after.go @@ -0,0 +1,18 @@ +package foo + +type S struct { + t int +} + +type P struct { + r int +} + +type B struct{ + s S + p P +} + +func main() { + var _ = []B{{ p: {r: 1}}} +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/multipleInnerLiterals.go b/testData/inspections/struct-initialization/multipleInnerLiterals.go new file mode 100644 index 0000000000..6c892badef --- /dev/null +++ b/testData/inspections/struct-initialization/multipleInnerLiterals.go @@ -0,0 +1,18 @@ +package foo + +type S struct { + t int +} + +type P struct { + r int +} + +type B struct{ + s S + p P +} + +func main() { + var _ = []B{{ p: {1}}} +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/nestedLiteral-after.go b/testData/inspections/struct-initialization/nestedLiteral-after.go new file mode 100644 index 0000000000..b2eeb1d2c7 --- /dev/null +++ b/testData/inspections/struct-initialization/nestedLiteral-after.go @@ -0,0 +1,13 @@ +package foo + +type S struct { + r, t int +} + +type B struct{ + S +} + +func main() { + var _ = []B{ S: S{r: 2, t: 3}} +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/nestedLiteral.go b/testData/inspections/struct-initialization/nestedLiteral.go new file mode 100644 index 0000000000..cb5ca8c5a3 --- /dev/null +++ b/testData/inspections/struct-initialization/nestedLiteral.go @@ -0,0 +1,13 @@ +package foo + +type S struct { + r, t int +} + +type B struct{ + S +} + +func main() { + var _ = []B{ S: S{2, 3}} +} \ No newline at end of file diff --git a/testData/inspections/struct-initialization/onelineQuickfix-after.go b/testData/inspections/struct-initialization/onelineQuickfix-after.go new file mode 100644 index 0000000000..d083c17594 --- /dev/null +++ b/testData/inspections/struct-initialization/onelineQuickfix-after.go @@ -0,0 +1,9 @@ +package foo + +type S struct { + X, Y int +} +func main() { + var s S + s = S{X: 0, Y: 0} +} diff --git a/testData/inspections/struct-initialization/onelineQuickfix.go b/testData/inspections/struct-initialization/onelineQuickfix.go new file mode 100644 index 0000000000..051a401c1a --- /dev/null +++ b/testData/inspections/struct-initialization/onelineQuickfix.go @@ -0,0 +1,9 @@ +package foo + +type S struct { + X, Y int +} +func main() { + var s S + s = S{0, 0} +} diff --git a/testData/inspections/go-struct-initialization/quickFix-after.go b/testData/inspections/struct-initialization/quickFix-after.go similarity index 100% rename from testData/inspections/go-struct-initialization/quickFix-after.go rename to testData/inspections/struct-initialization/quickFix-after.go diff --git a/testData/inspections/go-struct-initialization/quickFix.go b/testData/inspections/struct-initialization/quickFix.go similarity index 55% rename from testData/inspections/go-struct-initialization/quickFix.go rename to testData/inspections/struct-initialization/quickFix.go index 5527e079ed..a22185111e 100644 --- a/testData/inspections/go-struct-initialization/quickFix.go +++ b/testData/inspections/struct-initialization/quickFix.go @@ -4,6 +4,6 @@ import "io" func _() { _ = io.LimitedReader{ - nil, + nil, } } \ No newline at end of file diff --git a/testData/inspections/go-struct-initialization/uninitializedStructImportedOnly.go b/testData/inspections/struct-initialization/uninitializedStructImportedOnly.go similarity index 96% rename from testData/inspections/go-struct-initialization/uninitializedStructImportedOnly.go rename to testData/inspections/struct-initialization/uninitializedStructImportedOnly.go index 69704ed2eb..1949efd43e 100644 --- a/testData/inspections/go-struct-initialization/uninitializedStructImportedOnly.go +++ b/testData/inspections/struct-initialization/uninitializedStructImportedOnly.go @@ -114,7 +114,7 @@ func _() { // bad defs _ = io.LimitedReader{ - nil, + nil, } _ = os.LinkError{ "string", diff --git a/testData/inspections/go-struct-initialization/uninitializedStructWithLocal.go b/testData/inspections/struct-initialization/uninitializedStructWithLocal.go similarity index 100% rename from testData/inspections/go-struct-initialization/uninitializedStructWithLocal.go rename to testData/inspections/struct-initialization/uninitializedStructWithLocal.go diff --git a/testData/inspections/struct-initialization/varDeclaration-after.go b/testData/inspections/struct-initialization/varDeclaration-after.go new file mode 100644 index 0000000000..2e33e256d6 --- /dev/null +++ b/testData/inspections/struct-initialization/varDeclaration-after.go @@ -0,0 +1,8 @@ +package foo + +type S struct { + X, Y int +} +func main() { + var s S = S{X: 0, Y: 0} +} diff --git a/testData/inspections/struct-initialization/varDeclaration.go b/testData/inspections/struct-initialization/varDeclaration.go new file mode 100644 index 0000000000..82a12342f3 --- /dev/null +++ b/testData/inspections/struct-initialization/varDeclaration.go @@ -0,0 +1,8 @@ +package foo + +type S struct { + X, Y int +} +func main() { + var s S = S{0, 0} +} diff --git a/testData/inspections/struct-initialization/wrongOrder.go b/testData/inspections/struct-initialization/wrongOrder.go new file mode 100644 index 0000000000..37f16b1623 --- /dev/null +++ b/testData/inspections/struct-initialization/wrongOrder.go @@ -0,0 +1,8 @@ +package foo + +type S struct { + X, Y int +} +func main() { + s := S{Y: 1, 0} +} diff --git a/tests/com/goide/inspections/GoStructInitializationInspectionTest.java b/tests/com/goide/inspections/GoStructInitializationInspectionTest.java index 0779389a01..2aad091714 100644 --- a/tests/com/goide/inspections/GoStructInitializationInspectionTest.java +++ b/tests/com/goide/inspections/GoStructInitializationInspectionTest.java @@ -38,26 +38,45 @@ protected void tearDown() throws Exception { super.tearDown(); } - public void testUninitializedStructWithLocal() { - doTest(true); - } + public void testUninitializedStructWithLocal() { doHighlightTest(true); } + public void testMultipleFieldsOneline() { doHighlightTest(true); } + public void testWrongOrder() { doHighlightTest(true); } + public void testExceedElementsCountWithNamed() { doHighlightTest(true); } + public void testInvalidKeysNumber() { doHighlightTest(true); } + public void testUninitializedStructImportedOnly() { doHighlightTest(false); } - public void testUninitializedStructImportedOnly() { - doTest(false); - } + public void testLiteralValue() { doQuickfixTest(); } + public void testLiteralValueWithoutCompositeLit() { doQuickfixTest(); } + public void testExceedElementsCountOnlyUnnamed() { doQuickfixTest(); } + public void testInnerAnonStruct() { doQuickfixTest(); } + public void testAnonField() { doQuickfixTest(); } + public void testQuickFix() { doQuickfixTest(); } + public void testInnerStruct() { doQuickfixTest(); } + public void testOnelineQuickfix() { doQuickfixTest(); } + public void testVarDeclaration() { doQuickfixTest(); } + public void testNestedLiteral() { doQuickfixTest(); } + public void testInnerLiteral() { doQuickfixTest(); } + public void testInnerAnonLiteral() { doQuickfixTest(); } + public void testInnerLiteralMap() { doQuickfixTest(); } + public void testInnerLiteralFieldWithKey() { doQuickfixTest(); } + public void testMultipleInnerLiterals() { doQuickfixTest(); } + public void testInnerLiteralWithoutType() { doQuickfixTest(); } - public void testQuickFix() { - doTest(GoStructInitializationInspection.REPLACE_WITH_NAMED_STRUCT_FIELD_FIX_NAME, true); - } - private long doTest(boolean allowLocalStructs) { + + private long doHighlightTest(boolean allowLocalStructs) { myInspectionTool.reportLocalStructs = allowLocalStructs; return myFixture.testHighlighting(true, false, true, getTestName(true) + ".go"); } + private void doQuickfixTest() { + myInspectionTool.reportLocalStructs = true; + doTest(GoStructInitializationInspection.REPLACE_WITH_NAMED_STRUCT_FIELD_FIX_NAME, true); + } + @NotNull @Override protected String getBasePath() { - return "inspections/go-struct-initialization"; + return "inspections/struct-initialization"; } }