Merge remote-tracking branch 'origin/master'

This commit is contained in:
Dmitry Batkovich
2019-02-07 13:35:38 +03:00
28 changed files with 314 additions and 90 deletions
@@ -69,10 +69,13 @@ public class FieldCanBeLocalInspection extends AbstractBaseJavaLocalInspectionTo
if (candidates.isEmpty()) return;
final List<ImplicitUsageProvider> implicitUsageProviders = ImplicitUsageProvider.EP_NAME.getExtensionList();
PsiClass scope = PsiTreeUtil.getTopmostParentOfType(aClass, PsiClass.class);
if (scope == null) scope = aClass;
FieldLoop:
for (final PsiField field : candidates) {
if (usedFields.contains(field) && !hasImplicitReadOrWriteUsage(field, implicitUsageProviders)) {
final Query<PsiReference> references = ReferencesSearch.search(field, new LocalSearchScope(aClass));
final Query<PsiReference> references = ReferencesSearch.search(field, new LocalSearchScope(scope));
final Map<PsiCodeBlock, Collection<PsiReference>> refs = new HashMap<>();
for (PsiReference reference : references.findAll()) {
final PsiElement element = reference.getElement();
@@ -151,6 +154,11 @@ public class FieldCanBeLocalInspection extends AbstractBaseJavaLocalInspectionTo
//do not go inside class initializer
}
@Override
public void visitLambdaExpression(PsiLambdaExpression expression) {
// do not go inside lambda
}
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
excludeFieldCandidate(expression);
@@ -295,7 +303,7 @@ public class FieldCanBeLocalInspection extends AbstractBaseJavaLocalInspectionTo
private static boolean groupReferenceByCodeBlocks(Map<PsiCodeBlock, Collection<PsiReference>> refs, PsiReference psiReference) {
final PsiElement element = psiReference.getElement();
final PsiCodeBlock block = PsiTreeUtil.getTopmostParentOfType(element, PsiCodeBlock.class);
final PsiCodeBlock block = getTopmostBlock(element);
if (block == null) {
return false;
}
@@ -310,6 +318,17 @@ public class FieldCanBeLocalInspection extends AbstractBaseJavaLocalInspectionTo
return true;
}
@Nullable
private static PsiCodeBlock getTopmostBlock(@NotNull PsiElement element) {
PsiElement parent = element.getParent();
PsiCodeBlock block = null;
while (parent != null && !(parent instanceof PsiClass)) {
if (parent instanceof PsiCodeBlock) block = (PsiCodeBlock)parent;
parent = parent.getParent();
}
return block;
}
private static boolean findExistentBlock(Map<PsiCodeBlock, Collection<PsiReference>> refs,
PsiReference psiReference,
PsiCodeBlock block,
@@ -365,8 +384,29 @@ public class FieldCanBeLocalInspection extends AbstractBaseJavaLocalInspectionTo
return new JavaElementVisitor() {
@Override
public void visitJavaFile(PsiJavaFile file) {
final JavaElementVisitor visitor = new JavaElementVisitor() {
@Override
public void visitClass(PsiClass aClass) {
super.visitClass(aClass);
Arrays.stream(aClass.getChildren()).forEach(c -> c.accept(this));
doCheckClass(aClass, holder, EXCLUDE_ANNOS, IGNORE_FIELDS_USED_IN_MULTIPLE_METHODS);
}
@Override
public void visitDeclarationStatement(PsiDeclarationStatement statement) {
super.visitDeclarationStatement(statement);
Arrays.stream(statement.getDeclaredElements()).forEach(d -> d.accept(this));
}
@Override
public void visitMethod(PsiMethod method) {
super.visitMethod(method);
final PsiCodeBlock body = method.getBody();
if (body != null) Arrays.stream(body.getChildren()).forEach(c -> c.accept(this));
}
};
for (PsiClass aClass : file.getClasses()) {
doCheckClass(aClass, holder, EXCLUDE_ANNOS, IGNORE_FIELDS_USED_IN_MULTIPLE_METHODS);
aClass.accept(visitor);
}
}
};
@@ -0,0 +1,12 @@
// "Convert field to local variable in method 'test'" "true"
class Foo {
void test() {
class Bar {
void test() {
int x = 2; // could be local
System.out.println(x);
}
}
}
}
@@ -0,0 +1,19 @@
// "Convert to local" "true"
class Outer {
void test() {
class Local {
void foo() {
String s = "1";
System.out.println(s);
}
void bar() {
String s = "2";
System.out.println(s);
}
}
}
}
@@ -0,0 +1,10 @@
// "Convert field to local variable in method 'test'" "true"
class Foo {
static class Bar {
void test() {
int x = 2; // could be local
System.out.println(x);
}
}
}
@@ -0,0 +1,7 @@
// "Convert to local" "true"
class Test {
private Runnable r = () -> {
String field = "foo";
}
}
@@ -0,0 +1,15 @@
// "Fix all 'Field can be local' problems in file" "false"
class Outer {
void test(Inner inner) {
System.out.println(inner.field);
}
class Inner {
private final String f<caret>ield;
Inner(String field) {
this.field = field;
}
}
}
@@ -0,0 +1,13 @@
// "Convert field to local variable in method 'test'" "true"
class Foo {
void test() {
class Bar {
private int <caret>x;
void test() {
x = 2; // could be local
System.out.println(x);
}
}
}
}
@@ -0,0 +1,20 @@
// "Convert to local" "true"
class Outer {
void test() {
class Local {
private String <caret>s;
void foo() {
s = "1";
System.out.println(s);
}
void bar() {
s = "2";
System.out.println(s);
}
}
}
}
@@ -0,0 +1,11 @@
// "Convert field to local variable in method 'test'" "true"
class Foo {
static class Bar {
private int <caret>x;
void test() {
x = 2; // could be local
System.out.println(x);
}
}
}
@@ -0,0 +1,9 @@
// "Convert to local" "true"
class Test {
private String <caret>field;
private Runnable r = () -> {
field = "foo";
}
}
@@ -2,7 +2,7 @@
private int value = 0;
public class Inner {
private final int myValue;
private final int <warning descr="Field can be converted to a local variable">myValue</warning>;
public Inner() {
myValue = value++;
@@ -2,7 +2,7 @@
private int <warning descr="Field can be converted to a local variable">value</warning> = 0;
public class Inner {
private final int myValue;
private final int <warning descr="Field can be converted to a local variable">myValue</warning>;
public Inner() {
myValue = value;
@@ -438,7 +438,7 @@
</component>
</children>
</grid>
<grid id="7bb4" layout-manager="GridLayoutManager" row-count="3" column-count="3" same-size-horizontally="false" same-size-vertically="true" hgap="-1" vgap="0">
<grid id="7bb4" layout-manager="GridLayoutManager" row-count="3" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="0">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="1" column="0" row-span="2" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
@@ -486,7 +486,7 @@
<text resource-bundle="messages/ApplicationBundle" key="label.use.custom.soft.wraps.indent"/>
</properties>
</component>
<grid id="8800e" layout-manager="GridLayoutManager" row-count="1" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<grid id="8800e" layout-manager="GridLayoutManager" row-count="2" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<grid row="0" column="0" row-span="1" col-span="3" vsize-policy="3" hsize-policy="0" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
@@ -503,12 +503,18 @@
<text resource-bundle="messages/ApplicationBundle" key="checkbox.use.soft.wraps.at.editor"/>
</properties>
</component>
<component id="2a016" class="com.intellij.ui.components.JBTextField" binding="mySoftWrapFileMasks">
<component id="2a016" class="com.intellij.ui.components.JBTextField" binding="mySoftWrapFileMasks" custom-create="true">
<constraints>
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
<component id="ed230" class="javax.swing.JLabel" binding="mySoftWrapFileMasksHint" custom-create="true">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties/>
</component>
</children>
</grid>
</children>
@@ -28,6 +28,7 @@ import com.intellij.openapi.options.SearchableConfigurable;
import com.intellij.openapi.options.ex.ConfigurableWrapper;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.ui.panel.ComponentPanelBuilder;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.openapi.util.text.StringUtil;
@@ -39,6 +40,7 @@ import com.intellij.ui.ListCellRendererWrapper;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBTextField;
import com.intellij.util.ui.JBEmptyBorder;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
@@ -99,6 +101,7 @@ public class EditorOptionsPanel extends CompositeConfigurable<ErrorOptionsProvid
private JCheckBox myCbKeepTrailingSpacesOnCaretLine;
private JTextField myRecentLocationsLimitField;
private JBTextField mySoftWrapFileMasks;
private JLabel mySoftWrapFileMasksHint;
private static final String ACTIVE_COLOR_SCHEME = ApplicationBundle.message("combobox.richcopy.color.scheme.active");
private static final UINumericRange RECENT_FILES_RANGE = new UINumericRange(50, 1, 500);
@@ -377,6 +380,12 @@ public class EditorOptionsPanel extends CompositeConfigurable<ErrorOptionsProvid
ApplicationManager.getApplication().getMessageBus().syncPublisher(EditorOptionsListener.OPTIONS_PANEL_TOPIC).changesApplied();
}
private void createUIComponents() {
mySoftWrapFileMasks = new JBTextField();
mySoftWrapFileMasksHint = ComponentPanelBuilder.createCommentComponent(ApplicationBundle.message("soft.wraps.file.masks.hint"), true);
mySoftWrapFileMasksHint.setBorder(new JBEmptyBorder(ComponentPanelBuilder.computeCommentInsets(mySoftWrapFileMasks, true)));
}
private static boolean setRecentLocationLimit(@NotNull UISettings uiSettings, @NotNull String recentLocationsLimit) {
try {
int newRecentLocationsLimit = Integer.parseInt(recentLocationsLimit.trim());
@@ -175,9 +175,6 @@ class ITNProxy {
HttpURLConnection connection = post(new URL(NEW_THREAD_POST_URL), createRequest(login, password, error));
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_BAD_REQUEST && StringUtil.isEmpty(password)) {
throw new NoSuchEAPUserException(login);
}
if (responseCode != HttpURLConnection.HTTP_OK) {
throw new InternalEAPException(DiagnosticBundle.message("error.http.result.code", responseCode));
}
@@ -432,8 +432,9 @@ radio.close.less.frequently.used.files=Close unused
radio.close.non.modified.files.first=Close unchanged
label.when.number.of.opened.editors.exceeds.tab.limit=<html>When tabs exceed the limit:</html>
group.soft.wraps=Soft Wraps
checkbox.use.soft.wraps.at.editor=Soft-wrap file types:
soft.wraps.file.masks.empty.text=No file types
checkbox.use.soft.wraps.at.editor=Soft-wrap files:
soft.wraps.file.masks.empty.text=No files
soft.wraps.file.masks.hint=Use ; to separate patterns, * and ? as wildcards
checkbox.use.soft.wraps.at.console=Use soft wraps in console
checkbox.use.custom.soft.wraps.indent=Use original line's indent for wrapped parts
label.use.custom.soft.wraps.indent=Additional shift:
@@ -1579,10 +1579,6 @@ typescript.service.node.arguments=
typescript.service.node.arguments.description=Node options to use when starting TypeScript service process
javascript.exclude.source.if.package.has.typings=true
javascript.exclude.source.if.package.has.typings.description=Don't index JavaScript files in a node module if it has type declaration files
javascript.flow.lazy.mode=false
javascript.flow.lazy.mode.description=Passes '--lazy-mode fs' to flow server command
javascript.flow.use.lsp=false
javascript.flow.use.lsp.description=Enables using Language Server Protocol for communication with Flow
javascript.linters.prevent.detection=false
javascript.linters.prevent.enable.on.project.opening.description=Disables JS linter detection on project opening
@@ -1,10 +1,12 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.groovy.lang.psi.api;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrParameterListOwner;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
@@ -17,4 +19,10 @@ public interface GrFunctionalExpression extends GrExpression, GrParameterListOwn
@Nullable
PsiType getReturnType();
@NotNull
GrStatement[] getStatements();
@Nullable
PsiElement getArrow();
}
@@ -1,10 +1,19 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.groovy.lang.psi.api;
import com.intellij.psi.PsiType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner;
/**
* Represents a Groovy lambda expression body.
*/
public interface GrLambdaBody extends GrControlFlowOwner {
@NotNull
GrLambdaExpression getLambdaExpression();
@Nullable
PsiType getReturnType();
}
@@ -1,6 +1,8 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.groovy.lang.psi.api;
import com.intellij.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
@@ -14,4 +16,8 @@ public interface GrLambdaExpression extends GrFunctionalExpression {
*/
@Nullable
GrLambdaBody getBody();
@Override
@NotNull
PsiElement getArrow();
}
@@ -40,6 +40,7 @@ public interface GrClosableBlock extends GrFunctionalExpression, GrCodeBlock {
@Override
GrParameter[] getAllParameters();
@Override
@Nullable
PsiElement getArrow();
@@ -23,6 +23,7 @@ import org.jetbrains.annotations.Nullable;
* @author ven
*/
public interface Instruction {
Instruction[] EMPTY_ARRAY = new Instruction[0];
@NotNull
Iterable<Instruction> successors(@NotNull CallEnvironment environment);
@@ -17,11 +17,11 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor;
import org.jetbrains.plugins.groovy.lang.psi.api.GrInExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.GrTryResourceList;
import org.jetbrains.plugins.groovy.lang.psi.api.*;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrCondition;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.*;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
@@ -99,8 +99,9 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
final PsiElement lbrace = block.getLBrace();
if (lbrace != null && parent instanceof GrMethod) {
for (GrParameter parameter : ((GrMethod)parent).getParameters()) {
if (myPolicy.isVariableInitialized(parameter)) {
addNode(new ReadWriteVariableInstruction(parameter.getName(), parameter, ReadWriteVariableInstruction.WRITE));
String parameterName = parameter.getName();
if (myPolicy.isVariableInitialized(parameter) && parameterName != null) {
addNode(new ReadWriteVariableInstruction(parameterName, parameter, ReadWriteVariableInstruction.WRITE));
}
}
}
@@ -114,6 +115,12 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
}
}
@Override
public void visitLambdaBody(@NotNull GrLambdaBody body) {
addFunctionalExpressionParameters(body.getLambdaExpression());
addControlFlowInstructions(body);
}
@Override
public void visitFile(@NotNull GroovyFileBase file) {
super.visitFile(file);
@@ -133,14 +140,20 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
}
public Instruction[] buildControlFlow(GroovyPsiElement scope) {
if (scope instanceof GrLambdaExpression) {
GrLambdaBody body = ((GrLambdaExpression)scope).getBody();
return body != null ? body.getControlFlow() : Instruction.EMPTY_ARRAY;
}
myFinallyCount = 0;
myInstructionNumber = 0;
myScope = scope;
startNode(null);
if (scope instanceof GrClosableBlock) {
buildFlowForClosure((GrClosableBlock)scope);
addFunctionalExpressionParameters((GrFunctionalExpression)scope);
addControlFlowInstructions((GrControlFlowOwner)scope);
}
else {
scope.accept(this);
@@ -150,7 +163,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
checkPending(end); //collect return edges
return assertValidPsi(myInstructions.toArray(new Instruction[0]));
return assertValidPsi(myInstructions.toArray(Instruction.EMPTY_ARRAY));
}
public static Instruction[] assertValidPsi(Instruction[] instructions) {
@@ -163,16 +176,9 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
return instructions;
}
private void buildFlowForClosure(final GrClosableBlock closure) {
for (GrParameter parameter : closure.getAllParameters()) {
if (myPolicy.isVariableInitialized(parameter)) {
addNode(new ReadWriteVariableInstruction(parameter.getName(), parameter, ReadWriteVariableInstruction.WRITE));
}
}
addNode(new ReadWriteVariableInstruction("owner", closure.getLBrace(), ReadWriteVariableInstruction.WRITE));
PsiElement child = closure.getFirstChild();
private void addControlFlowInstructions(final GrControlFlowOwner owner) {
PsiElement child = owner.getFirstChild();
while (child != null) {
if (child instanceof GroovyPsiElement) {
((GroovyPsiElement)child).accept(this);
@@ -180,12 +186,25 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
child = child.getNextSibling();
}
final GrStatement[] statements = closure.getStatements();
final GrStatement[] statements = owner.getStatements();
if (statements.length > 0) {
handlePossibleReturn(statements[statements.length - 1]);
}
}
private void addFunctionalExpressionParameters(GrFunctionalExpression expression) {
for (GrParameter parameter : expression.getAllParameters()) {
String parameterName = parameter.getName();
if (myPolicy.isVariableInitialized(parameter) && parameterName != null) {
addNode(new ReadWriteVariableInstruction(parameterName, parameter, ReadWriteVariableInstruction.WRITE));
}
}
PsiElement anchor = expression.getArrow();
if (expression instanceof GrClosableBlock) anchor = ((GrClosableBlock)expression).getLBrace();
addNode(new ReadWriteVariableInstruction("owner", anchor, ReadWriteVariableInstruction.WRITE));
}
private <T extends InstructionImpl> T addNode(T instruction) {
instruction.setNumber(myInstructionNumber++);
myInstructions.add(instruction);
@@ -211,30 +230,37 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
}
}
@Override
public void visitLambdaExpression(@NotNull GrLambdaExpression expression) {
GrLambdaBody body = expression.getBody();
if (body == null) return;
ReadWriteVariableInstruction[] reads = ControlFlowBuilderUtil.getReadsWithoutPriorWrites(body.getControlFlow(), false);
addReadFromNestedControlFlow(expression, reads);
}
@Override
public void visitClosure(@NotNull GrClosableBlock closure) {
//do not go inside closures except gstring injections
if (closure.getParent() instanceof GrStringInjection) {
for (GrParameter parameter : closure.getAllParameters()) {
if (myPolicy.isVariableInitialized(parameter)) {
addNode(new ReadWriteVariableInstruction(parameter.getName(), parameter, ReadWriteVariableInstruction.WRITE));
}
}
addNode(new ReadWriteVariableInstruction("owner", closure.getLBrace(), ReadWriteVariableInstruction.WRITE));
addFunctionalExpressionParameters(closure);
super.visitClosure(closure);
return;
}
ReadWriteVariableInstruction[] reads = ControlFlowBuilderUtil.getReadsWithoutPriorWrites(closure.getControlFlow(), false);
addReadFromNestedControlFlow(closure, reads);
}
private void addReadFromNestedControlFlow(@NotNull PsiElement anchor, @NotNull ReadWriteVariableInstruction[] reads) {
for (ReadWriteVariableInstruction read : reads) {
PsiElement element = read.getElement();
if (!(element instanceof GrReferenceExpression) || myPolicy.isReferenceAccepted((GrReferenceExpression)element)) {
addNodeAndCheckPending(new ReadWriteVariableInstruction(read.getVariableName(), closure, ReadWriteVariableInstruction.READ));
addNodeAndCheckPending(new ReadWriteVariableInstruction(read.getVariableName(), anchor, ReadWriteVariableInstruction.READ));
}
}
addNodeAndCheckPending(new InstructionImpl(closure));
addNodeAndCheckPending(new InstructionImpl(anchor));
}
@Override
@@ -1186,13 +1212,7 @@ public class ControlFlowBuilder extends GroovyRecursiveElementVisitor {
final Set<ReadWriteVariableInstruction> vars = collectUsedVariableWithoutInitialization(typeDefinition);
for (ReadWriteVariableInstruction var : vars) {
PsiElement element = var.getElement();
if (!(element instanceof GrReferenceExpression) || myPolicy.isReferenceAccepted((GrReferenceExpression)element)) {
addNodeAndCheckPending(new ReadWriteVariableInstruction(var.getVariableName(), typeDefinition, ReadWriteVariableInstruction.READ));
}
}
addNodeAndCheckPending(new InstructionImpl(typeDefinition));
addReadFromNestedControlFlow(typeDefinition, vars.toArray(ReadWriteVariableInstruction.EMPTY_ARRAY));
}
private static Set<ReadWriteVariableInstruction> collectUsedVariableWithoutInitialization(GrTypeDefinition typeDefinition) {
@@ -1,30 +0,0 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.groovy.lang.psi.impl;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
import org.jetbrains.plugins.groovy.lang.psi.api.GrLambdaBody;
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.blocks.GrBlockImpl;
public class GrLambdaBodyBlockImpl extends GrBlockImpl implements GrLambdaBody {
public GrLambdaBodyBlockImpl(@NotNull IElementType type, CharSequence buffer) {
super(type, buffer);
}
@Override
public void accept(@NotNull GroovyElementVisitor visitor) {
visitor.visitLambdaBody(this);
}
@Override
public String toString() {
return "Lambda body block";
}
@Override
public boolean isTopControlFlowOwner() {
return true;
}
}
@@ -0,0 +1,24 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.plugins.groovy.lang.psi.impl
import com.intellij.psi.PsiType
import com.intellij.psi.tree.IElementType
import com.intellij.psi.util.parentOfType
import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor
import org.jetbrains.plugins.groovy.lang.psi.api.GrLambdaBody
import org.jetbrains.plugins.groovy.lang.psi.api.GrLambdaExpression
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.blocks.GrBlockImpl
import org.jetbrains.plugins.groovy.lang.resolve.MethodTypeInferencer
class GrLambdaBodyBlockImpl(type: IElementType, buffer: CharSequence?) : GrBlockImpl(type, buffer), GrLambdaBody {
override fun getReturnType(): PsiType? = GroovyPsiManager.inferType(this, MethodTypeInferencer(this))
override fun isTopControlFlowOwner(): Boolean = true
override fun getLambdaExpression(): GrLambdaExpression = requireNotNull(parentOfType())
override fun accept(visitor: GroovyElementVisitor) = visitor.visitLambdaBody(this)
override fun toString(): String = "Lambda body block"
}
@@ -2,15 +2,21 @@
package org.jetbrains.plugins.groovy.lang.psi.impl
import com.intellij.lang.ASTNode
import com.intellij.psi.PsiType
import com.intellij.psi.util.parentOfType
import com.intellij.util.IncorrectOperationException
import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor
import org.jetbrains.plugins.groovy.lang.psi.api.GrLambdaBody
import org.jetbrains.plugins.groovy.lang.psi.api.GrLambdaExpression
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.Instruction
import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.ControlFlowBuilder
class GrLambdaBodyExpressionImpl(node: ASTNode) : GroovyPsiElementImpl(node), GrLambdaBody {
override fun getReturnType(): PsiType? = getExpression().type
override fun getLambdaExpression(): GrLambdaExpression = requireNotNull(parentOfType())
fun getExpression() : GrExpression = findNotNullChildByClass(GrExpression::class.java)
@@ -9,20 +9,19 @@ import com.intellij.psi.scope.PsiScopeProcessor
import com.intellij.psi.util.CachedValueProvider.Result.create
import com.intellij.psi.util.CachedValuesManager.getCachedValue
import com.intellij.psi.util.PsiModificationTracker
import org.jetbrains.plugins.groovy.lang.psi.GroovyElementTypes
import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor
import org.jetbrains.plugins.groovy.lang.psi.api.GrLambdaBody
import org.jetbrains.plugins.groovy.lang.psi.api.GrLambdaExpression
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameterList
import org.jetbrains.plugins.groovy.lang.psi.dataFlow.types.TypeInferenceHelper
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrExpressionImpl
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.params.GrParameterListImpl
class GrLambdaExpressionImpl(node: ASTNode) : GrExpressionImpl(node), GrLambdaExpression {
override fun getParameters(): Array<GrParameter> {
return parameterList.parameters
}
override fun getParameters(): Array<GrParameter> = parameterList.parameters
override fun getParameterList(): GrParameterList = findNotNullChildByClass(GrParameterListImpl::class.java)
@@ -47,11 +46,13 @@ class GrLambdaExpressionImpl(node: ASTNode) : GrExpressionImpl(node), GrLambdaEx
}
}
override fun getReturnType(): PsiType? = TypeInferenceHelper.getCurrentContext().getExpressionType(this, ::calculateReturnType)
override fun getArrow(): PsiElement = findNotNullChildByType(GroovyElementTypes.T_ARROW)
override fun getType(): PsiType? {
return GrClosureType.create(this, true)
}
override fun getStatements(): Array<GrStatement> = body?.statements ?: emptyArray()
override fun getReturnType(): PsiType? = body?.returnType
override fun getType(): PsiType? = GrClosureType.create(this, true)
override fun toString(): String = "Lambda expression"
}
@@ -34,6 +34,7 @@ import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement;
import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor;
import org.jetbrains.plugins.groovy.lang.psi.api.GrLambdaBody;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrCodeBlock;
@@ -88,6 +89,13 @@ public class GrReassignedLocalVarsChecker {
}
}
@Override
public void visitLambdaBody(@NotNull GrLambdaBody body) {
if (getUsedVarsInsideBlock(body).contains(name)) {
isReassigned.set(true);
}
}
@Override
public void visitElement(@NotNull GroovyPsiElement element) {
if (isReassigned.get()) return;
@@ -144,11 +152,11 @@ public class GrReassignedLocalVarsChecker {
}
@NotNull
private static Set<String> getUsedVarsInsideBlock(@NotNull final GrCodeBlock block) {
return CachedValuesManager.getCachedValue(block, () -> {
private static Set<String> getUsedVarsInsideBlock(@NotNull final GroovyPsiElement element) {
return CachedValuesManager.getCachedValue(element, () -> {
final Set<String> result = ContainerUtil.newHashSet();
block.acceptChildren(new GroovyRecursiveElementVisitor() {
element.acceptChildren(new GroovyRecursiveElementVisitor() {
@Override
public void visitOpenBlock(@NotNull GrOpenBlock openBlock) {
@@ -160,6 +168,11 @@ public class GrReassignedLocalVarsChecker {
result.addAll(getUsedVarsInsideBlock(closure));
}
@Override
public void visitLambdaBody(@NotNull GrLambdaBody body) {
result.addAll(getUsedVarsInsideBlock(body));
}
@Override
public void visitReferenceExpression(@NotNull GrReferenceExpression referenceExpression) {
if (referenceExpression.getQualifier() == null && referenceExpression.getReferenceName() != null) {
@@ -167,7 +180,7 @@ public class GrReassignedLocalVarsChecker {
}
}
});
return CachedValueProvider.Result.create(result, block);
return CachedValueProvider.Result.create(result, element);
});
}