Skip to content

Commit f8a68be

Browse files
committed
Move to struct declaration: handle struct declaration via _var_ case
Replace single struct declaration with short var declaration, or add to existing struct literal in statement
1 parent 00f9d70 commit f8a68be

14 files changed

+238
-23
lines changed

src/com/goide/intentions/GoMoveToStructInitializationIntention.java

Lines changed: 101 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,29 @@ private static Data getData(@NotNull PsiElement element) {
6161
if (!element.isValid() || !element.isWritable()) return null;
6262
GoAssignmentStatement assignment = getValidAssignmentParent(element);
6363
GoReferenceExpression selectedFieldReference = assignment != null ? getFieldReferenceExpression(element, assignment) : null;
64-
GoCompositeLit compositeLit = selectedFieldReference != null ? getStructLiteralByReference(selectedFieldReference, assignment) : null;
65-
if (compositeLit == null) return null;
64+
if (selectedFieldReference == null) return null;
6665

67-
List<GoReferenceExpression> references = getUninitializedSingleFieldReferences(assignment, selectedFieldReference, compositeLit);
68-
return !references.isEmpty() ? new Data(assignment, compositeLit, references) : null;
66+
GoVarDefinition structDefinition = getDefinition(selectedFieldReference);
67+
GoStatement previousStatement = PsiTreeUtil.getPrevSiblingOfType(assignment, GoStatement.class);
68+
boolean needReplaceDeclarationWithShortVar = isUnassigned(getSingleVarSpecByDefinition(previousStatement, structDefinition));
69+
70+
GoCompositeLit compositeLit = getStructLiteralByReference(selectedFieldReference, assignment);
71+
if (compositeLit == null && !needReplaceDeclarationWithShortVar || structDefinition == null) return null;
72+
73+
List<GoReferenceExpression> references = getUninitializedSingleFieldReferences(assignment, structDefinition, compositeLit);
74+
return !references.isEmpty() ? new Data(assignment, compositeLit, references, previousStatement, structDefinition) : null;
75+
}
76+
77+
@Nullable
78+
private static GoCompositeLit createStructLiteral(@NotNull GoVarDefinition definition, @NotNull Project project) {
79+
GoType type = definition.getGoType(null);
80+
return type != null ? GoElementFactory.createCompositeLit(project, type) : null;
81+
}
82+
83+
@Nullable
84+
@Contract("null -> null")
85+
private static GoVarDefinition getDefinition(@Nullable GoReferenceExpression referenceExpressions) {
86+
return ObjectUtils.tryCast(resolveQualifier(referenceExpressions), GoVarDefinition.class);
6987
}
7088

7189
@Nullable
@@ -104,6 +122,22 @@ private static GoReferenceExpression unwrapParensAndCast(@Nullable PsiElement e)
104122
return ObjectUtils.tryCast(e, GoReferenceExpression.class);
105123
}
106124

