[java] adds support for Java 9's improved try-with-resources (IDEA-140266)

PSI, parser, highlighting, exception analysis, control flow, completion.
This commit is contained in:
Roman Shevchenko
2015-07-22 16:28:39 +02:00
parent 6296977fea
commit f7f0a5c0c4
33 changed files with 491 additions and 86 deletions
@@ -770,16 +770,15 @@ public class HighlightUtil extends HighlightUtilBase {
}
@Nullable
static HighlightInfo checkUnhandledCloserExceptions(@NotNull final PsiResourceVariable resource) {
final List<PsiClassType> unhandled = ExceptionUtil.getUnhandledCloserExceptions(resource, null);
static HighlightInfo checkUnhandledCloserExceptions(@NotNull PsiResourceListElement resource) {
List<PsiClassType> unhandled = ExceptionUtil.getUnhandledCloserExceptions(resource, null);
if (unhandled.isEmpty()) return null;
final HighlightInfoType highlightType = getUnhandledExceptionHighlightType(resource);
HighlightInfoType highlightType = getUnhandledExceptionHighlightType(resource);
if (highlightType == null) return null;
final String description = getUnhandledExceptionsDescriptor(unhandled, "auto-closeable resource");
final HighlightInfo highlight =
HighlightInfo.newHighlightInfo(highlightType).range(resource).descriptionAndTooltip(description).create();
String description = getUnhandledExceptionsDescriptor(unhandled, "auto-closeable resource");
HighlightInfo highlight = HighlightInfo.newHighlightInfo(highlightType).range(resource).descriptionAndTooltip(description).create();
registerUnhandledExceptionFixes(resource, highlight, unhandled);
return highlight;
}
@@ -1715,15 +1714,49 @@ public class HighlightUtil extends HighlightUtilBase {
}
@Nullable
static HighlightInfo checkTryResourceIsAutoCloseable(@NotNull final PsiResourceVariable resource) {
final PsiType type = resource.getType();
final PsiElementFactory factory = JavaPsiFacade.getInstance(resource.getProject()).getElementFactory();
final PsiClassType autoCloseable = factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE, resource.getResolveScope());
static HighlightInfo checkTryResourceIsAutoCloseable(@NotNull PsiResourceListElement resource) {
PsiType type = resource.getType();
if (type == null) return null;
PsiElementFactory factory = JavaPsiFacade.getInstance(resource.getProject()).getElementFactory();
PsiClassType autoCloseable = factory.createTypeByFQClassName(CommonClassNames.JAVA_LANG_AUTO_CLOSEABLE, resource.getResolveScope());
if (TypeConversionUtil.isAssignable(autoCloseable, type)) return null;
return createIncompatibleTypeHighlightInfo(autoCloseable, type, resource.getTextRange(), 0);
}
@Nullable
static HighlightInfo checkResourceVariableIsFinal(@NotNull PsiResourceExpression resource) {
PsiExpression expression = resource.getExpression();
if (expression instanceof PsiThisExpression) return null;
if (expression instanceof PsiReferenceExpression) {
PsiElement target = ((PsiReferenceExpression)expression).resolve();
if (target == null) return null;
if (target instanceof PsiVariable) {
PsiVariable variable = (PsiVariable)target;
PsiModifierList modifierList = variable.getModifierList();
if (modifierList != null && modifierList.hasModifierProperty(PsiModifier.FINAL)) return null;
PsiElement scope = null;
if (variable instanceof PsiParameter) scope = ((PsiParameter)variable).getDeclarationScope();
else if (variable instanceof PsiResourceVariable) scope = variable.getParent().getParent();
else if (variable instanceof PsiLocalVariable) scope = PsiTreeUtil.getParentOfType(variable, PsiCodeBlock.class);
if (scope != null) {
if (HighlightControlFlowUtil.isEffectivelyFinal(variable, scope, null)) return null;
}
}
String text = JavaErrorMessages.message("resource.variable.must.be.final");
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(text).create();
}
return null;
}
@Nullable
static Collection<HighlightInfo> checkArrayInitializer(final PsiExpression initializer, PsiType type) {
if (!(initializer instanceof PsiArrayInitializerExpression)) return null;
@@ -2913,7 +2946,8 @@ public class HighlightUtil extends HighlightUtilBase {
METHOD_REFERENCES(LanguageLevel.JDK_1_8, "feature.method.references"),
LAMBDA_EXPRESSIONS(LanguageLevel.JDK_1_8, "feature.lambda.expressions"),
TYPE_ANNOTATIONS(LanguageLevel.JDK_1_8, "feature.type.annotations"),
RECEIVERS(LanguageLevel.JDK_1_8, "feature.type.receivers");
RECEIVERS(LanguageLevel.JDK_1_8, "feature.type.receivers"),
REFS_AS_RESOURCE(LanguageLevel.JDK_1_9, "feature.try.with.resources.refs");
private final LanguageLevel level;
private final String key;
@@ -1482,10 +1482,19 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
}
@Override
public void visitResourceVariable(PsiResourceVariable variable) {
super.visitResourceVariable(variable);
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkTryResourceIsAutoCloseable(variable));
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkUnhandledCloserExceptions(variable));
public void visitResourceVariable(PsiResourceVariable resource) {
super.visitResourceVariable(resource);
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkTryResourceIsAutoCloseable(resource));
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkUnhandledCloserExceptions(resource));
}
@Override
public void visitResourceExpression(PsiResourceExpression resource) {
super.visitResourceExpression(resource);
if (!myHolder.hasErrorResults()) myHolder.add(checkFeature(resource, Feature.REFS_AS_RESOURCE));
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkResourceVariableIsFinal(resource));
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkTryResourceIsAutoCloseable(resource));
if (!myHolder.hasErrorResults()) myHolder.add(HighlightUtil.checkUnhandledCloserExceptions(resource));
}
@Override
@@ -350,8 +350,10 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
else if (parent instanceof PsiTryStatement) {
PsiResourceList list = ((PsiTryStatement)parent).getResourceList();
if (list != null) {
for (PsiResourceVariable variable : list.getResourceVariables()) {
myCurrentFlow.removeVariable(variable);
for (PsiResourceListElement resource : list) {
if (resource instanceof PsiResourceVariable) {
myCurrentFlow.removeVariable((PsiVariable)resource);
}
}
}
}
@@ -1029,12 +1031,19 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
@Override
public void visitResourceList(PsiResourceList resourceList) {
for (PsiResourceVariable variable : resourceList.getResourceVariables()) {
PsiExpression initializer = variable.getInitializer();
if (initializer != null) {
initializeVariable(variable, initializer);
for (PsiResourceListElement resource : resourceList) {
if (resource instanceof PsiResourceVariable) {
PsiResourceVariable variable = (PsiResourceVariable)resource;
PsiExpression initializer = variable.getInitializer();
if (initializer != null) {
initializeVariable(variable, initializer);
}
}
PsiMethod closer = PsiUtil.getResourceCloserMethod(variable);
else if (resource instanceof PsiResourceExpression) {
((PsiResourceExpression)resource).getExpression().accept(this);
}
PsiMethod closer = PsiUtil.getResourceCloserMethod(resource);
if (closer != null) {
addMethodThrows(closer, null);
}
@@ -61,10 +61,11 @@ public class PreferByKindWeigher extends LookupElementWeigher {
static final ElementPattern<PsiElement> INSIDE_METHOD_THROWS_CLAUSE =
psiElement().afterLeaf(PsiKeyword.THROWS, ",").inside(psiElement(JavaElementType.THROWS_LIST));
static final ElementPattern<PsiElement> IN_RESOURCE_TYPE =
psiElement().withParent(psiElement(PsiJavaCodeReferenceElement.class).
withParent(psiElement(PsiTypeElement.class).
withParent(or(psiElement(PsiResourceVariable.class), psiElement(PsiResourceList.class)))));
static final ElementPattern<PsiElement> IN_RESOURCE =
psiElement().withParent(or(
psiElement(PsiJavaCodeReferenceElement.class).withParent(PsiTypeElement.class).
withSuperParent(2, or(psiElement(PsiResourceVariable.class), psiElement(PsiResourceList.class))),
psiElement(PsiReferenceExpression.class).withParent(PsiResourceExpression.class)));
private final CompletionType myCompletionType;
private final PsiElement myPosition;
@@ -115,7 +116,7 @@ public class PreferByKindWeigher extends LookupElementWeigher {
};
}
if (IN_RESOURCE_TYPE.accepts(position)) {
if (IN_RESOURCE.accepts(position)) {
return new Condition<PsiClass>() {
@Override
public boolean value(PsiClass psiClass) {
@@ -312,6 +312,10 @@ public abstract class JavaElementVisitor extends PsiElementVisitor {
visitLocalVariable(variable);
}
public void visitResourceExpression(PsiResourceExpression expression) {
visitElement(expression);
}
public void visitTypeElement(PsiTypeElement type) {
visitElement(type);
}
@@ -0,0 +1,29 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi;
import org.jetbrains.annotations.NotNull;
/**
* Represents a resource expression of enhanced try-with-resources statement introduced in JDK 9.
*
* @see PsiResourceList
* @since 15
*/
public interface PsiResourceExpression extends PsiResourceListElement {
@NotNull
PsiExpression getExpression();
}
@@ -15,8 +15,6 @@
*/
package com.intellij.psi;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
@@ -25,9 +23,10 @@ import java.util.List;
* @see PsiTryStatement#getResourceList()
* @since 10.5
*/
public interface PsiResourceList extends PsiElement {
public interface PsiResourceList extends PsiElement, Iterable<PsiResourceListElement> {
int getResourceVariablesCount();
@NotNull
/** @deprecated use {@link #iterator()} (to be removed in IDEA 17) */
@SuppressWarnings("unused")
List<PsiResourceVariable> getResourceVariables();
}
@@ -0,0 +1,28 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi;
import org.jetbrains.annotations.Nullable;
/**
* Common interface for {@link PsiResourceVariable} and {@link PsiResourceExpression}.
*
* @since 15
*/
public interface PsiResourceListElement extends PsiElement {
@Nullable
PsiType getType();
}
@@ -20,10 +20,10 @@ import org.jetbrains.annotations.NotNull;
/**
* Represents a resource variable of try-with-resources statement (automatic resource management) introduced in JDK 7.
*
* @see PsiResourceList#getResourceVariables()
* @see PsiResourceList
* @since 10.5
*/
public interface PsiResourceVariable extends PsiLocalVariable {
public interface PsiResourceVariable extends PsiLocalVariable, PsiResourceListElement {
@NotNull
PsiElement[] getDeclarationScope();
}
@@ -1126,10 +1126,15 @@ public final class PsiUtil extends PsiUtilCore {
}
@Nullable
public static PsiMethod getResourceCloserMethod(@NotNull final PsiResourceVariable resource) {
final PsiType resourceType = resource.getType();
if (!(resourceType instanceof PsiClassType)) return null;
return getResourceCloserMethodForType((PsiClassType)resourceType);
public static PsiMethod getResourceCloserMethod(@NotNull PsiResourceListElement resource) {
PsiType resourceType = resource.getType();
return resourceType instanceof PsiClassType ? getResourceCloserMethodForType((PsiClassType)resourceType) : null;
}
/** @deprecated use {@link #getResourceCloserMethod(PsiResourceListElement)} (to be removed in IDEA 17) */
@SuppressWarnings("unused")
public static PsiMethod getResourceCloserMethod(@NotNull PsiResourceVariable resource) {
return getResourceCloserMethod((PsiResourceListElement)resource);
}
@Nullable
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -118,14 +118,19 @@ public class ExceptionUtil {
else if (element instanceof PsiTryStatement) {
return getTryExceptions((PsiTryStatement)element);
}
else if (element instanceof PsiResourceVariable) {
final PsiResourceVariable variable = (PsiResourceVariable)element;
final List<PsiClassType> types = ContainerUtil.newArrayList();
addExceptions(types, getCloserExceptions(variable));
final PsiExpression initializer = variable.getInitializer();
if (initializer != null) addExceptions(types, getThrownExceptions(initializer));
else if (element instanceof PsiResourceListElement) {
List<PsiClassType> types = ContainerUtil.newArrayList();
addExceptions(types, getCloserExceptions((PsiResourceListElement)element));
if (element instanceof PsiResourceVariable) {
PsiResourceVariable variable = (PsiResourceVariable)element;
PsiExpression initializer = variable.getInitializer();
if (initializer != null) {
addExceptions(types, getThrownExceptions(initializer));
}
}
return types;
}
return getThrownExceptions(element.getChildren());
}
@@ -135,8 +140,8 @@ public class ExceptionUtil {
PsiResourceList resourceList = tryStatement.getResourceList();
if (resourceList != null) {
for (PsiResourceVariable variable : resourceList.getResourceVariables()) {
addExceptions(array, getUnhandledCloserExceptions(variable, resourceList));
for (PsiResourceListElement resource : resourceList) {
addExceptions(array, getUnhandledCloserExceptions(resource, resourceList));
}
}
@@ -303,16 +308,10 @@ public class ExceptionUtil {
}
unhandledExceptions = unhandled;
}
if (element instanceof PsiResourceVariable) {
final List<PsiClassType> unhandled = getUnhandledCloserExceptions((PsiResourceVariable)element, topElement);
else if (element instanceof PsiResourceListElement) {
final List<PsiClassType> unhandled = getUnhandledCloserExceptions((PsiResourceListElement)element, topElement);
if (!unhandled.isEmpty()) {
if (unhandledExceptions == null) {
unhandledExceptions = ContainerUtil.newArrayList(unhandled);
}
else {
unhandledExceptions.addAll(unhandled);
}
unhandledExceptions = ContainerUtil.newArrayList(unhandled);
}
}
@@ -356,6 +355,7 @@ public class ExceptionUtil {
@NotNull
public static List<PsiClassType> getUnhandledExceptions(final @NotNull PsiElement[] elements) {
final List<PsiClassType> array = ContainerUtil.newArrayList();
final PsiElementVisitor visitor = new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitCallExpression(@NotNull PsiCallExpression expression) {
@@ -377,9 +377,15 @@ public class ExceptionUtil {
}
@Override
public void visitResourceVariable(@NotNull PsiResourceVariable resourceVariable) {
addExceptions(array, getUnhandledCloserExceptions(resourceVariable, null));
visitElement(resourceVariable);
public void visitResourceVariable(@NotNull PsiResourceVariable resource) {
addExceptions(array, getUnhandledCloserExceptions((PsiResourceListElement)resource, null));
visitElement(resource);
}
@Override
public void visitResourceExpression(@NotNull PsiResourceExpression resource) {
addExceptions(array, getUnhandledCloserExceptions(resource, null));
visitElement(resource);
}
};
@@ -392,18 +398,6 @@ public class ExceptionUtil {
@NotNull
public static List<PsiClassType> getUnhandledExceptions(@NotNull PsiElement element) {
if (element instanceof PsiCallExpression) {
PsiCallExpression expression = (PsiCallExpression)element;
return getUnhandledExceptions(expression, null);
}
else if (element instanceof PsiThrowStatement) {
PsiThrowStatement throwStatement = (PsiThrowStatement)element;
return getUnhandledExceptions(throwStatement, null);
}
else if (element instanceof PsiResourceVariable) {
return getUnhandledCloserExceptions((PsiResourceVariable)element, null);
}
return getUnhandledExceptions(new PsiElement[]{element});
}
@@ -532,19 +526,31 @@ public class ExceptionUtil {
}
@NotNull
public static List<PsiClassType> getCloserExceptions(@NotNull PsiResourceVariable resource) {
public static List<PsiClassType> getCloserExceptions(@NotNull PsiResourceListElement resource) {
PsiMethod method = PsiUtil.getResourceCloserMethod(resource);
PsiSubstitutor substitutor = PsiUtil.resolveGenericsClassInType(resource.getType()).getSubstitutor();
return method != null ? getExceptionsByMethod(method, substitutor, resource) : Collections.<PsiClassType>emptyList();
}
/** @deprecated use {@link #getCloserExceptions(PsiResourceListElement)} (to be removed in IDEA 16) */
@SuppressWarnings("unused")
public static List<PsiClassType> getCloserExceptions(@NotNull PsiResourceVariable resource) {
return getCloserExceptions((PsiResourceListElement)resource);
}
@NotNull
public static List<PsiClassType> getUnhandledCloserExceptions(@NotNull PsiResourceVariable resource, @Nullable PsiElement topElement) {
public static List<PsiClassType> getUnhandledCloserExceptions(@NotNull PsiResourceListElement resource, @Nullable PsiElement topElement) {
PsiMethod method = PsiUtil.getResourceCloserMethod(resource);
PsiSubstitutor substitutor = PsiUtil.resolveGenericsClassInType(resource.getType()).getSubstitutor();
return method != null ? getUnhandledExceptions(method, resource, topElement, substitutor) : Collections.<PsiClassType>emptyList();
}
/** @deprecated use {@link #getUnhandledCloserExceptions(PsiResourceListElement, PsiElement)} (to be removed in IDEA 16) */
@SuppressWarnings("unused")
public static List<PsiClassType> getUnhandledCloserExceptions(@NotNull PsiResourceVariable resource, @Nullable PsiElement topElement) {
return getUnhandledCloserExceptions((PsiResourceListElement)resource, topElement);
}
@NotNull
public static List<PsiClassType> getUnhandledExceptions(@NotNull PsiThrowStatement throwStatement, @Nullable PsiElement topElement) {
List<PsiClassType> unhandled = new SmartList<PsiClassType>();
@@ -574,7 +580,7 @@ public class ExceptionUtil {
if (expression != null) {
final PsiType type = expression.getType();
if (type != null) {
return Arrays.asList(type);
return Collections.singletonList(type);
}
}
@@ -48,6 +48,8 @@ public class DeclarationParser {
JavaTokenType.RPARENTH, JavaTokenType.LBRACE, JavaTokenType.ARROW);
private static final TokenSet TYPE_START = TokenSet.orSet(
ElementType.PRIMITIVE_TYPE_BIT_SET, TokenSet.create(JavaTokenType.IDENTIFIER, JavaTokenType.AT));
private static final TokenSet RESOURCE_EXPRESSIONS = TokenSet.create(
JavaElementType.REFERENCE_EXPRESSION, JavaElementType.THIS_EXPRESSION);
private static final String WHITESPACES = "\n\r \t";
private static final String LINE_ENDS = "\n\r";
@@ -575,6 +577,16 @@ public class DeclarationParser {
@Nullable
public PsiBuilder.Marker parseResource(PsiBuilder builder) {
PsiBuilder.Marker marker = builder.mark();
PsiBuilder.Marker expr = myParser.getExpressionParser().parse(builder);
if (expr != null && RESOURCE_EXPRESSIONS.contains(exprType(expr)) && builder.getTokenType() != JavaTokenType.IDENTIFIER) {
marker.done(JavaElementType.RESOURCE_EXPRESSION);
return marker;
}
marker.rollbackTo();
return parseListElement(builder, true, false, false, true);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -1163,10 +1163,14 @@ class ControlFlowAnalyzer extends JavaElementVisitor {
public void visitResourceList(final PsiResourceList resourceList) {
startElement(resourceList);
final List<PsiResourceVariable> resources = resourceList.getResourceVariables();
for (PsiResourceVariable resource : resources) {
for (PsiResourceListElement resource : resourceList) {
ProgressIndicatorProvider.checkCanceled();
processVariable(resource);
if (resource instanceof PsiResourceVariable) {
processVariable((PsiVariable)resource);
}
else if (resource instanceof PsiResourceExpression) {
((PsiResourceExpression)resource).getExpression().accept(this);
}
}
finishElement(resourceList);
@@ -239,11 +239,9 @@ public class PsiImplUtil {
final ElementClassHint hint = processor.getHint(ElementClassHint.KEY);
if (hint != null && !hint.shouldProcess(ElementClassHint.DeclarationKind.VARIABLE)) return true;
final List<PsiResourceVariable> resources = resourceList.getResourceVariables();
@SuppressWarnings({"SuspiciousMethodCalls"})
final int lastIdx = lastParent instanceof PsiResourceVariable ? resources.indexOf(lastParent) : resources.size();
for (int i = 0; i < lastIdx; i++) {
if (!processor.execute(resources.get(i), state)) return false;
for (PsiResourceListElement resource : resourceList) {
if (resource == lastParent) break;
if (resource instanceof PsiResourceVariable && !processor.execute(resource, state)) return false;
}
return true;
@@ -133,6 +133,7 @@ public interface JavaElementType {
IElementType TRY_STATEMENT = new JavaCompositeElementType("TRY_STATEMENT", PsiTryStatementImpl.class);
IElementType RESOURCE_LIST = new JavaCompositeElementType("RESOURCE_LIST", PsiResourceListImpl.class);
IElementType RESOURCE_VARIABLE = new JavaCompositeElementType("RESOURCE_VARIABLE", PsiResourceVariableImpl.class);
IElementType RESOURCE_EXPRESSION = new JavaCompositeElementType("RESOURCE_EXPRESSION", PsiResourceExpressionImpl.class);
IElementType CATCH_SECTION = new JavaCompositeElementType("CATCH_SECTION", PsiCatchSectionImpl.class);
IElementType LABELED_STATEMENT = new JavaCompositeElementType("LABELED_STATEMENT", PsiLabeledStatementImpl.class);
IElementType ASSERT_STATEMENT = new JavaCompositeElementType("ASSERT_STATEMENT", PsiAssertStatementImpl.class);
@@ -0,0 +1,55 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.impl.source.tree.java;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.tree.CompositePsiElement;
import com.intellij.psi.impl.source.tree.JavaElementType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PsiResourceExpressionImpl extends CompositePsiElement implements PsiResourceExpression {
public PsiResourceExpressionImpl() {
super(JavaElementType.RESOURCE_EXPRESSION);
}
@NotNull
@Override
public PsiExpression getExpression() {
return (PsiExpression)getFirstChild();
}
@Nullable
@Override
public PsiType getType() {
return getExpression().getType();
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof JavaElementVisitor) {
((JavaElementVisitor)visitor).visitResourceExpression(this);
}
else {
visitor.visitElement(this);
}
}
@Override
public String toString() {
return "PsiResourceExpression";
}
}
@@ -24,6 +24,7 @@ import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Iterator;
import java.util.List;
public class PsiResourceListImpl extends CompositePsiElement implements PsiResourceList {
@@ -35,17 +36,23 @@ public class PsiResourceListImpl extends CompositePsiElement implements PsiResou
public int getResourceVariablesCount() {
int count = 0;
for (PsiElement child = getFirstChild(); child != null; child = child.getNextSibling()) {
if (child instanceof PsiResourceVariable) ++count;
if (child instanceof PsiResourceListElement) ++count;
}
return count;
}
@NotNull
@Deprecated
@Override
public List<PsiResourceVariable> getResourceVariables() {
return PsiTreeUtil.getChildrenOfTypeAsList(this, PsiResourceVariable.class);
}
@NotNull
@Override
public Iterator<PsiResourceListElement> iterator() {
return PsiTreeUtil.childIterator(this, PsiResourceListElement.class);
}
@Override
public void accept(@NotNull PsiElementVisitor visitor) {
if (visitor instanceof JavaElementVisitor) {
@@ -66,7 +73,7 @@ public class PsiResourceListImpl extends CompositePsiElement implements PsiResou
@Override
public void deleteChildInternal(@NotNull ASTNode child) {
if (child.getPsi() instanceof PsiResourceVariable && getResourceVariablesCount() == 1) {
if (child.getPsi() instanceof PsiResourceListElement && getResourceVariablesCount() == 1) {
getTreeParent().deleteChildInternal(this);
return;
}
@@ -144,6 +144,7 @@ assignment.to.final.variable=Cannot assign a value to final variable ''{0}''
variable.must.be.final=Variable ''{0}'' is accessed from within inner class, needs to be declared final
variable.must.be.final.or.effectively.final=Variable ''{0}'' is accessed from within inner class, needs to be final or effectively final
lambda.variable.must.be.final=Variable used in lambda expression should be final or effectively final
resource.variable.must.be.final=Variable used as a try-with-resources resource should be final or effectively final
initializer.must.be.able.to.complete.normally=Initializer must be able to complete normally
weaker.privileges={0}; attempting to assign weaker access privileges (''{1}''); was ''{2}''
incompatible.return.type=attempting to use incompatible return type
@@ -397,4 +398,5 @@ feature.method.references=Method references
feature.lambda.expressions=Lambda expressions
feature.type.annotations=Type annotations
feature.type.receivers=Receiver parameters
feature.try.with.resources.refs=Resource references
insufficient.language.level={0} are not supported at this language level
@@ -0,0 +1,8 @@
class MyClass {
void f() {
String response;
AutoCloseable resource;
try (re<caret>) {
}
}
}
@@ -0,0 +1,8 @@
class MyClass {
void f() {
String response;
AutoCloseable resource;
try (resource<caret>) {
}
}
}
@@ -0,0 +1,8 @@
import java.io.*;
class UnsupportedFeatures {
void m() throws Exception {
Reader r1 = new FileReader("/dev/null");
try (<error descr="Resource references are not supported at this language level">r1</error>; Reader r2 = new FileReader("/dev/null")) { }
}
}
@@ -0,0 +1,45 @@
import java.io.IOException;
class TryWithResources {
final AutoCloseable f1 = null;
AutoCloseable f2 = null;
void testFields() throws Exception {
try (f1; <error descr="Variable used as a try-with-resources resource should be final or effectively final">f2</error>) { }
catch (Exception ignore) { }
}
void testLocalVars() throws Exception {
final AutoCloseable r1 = null;
AutoCloseable r2 = null;
AutoCloseable r3 = null;
try (r1; r2; <error descr="Variable used as a try-with-resources resource should be final or effectively final">r3</error>) { }
r3 = null;
}
void testType() throws Exception {
String s = "";
try (<error descr="Incompatible types. Found: 'java.lang.String', required: 'java.lang.AutoCloseable'">s</error>;
<error descr="Incompatible types. Found: 'TryWithResources', required: 'java.lang.AutoCloseable'">this</error>) { }
}
void testUnhandled() {
class Resource implements AutoCloseable {
@Override public void close() throws IOException { }
}
Resource r = new Resource();
try (<error descr="Unhandled exception from auto-closeable resource: java.io.IOException">r</error>) { }
}
void testResolve() throws Exception {
try (<error descr="Cannot resolve symbol 'r'">r</error>; AutoCloseable r = null) { }
try (AutoCloseable r = null; r) { }
}
void testUnassigned() throws Exception {
AutoCloseable r;
try (<error descr="Variable 'r' might not have been initialized">r</error>) {
System.out.println(r);
}
}
}
@@ -15,7 +15,6 @@ class C {
@interface Anno { String f(Anno this); }
void m0() {
try (Object <error descr="Receivers are not allowed outside of method parameter list">this</error>) { }
Runnable r = (C <error descr="Receivers are not allowed outside of method parameter list">C.this</error>) -> { };
}
@@ -0,0 +1,27 @@
PsiJavaFile:TryIncomplete18.java
PsiTryStatement
PsiKeyword:try('try')
PsiResourceList:(R<T> r)
PsiJavaToken:LPARENTH('(')
PsiResourceVariable:r
PsiModifierList:
<empty list>
PsiTypeElement:R<T>
PsiJavaCodeReferenceElement:R<T>
PsiIdentifier:R('R')
PsiReferenceParameterList
PsiJavaToken:LT('<')
PsiTypeElement:T
PsiJavaCodeReferenceElement:T
PsiIdentifier:T('T')
PsiReferenceParameterList
<empty list>
PsiJavaToken:GT('>')
PsiWhiteSpace(' ')
PsiIdentifier:r('r')
PsiErrorElement:'=' expected
<empty list>
PsiJavaToken:RPARENTH(')')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,14 @@
PsiJavaFile:TryNormal10.java
PsiTryStatement
PsiKeyword:try('try')
PsiResourceList:(this)
PsiJavaToken:LPARENTH('(')
PsiResourceExpression
PsiThisExpression:this
PsiReferenceParameterList
<empty list>
PsiKeyword:this('this')
PsiJavaToken:RPARENTH(')')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,14 @@
PsiJavaFile:TryNormal7.java
PsiTryStatement
PsiKeyword:try('try')
PsiResourceList:(r)
PsiJavaToken:LPARENTH('(')
PsiResourceExpression
PsiReferenceExpression:r
PsiReferenceParameterList
<empty list>
PsiIdentifier:r('r')
PsiJavaToken:RPARENTH(')')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,15 @@
PsiJavaFile:TryNormal8.java
PsiTryStatement
PsiKeyword:try('try')
PsiResourceList:(r;)
PsiJavaToken:LPARENTH('(')
PsiResourceExpression
PsiReferenceExpression:r
PsiReferenceParameterList
<empty list>
PsiIdentifier:r('r')
PsiJavaToken:SEMICOLON(';')
PsiJavaToken:RPARENTH(')')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,31 @@
PsiJavaFile:TryNormal9.java
PsiTryStatement
PsiKeyword:try('try')
PsiResourceList:(r1; R r2 = 0)
PsiJavaToken:LPARENTH('(')
PsiResourceExpression
PsiReferenceExpression:r1
PsiReferenceParameterList
<empty list>
PsiIdentifier:r1('r1')
PsiJavaToken:SEMICOLON(';')
PsiWhiteSpace(' ')
PsiResourceVariable:r2
PsiModifierList:
<empty list>
PsiTypeElement:R
PsiJavaCodeReferenceElement:R
PsiIdentifier:R('R')
PsiReferenceParameterList
<empty list>
PsiWhiteSpace(' ')
PsiIdentifier:r2('r2')
PsiWhiteSpace(' ')
PsiJavaToken:EQ('=')
PsiWhiteSpace(' ')
PsiLiteralExpression:0
PsiJavaToken:INTEGER_LITERAL('0')
PsiJavaToken:RPARENTH(')')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -38,6 +38,7 @@ public class Normal17CompletionTest extends LightFixtureCompletionTestCase {
public void testOnlyResourcesInResourceList2() { doTest() }
public void testOnlyResourcesInResourceList3() { doTest() }
public void testOnlyResourcesInResourceList4() { doTest() }
public void testOnlyResourcesInResourceList5() { doTest() }
public void testMethodReferenceNoStatic() { doTest() }
public void testMethodReferenceCallContext() { doTest() }
@@ -48,4 +48,5 @@ public class LightAdvHighlightingJdk8Test extends LightDaemonAnalyzerTestCase {
public void testMethodReferences() { doTest(false, true); }
public void testUsedMethodsByMethodReferences() { enableInspectionTool(new UnusedDeclarationInspection()); doTest(true, true); }
public void testLambdaExpressions() { doTest(false, true); }
public void testUnsupportedFeatures() { doTest(false, false); }
}
@@ -46,6 +46,7 @@ public class LightAdvHighlightingJdk9Test extends LightDaemonAnalyzerTestCase {
public void testSafeVarargsApplicability() { doTest(true, false); }
public void testPrivateInInterfaces() { doTest(false, false); }
public void testTryWithResources() { doTest(false, false); }
public void testValueTypes() { setLanguageLevel(LanguageLevel.JDK_X); doTest(false, false); }
}
@@ -122,6 +122,10 @@ public class StatementParserTest extends JavaParsingTestCase {
public void testTryNormal4() { doParserTest("try(R r = 0){}"); }
public void testTryNormal5() { doParserTest("try(R1 r1 = 1; R2 r2 = 2){}"); }
public void testTryNormal6() { doParserTest("try(R r = 0;){}"); }
public void testTryNormal7() { doParserTest("try(r){}"); }
public void testTryNormal8() { doParserTest("try(r;){}"); }
public void testTryNormal9() { doParserTest("try(r1; R r2 = 0){}"); }
public void testTryNormal10() { doParserTest("try(this){}"); }
public void testTryIncomplete0() { doParserTest("try"); }
public void testTryIncomplete1() { doParserTest("try{}"); }
public void testTryIncomplete2() { doParserTest("try{}catch"); }
@@ -140,6 +144,7 @@ public class StatementParserTest extends JavaParsingTestCase {
public void testTryIncomplete15() { doParserTest("try(R r){}"); }
public void testTryIncomplete16() { doParserTest("try(R r =){}"); }
public void testTryIncomplete17() { doParserTest("try(R r = 0;;){}"); }
public void testTryIncomplete18() { doParserTest("try(R<T> r){}"); }
public void testWhileNormal() { doParserTest("while (true) foo();"); }
public void testWhileIncomplete0() { doParserTest("while"); }
@@ -1044,4 +1044,29 @@ public class PsiTreeUtil {
return res;
}
@NotNull
public static <T extends PsiElement> Iterator<T> childIterator(@NotNull final PsiElement element, @NotNull final Class<T> aClass) {
return new Iterator<T>() {
private T next = getChildOfType(element, aClass);
@Override
public boolean hasNext() {
return next != null;
}
@Override
public T next() {
if (next == null) throw new NoSuchElementException();
T current = this.next;
next = getNextSiblingOfType(current, aClass);
return current;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}