Skip to content

Commit fdb56e7

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 fdb56e7

14 files changed

+244
-28
lines changed

src/com/goide/intentions/GoMoveToStructInitializationIntention.java

Lines changed: 107 additions & 28 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
@@ -86,8 +104,8 @@ private static GoReferenceExpression getFieldReferenceExpression(@NotNull PsiEle
86104
List<GoReferenceExpression> fieldReferenceExpressions = getFieldReferenceExpressions(assignment);
87105
if (exists(fieldReferenceExpressions, expression -> isAssignedInPreviousStatement(expression, assignment))) return null;
88106

89-
Set<PsiElement> resolvedFields = map2Set(fieldReferenceExpressions, GoMoveToStructInitializationIntention::resolveQualifier);
90-
return resolvedFields.size() == 1 ? getFirstItem(fieldReferenceExpressions) : null;
107+
Set<PsiElement> resolvedQualifiers = map2Set(fieldReferenceExpressions, GoMoveToStructInitializationIntention::resolveQualifier);
108+
return resolvedQualifiers.size() == 1 ? getFirstItem(fieldReferenceExpressions) : null;
91109
}
92110

93111
@NotNull
@@ -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());
@@ -129,20 +163,19 @@ private static GoExpression getTopmostExpression(@NotNull GoExpression expressio
129163
return ObjectUtils.notNull(PsiTreeUtil.getTopmostParentOfType(expression, GoExpression.class), expression);
130164
}
131165

132-
private static boolean isResolvedTo(@Nullable PsiElement e, @Nullable PsiElement resolve) {
133-
if (e instanceof GoVarDefinition) return resolve == e;
166+
private static boolean isResolvedTo(@Nullable PsiElement element, @Nullable PsiElement resolve) {
167+
if (element instanceof GoVarDefinition) return resolve == element;
134168

135-
GoReferenceExpression refExpression = unwrapParensAndCast(e);
169+
GoReferenceExpression refExpression = unwrapParensAndCast(element);
136170
return refExpression != null && refExpression.resolve() == resolve;
137171
}
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,17 @@ 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;
264+
219265
GoLiteralValue literalValue = structLiteral.getLiteralValue();
220266
PsiElement resolve = fieldReferenceExpression.resolve();
221267
return literalValue != null && isFieldDefinition(resolve) &&
@@ -238,19 +284,31 @@ private static List<? extends PsiElement> getLeftHandElements(@NotNull GoStateme
238284
public void invoke(@NotNull Project project, Editor editor, @NotNull PsiElement element) throws IncorrectOperationException {
239285
Data data = getData(element);
240286
if (data == null) return;
241-
moveFieldReferenceExpressions(data);
287+
288+
boolean needReplaceDeclarationWithShortVar = data.getCompositeLit() == null;
289+
GoCompositeLit compositeLit =
290+
needReplaceDeclarationWithShortVar ? createStructLiteral(data.getStructDefinition(), project) : data.getCompositeLit();
291+
if (compositeLit == null || needReplaceDeclarationWithShortVar && data.getStructDeclaration() == null) return;
292+
293+
moveFieldReferenceExpressions(data.getReferenceExpressions(), compositeLit, data.getAssignment());
294+
if (!needReplaceDeclarationWithShortVar) return;
295+
GoStatement shortVarStatement =
296+
GoElementFactory.createShortVarDeclarationStatement(project, data.getStructDefinition().getText(), compositeLit.getText());
297+
data.getStructDeclaration().replace(shortVarStatement);
242298
}
243299

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

248-
for (GoReferenceExpression expression : data.getReferenceExpressions()) {
306+
for (GoReferenceExpression expression : referenceExpressions) {
249307
GoExpression anchor = getTopmostExpression(expression);
250-
GoExpression fieldValue = GoPsiImplUtil.getRightExpression(data.getAssignment(), anchor);
308+
GoExpression fieldValue = GoPsiImplUtil.getRightExpression(parentAssignment, anchor);
251309
if (fieldValue == null) continue;
252310

253-
GoPsiImplUtil.deleteExpressionFromAssignment(data.getAssignment(), anchor);
311+
GoPsiImplUtil.deleteExpressionFromAssignment(parentAssignment, anchor);
254312
addFieldDefinition(literalValue, expression.getIdentifier().getText(), fieldValue.getText());
255313
}
256314
}
@@ -272,25 +330,46 @@ private static class Data {
272330
private final GoCompositeLit myCompositeLit;
273331
private final GoAssignmentStatement myAssignment;
274332
private final List<GoReferenceExpression> myReferenceExpressions;
333+
private final GoStatement myStructDeclaration;
334+
private final GoVarDefinition myStructDefinition;
275335

276336
public Data(@NotNull GoAssignmentStatement assignment,
277-
@NotNull GoCompositeLit compositeLit,
278-
@NotNull List<GoReferenceExpression> referenceExpressions) {
337+
@Nullable GoCompositeLit compositeLit,
338+
@NotNull List<GoReferenceExpression> referenceExpressions,
339+
@Nullable GoStatement structDeclaration,
340+
@NotNull GoVarDefinition structDefinition) {
279341
myCompositeLit = compositeLit;
280342
myAssignment = assignment;
281343
myReferenceExpressions = referenceExpressions;
344+
myStructDeclaration = structDeclaration;
345+
myStructDefinition = structDefinition;
282346
}
283347

348+
@Nullable
284349
public GoCompositeLit getCompositeLit() {
285350
return myCompositeLit;
286351
}
287352

353+
@NotNull
288354
public GoAssignmentStatement getAssignment() {
289355
return myAssignment;
290356
}
291357

358+
@NotNull
292359
public List<GoReferenceExpression> getReferenceExpressions() {
293360
return myReferenceExpressions;
294361
}
362+
363+
@Nullable
364+
public GoStatement getStructDeclaration() {
365+
return myStructDeclaration;
366+
}
367+
368+
@NotNull
369+
public GoVarDefinition getStructDefinition() {
370+
return myStructDefinition;
371+
}
295372
}
296373
}
374+
375+

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)