125+
@Nullable
126+
@Contract("_, null -> null; null, _ -> null")
127+
private static GoVarSpec getSingleVarSpecByDefinition(@Nullable GoStatement statement,
128+
@Nullable GoVarDefinition definition) {
129+
GoVarDeclaration declaration = statement != null ? statement.getVarDeclaration() : null;
130+
List<GoVarSpec> varSpecs = declaration != null ? declaration.getVarSpecList() : emptyList();
131+
GoVarSpec singleVarSpec = varSpecs.size() == 1 ? getFirstItem(varSpecs) : null;
132+
List<GoVarDefinition> varDefinitions = singleVarSpec != null ? singleVarSpec.getVarDefinitionList() : emptyList();
133+
return varDefinitions.size() == 1 && definition == getFirstItem(varDefinitions) ? singleVarSpec : null;
134+
}
135+
136+
@Contract("null -> false")
137+
private static boolean isUnassigned(@Nullable GoVarSpec varSpec) {
138+
return varSpec != null && varSpec.getExpressionList().isEmpty();
139+
}
140+
107141
@Contract("null -> false")
108142
private static boolean isFieldReferenceExpression(@Nullable PsiElement element) {
109143
return element instanceof GoReferenceExpression && isFieldDefinition(((GoReferenceExpression)element).resolve());
@@ -138,11 +172,10 @@ private static boolean isResolvedTo(@Nullable PsiElement e, @Nullable PsiElement
138172

139173
@NotNull
140174
private static List<GoReferenceExpression> getUninitializedSingleFieldReferences(@NotNull GoAssignmentStatement assignment,
141-
@NotNull GoReferenceExpression fieldReferenceExpression,
142-
@NotNull GoCompositeLit compositeLit) {
143-
PsiElement resolve = resolveQualifier(fieldReferenceExpression);
175+
@Nullable GoVarDefinition definition,
176+
@Nullable GoCompositeLit compositeLit) {
144177
List<GoReferenceExpression> uninitializedFieldReferencesByQualifier =
145-
filter(getUninitializedFieldReferenceExpressions(assignment, compositeLit), e -> isResolvedTo(e.getQualifier(), resolve));
178+
filter(getUninitializedFieldReferenceExpressions(assignment, compositeLit), e -> isResolvedTo(e.getQualifier(), definition));
146179
MultiMap<PsiElement, GoReferenceExpression> resolved = groupBy(uninitializedFieldReferencesByQualifier, GoReferenceExpression::resolve);
147180
return map(filter(resolved.entrySet(), set -> set.getValue().size() == 1), set -> getFirstItem(set.getValue()));
148181
}
@@ -151,13 +184,15 @@ private static List<GoReferenceExpression> getUninitializedSingleFieldReferences
151184
private static GoCompositeLit getStructLiteralByReference(@NotNull GoReferenceExpression fieldReferenceExpression,
152185
@NotNull GoAssignmentStatement assignment) {
153186
GoStatement previousStatement = PsiTreeUtil.getPrevSiblingOfType(assignment, GoStatement.class);
187+
if (previousStatement == null) return null;
188+
154189
if (previousStatement instanceof GoSimpleStatement) {
155190
return getStructLiteral(fieldReferenceExpression, (GoSimpleStatement)previousStatement);
156191
}
157192
if (previousStatement instanceof GoAssignmentStatement) {
158193
return getStructLiteral(fieldReferenceExpression, (GoAssignmentStatement)previousStatement);
159194
}
160-
return null;
195+
return getStructLiteral(previousStatement, fieldReferenceExpression);
161196
}
162197

163198
@Nullable
@@ -172,15 +207,16 @@ private static GoCompositeLit getStructLiteral(@NotNull GoReferenceExpression fi
172207
}
173208

174209
@Nullable
175-
private static PsiElement resolveQualifier(@NotNull GoReferenceExpression fieldReferenceExpression) {
176-
GoReferenceExpression qualifier = fieldReferenceExpression.getQualifier();
210+
@Contract("null -> null")
211+
private static PsiElement resolveQualifier(@Nullable GoReferenceExpression fieldReferenceExpression) {
212+
GoReferenceExpression qualifier = fieldReferenceExpression != null ? fieldReferenceExpression.getQualifier() : null;
177213
return qualifier != null ? qualifier.resolve() : null;
178214
}
179215

180216
@Nullable
181217
private static GoCompositeLit getStructLiteral(@NotNull GoReferenceExpression fieldReferenceExpression,
182218
@NotNull GoAssignmentStatement structAssignment) {
183-
GoVarDefinition varDefinition = ObjectUtils.tryCast(resolveQualifier(fieldReferenceExpression), GoVarDefinition.class);
219+
GoVarDefinition varDefinition = getDefinition(fieldReferenceExpression);
184220
PsiElement field = fieldReferenceExpression.resolve();
185221
if (varDefinition == null || !isFieldDefinition(field) || !hasStructTypeWithField(varDefinition, (GoNamedElement)field)) {
186222
return null;
@@ -193,6 +229,14 @@ private static GoCompositeLit getStructLiteral(@NotNull GoReferenceExpression fi
193229
return ObjectUtils.tryCast(compositeLit, GoCompositeLit.class);
194230
}
195231

232+
@Nullable
233+
private static GoCompositeLit getStructLiteral(@NotNull GoStatement statement,
234+
@NotNull GoReferenceExpression fieldReferenceExpression) {
235+
GoVarDefinition definition = getDefinition(fieldReferenceExpression);
236+
GoVarSpec varSpec = definition != null ? getSingleVarSpecByDefinition(statement, definition) : null;
237+
return varSpec != null ? ObjectUtils.tryCast(getFirstItem(varSpec.getRightExpressionsList()), GoCompositeLit.class) : null;
238+
}
239+
196240
private static boolean hasStructTypeWithField(@NotNull GoVarDefinition structVarDefinition, @NotNull GoNamedElement field) {
197241
GoType type = structVarDefinition.getGoType(null);
198242
GoStructType structType = type != null ? ObjectUtils.tryCast(type.getUnderlyingType(), GoStructType.class) : null;
@@ -207,15 +251,16 @@ private static boolean isFieldInitialization(@NotNull GoElement element, @NotNul
207251

208252
@NotNull
209253
private static List<GoReferenceExpression> getUninitializedFieldReferenceExpressions(@NotNull GoAssignmentStatement assignment,
210-
@NotNull GoCompositeLit structLiteral) {
254+
@Nullable GoCompositeLit structLiteral) {
211255
return filter(getFieldReferenceExpressions(assignment), expression ->
212256
isUninitializedFieldReferenceExpression(expression, structLiteral) && !isAssignedInPreviousStatement(expression, assignment));
213257
}
214258

215-
@Contract("null, _-> false")
259+
@Contract("null, _-> false; !null, null -> true")
216260
private static boolean isUninitializedFieldReferenceExpression(@Nullable GoReferenceExpression fieldReferenceExpression,
217-
@NotNull GoCompositeLit structLiteral) {
261+
@Nullable GoCompositeLit structLiteral) {
218262
if (fieldReferenceExpression == null) return false;
263+
if (structLiteral == null) return true;
219264
GoLiteralValue literalValue = structLiteral.getLiteralValue();
220265
PsiElement resolve = fieldReferenceExpression.resolve();
221266
return literalValue != null && isFieldDefinition(resolve) &&
@@ -238,19 +283,31 @@ private static List<? extends PsiElement> getLeftHandElements(@NotNull GoStateme
238283
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
239284
Data data = getData(element);
240285
if (data == null) return;
241-
moveFieldReferenceExpressions(data);
286+
287+
boolean needReplaceDeclarationWithShortVar = data.getCompositeLit() == null;
288+
GoCompositeLit compositeLit =
289+
needReplaceDeclarationWithShortVar ? createStructLiteral(data.getStructDefinition(), project) : data.getCompositeLit();
290+
if (compositeLit == null || needReplaceDeclarationWithShortVar && data.getStructDeclaration() == null) return;
291+
292+
moveFieldReferenceExpressions(data.getReferenceExpressions(), compositeLit, data.getAssignment());
293+
if (!needReplaceDeclarationWithShortVar) return;
294+
GoStatement shortVarStatement =
295+
GoElementFactory.createShortVarDeclarationStatement(project, data.getStructDefinition().getText(), compositeLit.getText());
296+
data.getStructDeclaration().replace(shortVarStatement);
242297
}
243298

244-
private static void moveFieldReferenceExpressions(@NotNull Data data) {
245-
GoLiteralValue literalValue = data.getCompositeLit().getLiteralValue();
299+
private static void moveFieldReferenceExpressions(@NotNull List<GoReferenceExpression> referenceExpressions,
300+
@NotNull GoCompositeLit compositeLit,
301+
@NotNull GoAssignmentStatement parentAssignment) {
302+
GoLiteralValue literalValue = compositeLit.getLiteralValue();
246303
if (literalValue == null) return;
247304

248-
for (GoReferenceExpression expression : data.getReferenceExpressions()) {
305+
for (GoReferenceExpression expression : referenceExpressions) {
249306
GoExpression anchor = getTopmostExpression(expression);
250-
GoExpression fieldValue = GoPsiImplUtil.getRightExpression(data.getAssignment(), anchor);
307+
GoExpression fieldValue = GoPsiImplUtil.getRightExpression(parentAssignment, anchor);
251308
if (fieldValue == null) continue;
252309

253-
GoPsiImplUtil.deleteExpressionFromAssignment(data.getAssignment(), anchor);
310+
GoPsiImplUtil.deleteExpressionFromAssignment(parentAssignment, anchor);
254311
addFieldDefinition(literalValue, expression.getIdentifier().getText(), fieldValue.getText());
255312
}
256313
}
@@ -272,25 +329,46 @@ private static class Data {
272329
private final GoCompositeLit myCompositeLit;
273330
private final GoAssignmentStatement myAssignment;
274331
private final List<GoReferenceExpression> myReferenceExpressions;
332+
private final GoStatement myStructDeclaration;
333+
private final GoVarDefinition myStructDefinition;
275334

276335
public Data(@NotNull GoAssignmentStatement assignment,
277-
@NotNull GoCompositeLit compositeLit,
278-
@NotNull List<GoReferenceExpression> referenceExpressions) {
336+
@Nullable GoCompositeLit compositeLit,
337+
@NotNull List<GoReferenceExpression> referenceExpressions,
338+
@Nullable GoStatement structDeclaration,
339+
@NotNull GoVarDefinition structDefinition) {
279340
myCompositeLit = compositeLit;
280341
myAssignment = assignment;
281342
myReferenceExpressions = referenceExpressions;
343+
myStructDeclaration = structDeclaration;
344+
myStructDefinition = structDefinition;
282345
}
283346

347+
@Nullable
284348
public GoCompositeLit getCompositeLit() {
285349
return myCompositeLit;
286350
}
287351

352+
@NotNull
288353
public GoAssignmentStatement getAssignment() {
289354
return myAssignment;
290355
}
291356

357+
@NotNull
292358
public List<GoReferenceExpression> getReferenceExpressions() {
293359
return myReferenceExpressions;
294360
}
361+
362+
@Nullable
363+
public GoStatement getStructDeclaration() {
364+
return myStructDeclaration;
365+
}
366+
367+
@NotNull
368+
public GoVarDefinition getStructDefinition() {
369+
return myStructDefinition;
370+
}
295371
}
296372
}
373+
374+

src/com/goide/psi/impl/GoElementFactory.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,4 +266,10 @@ public static GoTypeDeclaration createTypeDeclaration(@NotNull Project project,
266266
GoFile file = createFileFromText(project, "package a; type " + name + " " + type.getText());
267267
return PsiTreeUtil.findChildOfType(file, GoTypeDeclaration.class);
268268
}
269+
270+
@NotNull
271+
public static GoCompositeLit createCompositeLit(@NotNull Project project, @NotNull GoType type) {
272+
GoFile file = createFileFromText(project, "package a; var _ = " + type.getText() + "{}");
273+
return PsiTreeUtil.findChildOfType(file, GoCompositeLit.class);
274+
}
269275
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package main
2+
3+
type S struct {
4+
foo string
5+
}
6+
7+
func main() {
8+
var s string
9+
s.foo <caret>= "bar"
10+
print(s.foo)
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package main
2+
3+
type S struct {
4+
foo string
5+
}
6+
7+
func main() {
8+
s := S{foo: "bar"}
9+
10+
print(s.foo)
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package main
2+
3+
type S struct {
4+
foo string
5+
}
6+
7+
func main() {
8+
var (s S)
9+
s.foo <caret>= "bar"
10+
print(s.foo)
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package main
2+
3+
type S struct {
4+
foo string
5+
}
6+
7+
func main() {
8+
s := S{foo: "bar"}
9+
10+
print(s.foo)
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package main
2+
3+
type S struct {
4+
foo string
5+
}
6+
7+
func main() {
8+
var s S
9+
s.foo <caret>= "bar"
10+
print(s.foo)
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package main
2+
3+
type S struct {
4+
foo string
5+
}
6+
7+
func main() {
8+
var s, b S
9+
s.foo <caret>= "bar"
10+
print(s.foo)
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package main
2+
3+
type S struct {
4+
foo string
5+
}
6+
7+
func main() {
8+
var s S = S{foo: "bar"}
9+
10+
print(s.foo)
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package main
2+
3+
type S struct {
4+
foo string
5+
}
6+
7+
func main() {
8+
var s S = S{}
9+
s.foo <caret>= "bar"
10+
print(s.foo)
11+
}

0 commit comments

Comments
 (0)