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..bb98e52d25 100644 --- a/src/com/goide/inspections/GoStructInitializationInspection.java +++ b/src/com/goide/inspections/GoStructInitializationInspection.java @@ -22,25 +22,31 @@ 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.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; @@ -48,68 +54,148 @@ public class GoStructInitializationInspection extends GoInspectionBase { @Override 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 keys = getKeys(elements); + if (!areKeysMatchesDefinitions(keys, getFieldDefinitionsNames(structType))) return; + registerProblemsForElementsWithoutKeys(elements, keys, holder); } }; } - @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; + + GoStructType litType = ObjectUtils.tryCast(GoPsiImplUtil.getLiteralType(literalValue, parentLit == null), GoStructType.class); + String definitionName = getFieldDefinitionName(GoPsiTreeUtil.getDirectParentOfType(literalValue, GoValue.class)); + return definitionName != null && litType != null ? getFieldDefinitionType(litType, definitionName) : litType; } - 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()); - } + @Nullable + private static String getFieldDefinitionName(@Nullable GoValue value) { + GoKey key = PsiTreeUtil.getPrevSiblingOfType(value, GoKey.class); + GoFieldName fieldName = key != null ? key.getFieldName() : null; + return fieldName != null ? fieldName.getText() : null; } - 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 GoStructType getFieldDefinitionType(@NotNull GoStructType structType, @NotNull String definitionName) { + GoFieldDefinition fieldDefinition = getDefinition(structType, definitionName); + if (fieldDefinition != null) return ObjectUtils.tryCast(getUnderlyingType(fieldDefinition), GoStructType.class); + + GoAnonymousFieldDefinition anonymousDefinition = getAnonDefinition(structType, definitionName); + return anonymousDefinition != null ? ObjectUtils + .tryCast(GoPsiImplUtil.getUnderlyingType(anonymousDefinition.getType()), GoStructType.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; + private static GoType getUnderlyingType(@NotNull GoFieldDefinition fieldDefinition) { + GoType type = fieldDefinition.getGoType(null); + return type != null ? GoPsiImplUtil.getUnderlyingType(type) : null; + } + + @Nullable + private static GoAnonymousFieldDefinition getAnonDefinition(@NotNull GoStructType type, @NotNull String definitionName) { + return type.getFieldDeclarationList().stream() + .map(GoFieldDeclaration::getAnonymousFieldDefinition) + .filter(definition -> definition != null && Comparing.equal(definitionName, definition.getName())) + .findAny().orElse(null); + } + + @Nullable + private static GoFieldDefinition getDefinition(@NotNull GoStructType type, @NotNull String definitionName) { + return type.getFieldDeclarationList().stream().flatMap(declaration -> declaration.getFieldDefinitionList().stream()) + .filter(definition -> Comparing.equal(definition.getName(), definitionName)) + .findAny().orElse(null); + } + + + private static boolean isStructLit(@NotNull GoCompositeLit parentLit) { + GoType type = parentLit.getGoType(null); + return type != null && GoPsiImplUtil.getUnderlyingType(type) instanceof GoStructType; + } + + private boolean isStructImportedOrLocalAllowed(@NotNull GoStructType structType, @NotNull GoLiteralValue literalValue) { + return reportLocalStructs || !GoUtil.inSamePackage(structType.getContainingFile(), literalValue.getContainingFile()); + } + + @NotNull + private static List getKeys(@NotNull List elements) { + return map(elements, element -> { + GoKey key = element.getKey(); + return key != null ? key.getText() : null; + }); + } + + private static boolean areKeysMatchesDefinitions(@NotNull List keys, @NotNull List fieldDefinitionsNames) { + return range(0, keys.size()).allMatch(i -> isNullOrEqual(keys.get(i), GoPsiImplUtil.getByIndex(fieldDefinitionsNames, i))); + } + + @Contract("null, _ -> true") + private static boolean isNullOrEqual(@Nullable Object o, @Nullable Object objectToCompare) { + return o == null || Comparing.equal(o, objectToCompare); + } + + @NotNull + private static List getFieldDefinitionsNames(@Nullable GoStructType type) { + return type != null ? type.getFieldDeclarationList().stream() + .flatMap(declaration -> getFieldDefinitionsNames(declaration).stream()) + .collect(toList()) : emptyList(); + } + + @NotNull + private static List getFieldDefinitionsNames(@NotNull GoFieldDeclaration declaration) { + GoAnonymousFieldDefinition definition = declaration.getAnonymousFieldDefinition(); + return definition != null ? list(definition.getName()) : map(declaration.getFieldDefinitionList(), GoNamedElement::getName); + } + + private static void registerProblemsForElementsWithoutKeys(@NotNull List elements, + @NotNull List keys, + @NotNull ProblemsHolder holder) { + for (int i = 0; i < elements.size(); i++) { + if (GoPsiImplUtil.getByIndex(keys, i) != null) continue; + holder.registerProblem(elements.get(i), "Unnamed field initializations", ProblemHighlightType.WEAK_WARNING, QUICK_FIX); + } + } + + @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 = getFieldDefinitionsNames(getLiteralStructType(literal)); + if (!areKeysMatchesDefinitions(getKeys(elements), fieldDefinitionNames)) return; + addKeysToElements(project, elements, fieldDefinitionNames); + } + } + + private static void addKeysToElements(@NotNull Project project, + @NotNull List elements, + @NotNull List fieldDefinitionNames) { + for (int i = 0; i < elements.size(); i++) { + GoElement element = elements.get(i); + String fieldDefinitionName = GoPsiImplUtil.getByIndex(fieldDefinitionNames, i); + 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/go-struct-initialization/quickFix.go b/testData/inspections/go-struct-initialization/quickFix.go deleted file mode 100644 index 5527e079ed..0000000000 --- a/testData/inspections/go-struct-initialization/quickFix.go +++ /dev/null @@ -1,9 +0,0 @@ -package foo - -import "io" - -func _() { - _ = io.LimitedReader{ - nil, - } -} \ No newline at end of file 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..c61ee1f7f8 --- /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..3111e5f85a --- /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..9b7fcd633b --- /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..2161d91d9b --- /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..7d886b6d56 --- /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..b63b21bdac --- /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: {t: 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..d7dde67b72 --- /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..cab7e92d59 --- /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..436a99f841 --- /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..335396c9b3 --- /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..7998f778b7 --- /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/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..5d45f3ca9e --- /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..daf2e220c7 --- /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: {t: 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..9ec198cd3e --- /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..19d8f681f9 --- /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..a2120c2381 --- /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..5f59eb9cc6 --- /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..7b99347057 --- /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..f2bd95d799 --- /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/struct-initialization/quickFix.go b/testData/inspections/struct-initialization/quickFix.go new file mode 100644 index 0000000000..bfb2a87469 --- /dev/null +++ b/testData/inspections/struct-initialization/quickFix.go @@ -0,0 +1,9 @@ +package foo + +import "io" + +func _() { + _ = io.LimitedReader{ + 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 81% rename from testData/inspections/go-struct-initialization/uninitializedStructImportedOnly.go rename to testData/inspections/struct-initialization/uninitializedStructImportedOnly.go index 69704ed2eb..d7740eeb92 100644 --- a/testData/inspections/go-struct-initialization/uninitializedStructImportedOnly.go +++ b/testData/inspections/struct-initialization/uninitializedStructImportedOnly.go @@ -114,13 +114,13 @@ func _() { // bad defs _ = io.LimitedReader{ - nil, + nil, } _ = os.LinkError{ - "string", - "string", - "string", - nil, + "string", + "string", + "string", + nil, } } diff --git a/testData/inspections/go-struct-initialization/uninitializedStructWithLocal.go b/testData/inspections/struct-initialization/uninitializedStructWithLocal.go similarity index 64% rename from testData/inspections/go-struct-initialization/uninitializedStructWithLocal.go rename to testData/inspections/struct-initialization/uninitializedStructWithLocal.go index 1e333be312..5fa4d4c8bd 100644 --- a/testData/inspections/go-struct-initialization/uninitializedStructWithLocal.go +++ b/testData/inspections/struct-initialization/uninitializedStructWithLocal.go @@ -76,21 +76,21 @@ func _() struct { _ = demo{a: "demo", b: 1} // bad defs - _ = demo{"demo"} - b, _ := demo{"demo"}, 1 + _ = demo{"demo"} + b, _ := demo{"demo"}, 1 _ = demo{ - "demo", - 1, - "demo", + "demo", + 1, + "demo", } _ = demo{ a: "demo", - 1, + 1, } _ = bemo{x: "demo"} _ = b - return struct{ x string }{"demo"} + return struct{ x string }{"demo"} } type Item struct { @@ -114,13 +114,13 @@ func _() { // bad defs _ = io.LimitedReader{ - nil, + nil, } _ = os.LinkError{ - "string", - "string", - "string", - nil, + "string", + "string", + "string", + nil, } } 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..048bc5c0e2 --- /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..2266580675 100644 --- a/tests/com/goide/inspections/GoStructInitializationInspectionTest.java +++ b/tests/com/goide/inspections/GoStructInitializationInspectionTest.java @@ -38,26 +38,44 @@ 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 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"; } }