From e84bc3af06a93c4a860eb9c280eeaab9c807acd1 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Mon, 21 Feb 2011 17:48:10 +0100 Subject: [PATCH] Project Coin try-with-resource support, take 2 --- .../intellij/codeInsight/ExceptionUtil.java | 55 ++++++-- .../daemon/impl/analysis/HighlightUtil.java | 33 +++-- .../impl/analysis/HighlightVisitorImpl.java | 10 +- .../lang/java/parser/DeclarationParser.java | 120 ++++++++++-------- .../lang/java/parser/StatementParser.java | 4 +- .../psi/controlFlow/ControlFlowAnalyzer.java | 9 +- .../com/intellij/psi/impl/PsiImplUtil.java | 25 ++-- .../psi/impl/PsiJavaParserFacadeImpl.java | 14 +- .../psi/impl/source/tree/JavaElementType.java | 3 +- .../tree/java/PsiLocalVariableImpl.java | 13 +- .../source/tree/java/PsiResourceListImpl.java | 58 +++++++++ .../source/tree/java/PsiTryStatementImpl.java | 4 +- .../advHighlighting7/TryWithResources.java | 58 +++++---- .../statements/TryIncomplete10.txt | 2 +- .../statements/TryIncomplete11.txt | 2 +- .../statements/TryIncomplete12.txt | 2 +- .../statements/TryIncomplete13.txt | 10 +- .../statements/TryIncomplete14.txt | 15 +-- .../statements/TryIncomplete15.txt | 4 +- .../statements/TryIncomplete16.txt | 4 +- .../statements/TryIncomplete17.txt | 4 +- .../parser-partial/statements/TryNormal4.txt | 4 +- .../parser-partial/statements/TryNormal5.txt | 6 +- .../parser-partial/statements/TryNormal6.txt | 13 ++ .../parser-partial/statements/TryNormal7.txt | 17 +++ .../parser/partial/StatementParserTest.java | 4 +- .../com/intellij/psi/JavaElementVisitor.java | 6 +- .../src/com/intellij/psi/PsiResourceList.java | 44 +++++++ .../src/com/intellij/psi/PsiTryStatement.java | 2 +- .../src/com/intellij/psi/util/PsiUtil.java | 17 ++- .../com/intellij/psi/util/PsiTreeUtil.java | 16 +++ 31 files changed, 404 insertions(+), 174 deletions(-) create mode 100644 java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiResourceListImpl.java create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryNormal6.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryNormal7.txt create mode 100644 java/openapi/src/com/intellij/psi/PsiResourceList.java diff --git a/java/java-impl/src/com/intellij/codeInsight/ExceptionUtil.java b/java/java-impl/src/com/intellij/codeInsight/ExceptionUtil.java index c7f3c32476d5..a8a6f914ada4 100644 --- a/java/java-impl/src/com/intellij/codeInsight/ExceptionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/ExceptionUtil.java @@ -22,6 +22,7 @@ import com.intellij.psi.*; import com.intellij.psi.controlFlow.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; import gnu.trove.THashSet; import org.jetbrains.annotations.NonNls; @@ -206,10 +207,10 @@ public class ExceptionUtil { PsiClassType exception = getUnhandledException(statement, topElement); unhandledExceptions = exception == null ? Collections.emptyList() : Collections.singletonList(exception); } - else if (element instanceof PsiCodeBlock - && element.getParent() instanceof PsiMethod - && ((PsiMethod)element.getParent()).isConstructor() - && !firstStatementIsConstructorCall((PsiCodeBlock)element)) { + else if (element instanceof PsiCodeBlock && + element.getParent() instanceof PsiMethod && + ((PsiMethod)element.getParent()).isConstructor() && + !firstStatementIsConstructorCall((PsiCodeBlock)element)) { // there is implicit parent constructor call final PsiMethod constructor = (PsiMethod)element.getParent(); final PsiClass aClass = constructor.getContainingClass(); @@ -246,6 +247,22 @@ public class ExceptionUtil { unhandledExceptions = unhandled; } + if (PsiUtil.isResourceInTryStatement(element)) { + final PsiType resourceType = PsiUtil.getResourceType(element); + if (resourceType instanceof PsiClassType) { + final PsiClass resourceClass = ((PsiClassType)resourceType).resolve(); + if (resourceClass != null) { + final List unhandled = getUnhandledCloserExceptions(element, resourceClass, topElement); + if (unhandledExceptions == null) { + unhandledExceptions = unhandled; + } + else { + unhandledExceptions.addAll(unhandled); + } + } + } + } + if (unhandledExceptions != null) { if (foundExceptions == null) { foundExceptions = new THashSet(); @@ -309,15 +326,28 @@ public class ExceptionUtil { } @NotNull - public static List getUnhandledExceptions(PsiCallExpression methodCall, PsiElement topElement) { + public static List getUnhandledExceptions(final PsiCallExpression methodCall, final PsiElement topElement) { final JavaResolveResult result = methodCall.resolveMethodGenerics(); - PsiMethod method = (PsiMethod)result.getElement(); - return getUnhandledExceptions(method, methodCall, topElement, - ApplicationManager.getApplication().runReadAction(new Computable() { - public PsiSubstitutor compute() { - return result.getSubstitutor(); - } - })); + final PsiMethod method = (PsiMethod)result.getElement(); + final PsiSubstitutor substitutor = ApplicationManager.getApplication().runReadAction(new Computable() { + public PsiSubstitutor compute() { + return result.getSubstitutor(); + } + }); + return getUnhandledExceptions(method, methodCall, topElement, substitutor); + } + + @NotNull + public static List getUnhandledCloserExceptions(final PsiElement resource, + final PsiClass resourceClass, + final PsiElement topElement) { + final PsiMethod[] closers = resourceClass.findMethodsByName("close", false); + for (final PsiMethod method : closers) { + if (method.getParameterList().getParametersCount() == 0) { + return getUnhandledExceptions(method, resource, topElement, PsiSubstitutor.EMPTY); + } + } + return Collections.emptyList(); } @Nullable @@ -335,7 +365,6 @@ public class ExceptionUtil { return null; } - @NotNull private static List getUnhandledExceptions(PsiMethod method, PsiElement element, diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java index ec903511c0e1..8368aec10784 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java @@ -553,18 +553,19 @@ public class HighlightUtil { return JavaErrorMessages.message("unhandled.exceptions", exceptionsText.toString(), unhandledExceptions.size()); } - @Nullable static HighlightInfo checkVariableAlreadyDefined(PsiVariable variable) { if (variable instanceof ExternallyDefinedPsiElement) return null; boolean isIncorrect = false; PsiIdentifier identifier = variable.getNameIdentifier(); + assert identifier != null : variable; String name = variable.getName(); if (variable instanceof PsiLocalVariable || variable instanceof PsiParameter && ((PsiParameter)variable).getDeclarationScope() instanceof PsiCatchSection || - variable instanceof PsiParameter && ((PsiParameter)variable).getDeclarationScope() instanceof PsiForeachStatement) { + variable instanceof PsiParameter && ((PsiParameter)variable).getDeclarationScope() instanceof PsiForeachStatement || + PsiUtil.isResourceInTryStatement(variable)) { PsiElement scope = PsiTreeUtil.getParentOfType(variable, PsiFile.class, PsiMethod.class, PsiClassInitializer.class); - VariablesNotProcessor proc = new VariablesNotProcessor(variable, false){ + VariablesNotProcessor proc = new VariablesNotProcessor(variable, false) { protected boolean check(final PsiVariable var, final ResolveState state) { return (var instanceof PsiLocalVariable || var instanceof PsiParameter) && super.check(var, state); } @@ -864,13 +865,11 @@ public class HighlightUtil { assert tryBlock != null : statement; thrownTypes.addAll(ExceptionUtil.collectUnhandledExceptions(tryBlock, tryBlock)); - final PsiParameterList resources = statement.getResourceList(); + final PsiResourceList resources = statement.getResourceList(); if (resources != null) { thrownTypes.addAll(ExceptionUtil.collectUnhandledExceptions(resources, resources)); } - // todo: add exceptions from resource's close() method - final PsiType caughtType = parameter.getType(); if (caughtType instanceof PsiClassType) { return checkSimpleCatchParameter(parameter, thrownTypes, (PsiClassType)caughtType); @@ -1110,14 +1109,32 @@ public class HighlightUtil { @Nullable - public static HighlightInfo checkCatchParameterIsThrowable(PsiParameter parameter) { + public static HighlightInfo checkCatchParameterIsThrowable(final PsiParameter parameter) { if (parameter.getDeclarationScope() instanceof PsiCatchSection) { - PsiType type = parameter.getType(); + final PsiType type = parameter.getType(); return checkMustBeThrowable(type, parameter, true); } return null; } + @Nullable + public static HighlightInfo checkTryResourceIsAutoCloseable(@NotNull final PsiElement resource) { + final PsiType type = PsiUtil.getResourceType(resource); + if (type == null) return null; + + final PsiElementFactory factory = JavaPsiFacade.getInstance(resource.getProject()).getElementFactory(); + final PsiClassType autoCloseable = factory.createTypeByFQClassName("java.lang.AutoCloseable", resource.getResolveScope()); + if (TypeConversionUtil.isAssignable(autoCloseable, type)) return null; + + final HighlightInfo highlightInfo = createIncompatibleTypeHighlightInfo(autoCloseable, type, resource.getTextRange()); + //if (addCastIntention && TypeConversionUtil.areTypesConvertible(type, autoCloseable)) { + // if (parameter instanceof PsiExpression) { + // QuickFixAction.registerQuickFixAction(highlightInfo, new AddTypeCastFix(autoCloseable, (PsiExpression)parameter)); + // } + //} + return highlightInfo; + } + public static void checkArrayInitalizer(final PsiExpression initializer, final HighlightInfoHolder holder) { if (! (initializer instanceof PsiArrayInitializerExpression)) return; diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index 9d963c68c0d2..003e0d8e01a0 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -897,12 +897,18 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh @Override public void visitTryStatement(PsiTryStatement statement) { super.visitTryStatement(statement); if (!myHolder.hasErrorResults()) { - PsiParameter[] parameters = statement.getCatchBlockParameters(); - for (PsiParameter parameter : parameters) { + for (PsiParameter parameter : statement.getCatchBlockParameters()) { myHolder.addAll(HighlightUtil.checkExceptionThrownInTry(parameter)); myHolder.add(HighlightUtil.checkCatchParameterIsThrowable(parameter)); myHolder.add(GenericsHighlightUtil.checkCatchParameterIsClass(parameter)); } + + PsiResourceList resources = statement.getResourceList(); + if (resources != null) { + for (PsiElement resource : resources.getResources()) { + myHolder.add(HighlightUtil.checkTryResourceIsAutoCloseable(resource)); + } + } } } diff --git a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java index 23a23791a1aa..018bbbc3c875 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java @@ -35,7 +35,7 @@ import static com.intellij.lang.java.parser.JavaParserUtil.*; public class DeclarationParser { public enum Context { - FILE, CLASS, CODE_BLOCK, ANNOTATION_INTERFACE + FILE, CLASS, CODE_BLOCK, ANNOTATION_INTERFACE, RESOURCE_LIST } private static final TokenSet AFTER_END_DECLARATION_SET = TokenSet.create(JavaElementType.FIELD, JavaElementType.METHOD); @@ -222,7 +222,7 @@ public class DeclarationParser { if (tokenType == null) return null; if (tokenType == JavaTokenType.LBRACE) { - if (context == Context.FILE || context == Context.CODE_BLOCK) return null; + if (context == Context.FILE || context == Context.CODE_BLOCK || context == Context.RESOURCE_LIST) return null; } else if (tokenType == JavaTokenType.IDENTIFIER || ElementType.PRIMITIVE_TYPE_BIT_SET.contains(tokenType)) { if (context == Context.FILE) return null; @@ -234,7 +234,7 @@ public class DeclarationParser { else if (!ElementType.MODIFIER_BIT_SET.contains(tokenType) && !ElementType.CLASS_KEYWORD_BIT_SET.contains(tokenType) && tokenType != JavaTokenType.AT && - (context == Context.CODE_BLOCK || tokenType != JavaTokenType.LT)) { + (context == Context.CODE_BLOCK || context == Context.RESOURCE_LIST || tokenType != JavaTokenType.LT)) { return null; } @@ -278,7 +278,7 @@ public class DeclarationParser { final PsiBuilder.Marker idPos = builder.mark(); type = parseTypeNotNull(builder); if (builder.getTokenType() == JavaTokenType.LPARENTH) { // constructor - if (context == Context.CODE_BLOCK) { + if (context == Context.CODE_BLOCK || context == Context.RESOURCE_LIST) { declaration.rollbackTo(); return null; } @@ -301,6 +301,10 @@ public class DeclarationParser { declaration.drop(); return modList; } + else if (context == Context.RESOURCE_LIST) { + declaration.rollbackTo(); + return null; + } final PsiBuilder.Marker codeBlock = StatementParser.parseCodeBlock(builder); assert codeBlock != null : builder.getOriginalText(); @@ -326,7 +330,7 @@ public class DeclarationParser { } if (!expect(builder, JavaTokenType.IDENTIFIER)) { - if (context == Context.CODE_BLOCK && modListInfo.second) { + if ((context == Context.CODE_BLOCK || context == Context.RESOURCE_LIST) && modListInfo.second) { declaration.rollbackTo(); return null; } @@ -447,33 +451,33 @@ public class DeclarationParser { @NotNull public static PsiBuilder.Marker parseParameterList(final PsiBuilder builder) { - return parseParameterList(builder, false); + return parseElementList(builder, false); } @NotNull public static PsiBuilder.Marker parseResourceList(final PsiBuilder builder) { - return parseParameterList(builder, true); + return parseElementList(builder, true); } @NotNull - private static PsiBuilder.Marker parseParameterList(final PsiBuilder builder, final boolean resources) { + private static PsiBuilder.Marker parseElementList(final PsiBuilder builder, final boolean resources) { assert builder.getTokenType() == JavaTokenType.LPARENTH : builder.getTokenType(); - final PsiBuilder.Marker paramList = builder.mark(); + final PsiBuilder.Marker elementList = builder.mark(); builder.advanceLexer(); final IElementType delimiter = resources ? JavaTokenType.SEMICOLON : JavaTokenType.COMMA; final String noDelimiterMsg = JavaErrorMessages.message(resources ? "expected.semicolon" : "expected.comma"); - final String noParameterMsg = JavaErrorMessages.message(resources ? "expected.resource" : "expected.parameter"); + final String noElementMsg = JavaErrorMessages.message(resources ? "expected.resource" : "expected.parameter"); PsiBuilder.Marker invalidElements = null; String errorMessage = null; boolean delimiterExpected = false; - int paramCount = 0; + int elementCount = 0; while (true) { final IElementType tokenType = builder.getTokenType(); if (tokenType == null || tokenType == JavaTokenType.RPARENTH || tokenType == JavaTokenType.LBRACE) { - boolean noLastParam = !delimiterExpected && paramCount > 0; - if (noLastParam) { + boolean noLastElement = !delimiterExpected && elementCount > 0; + if (noLastElement) { error(builder, JavaErrorMessages.message("expected.identifier.or.type")); } if (tokenType == JavaTokenType.RPARENTH) { @@ -481,13 +485,13 @@ public class DeclarationParser { invalidElements.error(errorMessage); invalidElements = null; } - else if (resources && paramCount == 0) { + else if (resources && elementCount == 0) { error(builder, JavaErrorMessages.message("expected.resource")); } builder.advanceLexer(); } else { - if (!noLastParam) { + if (!noLastElement) { if (invalidElements != null) { invalidElements.error(errorMessage); } @@ -510,27 +514,27 @@ public class DeclarationParser { } } else { - final PsiBuilder.Marker param = parseParameter(builder, true, false, resources); - if (param != null) { + final PsiBuilder.Marker listElement = resources ? parseResource(builder) : parseParameter(builder, true, false); + if (listElement != null) { delimiterExpected = true; if (invalidElements != null) { - invalidElements.errorBefore(errorMessage, param); + invalidElements.errorBefore(errorMessage, listElement); invalidElements = null; } - paramCount++; + elementCount++; continue; } } if (invalidElements == null) { if (builder.getTokenType() == delimiter) { - error(builder, noParameterMsg); + error(builder, noElementMsg); builder.advanceLexer(); continue; } else { invalidElements = builder.mark(); - errorMessage = delimiterExpected ? noDelimiterMsg : noParameterMsg; + errorMessage = delimiterExpected ? noDelimiterMsg : noElementMsg; } } @@ -545,19 +549,19 @@ public class DeclarationParser { invalidElements.error(errorMessage); } - done(paramList, JavaElementType.PARAMETER_LIST); - return paramList; + done(elementList, resources ? JavaElementType.RESOURCE_LIST : JavaElementType.PARAMETER_LIST); + return elementList; } @Nullable - public static PsiBuilder.Marker parseParameter(final PsiBuilder builder, final boolean ellipsis, final boolean disjunction, final boolean value) { + public static PsiBuilder.Marker parseParameter(final PsiBuilder builder, final boolean ellipsis, final boolean disjunctiveType) { final PsiBuilder.Marker param = builder.mark(); final Pair modListInfo = parseModifierList(builder); int flags = ReferenceParser.EAT_LAST_DOT | ReferenceParser.WILDCARD; if (ellipsis) flags |= ReferenceParser.ELLIPSIS; - if (disjunction) flags |= ReferenceParser.DISJUNCTIONS; + if (disjunctiveType) flags |= ReferenceParser.DISJUNCTIONS; final ReferenceParser.TypeInfo typeInfo = ReferenceParser.parseTypeInfo(builder, flags); if (typeInfo == null) { @@ -572,23 +576,24 @@ public class DeclarationParser { } if (expect(builder, JavaTokenType.IDENTIFIER)) { - eatBrackets(builder, typeInfo != null && typeInfo.isVarArg || value, JavaErrorMessages.message("expected.rparen")); - if (value) { - if (expectOrError(builder, JavaTokenType.EQ, JavaErrorMessages.message("expected.eq"))) { - if (ExpressionParser.parse(builder) == null) { - error(builder, JavaErrorMessages.message("expected.expression")); - } - } - } + eatBrackets(builder, typeInfo != null && typeInfo.isVarArg, JavaErrorMessages.message("expected.rparen")); + done(param, JavaElementType.PARAMETER); + return param; } else { error(builder, JavaErrorMessages.message("expected.identifier")); param.drop(); return modListInfo.first; } + } - done(param, JavaElementType.PARAMETER); - return param; + @Nullable + private static PsiBuilder.Marker parseResource(final PsiBuilder builder) { + PsiBuilder.Marker resource = parse(builder, Context.RESOURCE_LIST); + if (resource == null) { + resource = ExpressionParser.parse(builder); + } + return resource; } @Nullable @@ -598,7 +603,7 @@ public class DeclarationParser { if (context == Context.CLASS || context == Context.ANNOTATION_INTERFACE) { varType = JavaElementType.FIELD; } - else if (context == Context.CODE_BLOCK) { + else if (context == Context.CODE_BLOCK || context == Context.RESOURCE_LIST) { varType = JavaElementType.LOCAL_VARIABLE; } else { @@ -629,6 +634,11 @@ public class DeclarationParser { unclosed = true; break; } + if (context == Context.RESOURCE_LIST) break; + } + else if (context == Context.RESOURCE_LIST) { + error(builder, JavaErrorMessages.message("expected.eq")); + break; } if (builder.getTokenType() != JavaTokenType.COMMA) break; @@ -647,26 +657,28 @@ public class DeclarationParser { builder.advanceLexer(); } - if (builder.getTokenType() == JavaTokenType.SEMICOLON && eatSemicolon) { - builder.advanceLexer(); - } - else { - // special treatment (see DeclarationParserTest.testMultiLineUnclosed()) - if (!builder.eof() && shouldRollback) { - final CharSequence text = builder.getOriginalText(); - final int spaceEnd = builder.getCurrentOffset(); - final int spaceStart = CharArrayUtil.shiftBackward(text, spaceEnd-1, WHITESPACES); - final int lineStart = CharArrayUtil.shiftBackwardUntil(text, spaceEnd, LINE_ENDS); - - if (declarationStart < lineStart && lineStart < spaceStart) { - final int newBufferEnd = CharArrayUtil.shiftForward(text, lineStart, WHITESPACES); - declaration.rollbackTo(); - return parse(stoppingBuilder(builder, newBufferEnd), context); - } + if (context != Context.RESOURCE_LIST) { + if (builder.getTokenType() == JavaTokenType.SEMICOLON && eatSemicolon) { + builder.advanceLexer(); } + else { + // special treatment (see DeclarationParserTest.testMultiLineUnclosed()) + if (!builder.eof() && shouldRollback) { + final CharSequence text = builder.getOriginalText(); + final int spaceEnd = builder.getCurrentOffset(); + final int spaceStart = CharArrayUtil.shiftBackward(text, spaceEnd-1, WHITESPACES); + final int lineStart = CharArrayUtil.shiftBackwardUntil(text, spaceEnd, LINE_ENDS); - if (!unclosed) { - error(builder, JavaErrorMessages.message("expected.semicolon")); + if (declarationStart < lineStart && lineStart < spaceStart) { + final int newBufferEnd = CharArrayUtil.shiftForward(text, lineStart, WHITESPACES); + declaration.rollbackTo(); + return parse(stoppingBuilder(builder, newBufferEnd), context); + } + } + + if (!unclosed) { + error(builder, JavaErrorMessages.message("expected.semicolon")); + } } } diff --git a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java index eb10691aca69..2f4061c26114 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java @@ -375,7 +375,7 @@ public class StatementParser { } final PsiBuilder.Marker afterParenth = builder.mark(); - final PsiBuilder.Marker param = DeclarationParser.parseParameter(builder, false, false, false); + final PsiBuilder.Marker param = DeclarationParser.parseParameter(builder, false, false); if (param == null || JavaParserUtil.exprType(param) != JavaElementType.PARAMETER || builder.getTokenType() != JavaTokenType.COLON) { afterParenth.rollbackTo(); return parseForLoopFromInitialization(builder, statement); @@ -657,7 +657,7 @@ public class StatementParser { return false; } - final PsiBuilder.Marker param = DeclarationParser.parseParameter(builder, false, areMultiCatchSupported(builder), false); + final PsiBuilder.Marker param = DeclarationParser.parseParameter(builder, false, areMultiCatchSupported(builder)); if (param == null) { error(builder, JavaErrorMessages.message("expected.parameter")); } diff --git a/java/java-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java b/java/java-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java index 59c3cf484641..add49f2a4f7a 100644 --- a/java/java-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java +++ b/java/java-impl/src/com/intellij/psi/controlFlow/ControlFlowAnalyzer.java @@ -268,7 +268,6 @@ class ControlFlowAnalyzer extends JavaJspElementVisitor { } } - // generate jump to the top finally block if (!myFinallyBlocks.isEmpty()) { final PsiElement finallyBlock = myFinallyBlocks.peek(); @@ -278,13 +277,10 @@ class ControlFlowAnalyzer extends JavaJspElementVisitor { addElementOffsetLater(finallyBlock, true); } } - } private void generateCheckedExceptionJumps(PsiElement element) { //generate jumps to all handled exception handlers - //if (myCatchBlocks.size() != 0) { - //} Collection unhandledExceptions = ExceptionUtil.collectUnhandledExceptions(element, element.getParent()); for (PsiClassType unhandledException : unhandledExceptions) { ProgressManager.checkCanceled(); @@ -1040,6 +1036,11 @@ class ControlFlowAnalyzer extends JavaJspElementVisitor { generateCheckedExceptionJumps(tryBlock); tryBlock.accept(this); } + PsiResourceList resourceList = statement.getResourceList(); + if (resourceList != null) { + generateCheckedExceptionJumps(resourceList); + resourceList.accept(this); + } //noinspection StatementWithEmptyBody while (myUnhandledExceptionCatchBlocks.pop() != null) ; diff --git a/java/java-impl/src/com/intellij/psi/impl/PsiImplUtil.java b/java/java-impl/src/com/intellij/psi/impl/PsiImplUtil.java index aaa1450e159d..2fa673130c31 100644 --- a/java/java-impl/src/com/intellij/psi/impl/PsiImplUtil.java +++ b/java/java-impl/src/com/intellij/psi/impl/PsiImplUtil.java @@ -185,8 +185,10 @@ public class PsiImplUtil { if (list != null && !list.processDeclarations(processor, state, null, place)) return false; } if (lastParent instanceof PsiCodeBlock) { - final PsiParameterList parameterList = method.getParameterList(); - if (processDeclarationsInParameterList(parameterList, processor, state)) return false; + final PsiParameter[] parameters = method.getParameterList().getParameters(); + for (PsiParameter parameter : parameters) { + if (!processor.execute(parameter, state)) return false; + } } return true; @@ -196,24 +198,17 @@ public class PsiImplUtil { @NotNull final PsiScopeProcessor processor, @NotNull final ResolveState state, final PsiElement lastParent) { - if (lastParent instanceof PsiCodeBlock) { - final PsiParameterList parameterList = statement.getResourceList(); - if (parameterList != null && processDeclarationsInParameterList(parameterList, processor, state)) return false; + final PsiResourceList resourceList = statement.getResourceList(); + if (resourceList != null && lastParent instanceof PsiCodeBlock && lastParent == statement.getTryBlock()) { + final List resources = resourceList.getNamedResources(); + for (PsiLocalVariable resource : resources) { + if (!processor.execute(resource, state)) return false; + } } return true; } - private static boolean processDeclarationsInParameterList(@NotNull final PsiParameterList parameterList, - @NotNull final PsiScopeProcessor processor, - @NotNull final ResolveState state) { - final PsiParameter[] parameters = parameterList.getParameters(); - for (PsiParameter parameter : parameters) { - if (!processor.execute(parameter, state)) return true; - } - return false; - } - public static boolean hasTypeParameters(@NotNull PsiTypeParameterListOwner owner) { final PsiTypeParameterList typeParameterList = owner.getTypeParameterList(); return typeParameterList != null && typeParameterList.getTypeParameters().length != 0; diff --git a/java/java-impl/src/com/intellij/psi/impl/PsiJavaParserFacadeImpl.java b/java/java-impl/src/com/intellij/psi/impl/PsiJavaParserFacadeImpl.java index 083602d0e83f..52d766646901 100644 --- a/java/java-impl/src/com/intellij/psi/impl/PsiJavaParserFacadeImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/PsiJavaParserFacadeImpl.java @@ -60,14 +60,7 @@ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJ private static final JavaParserUtil.ParserWrapper PARAMETER = new JavaParserUtil.ParserWrapper() { @Override public void parse(final PsiBuilder builder) { - DeclarationParser.parseParameter(builder, true, false, false); - } - }; - - private static final JavaParserUtil.ParserWrapper RESOURCE = new JavaParserUtil.ParserWrapper() { - @Override - public void parse(final PsiBuilder builder) { - DeclarationParser.parseParameter(builder, true, false, true); + DeclarationParser.parseParameter(builder, true, false); } }; @@ -239,10 +232,7 @@ public class PsiJavaParserFacadeImpl extends PsiParserFacadeImpl implements PsiJ @NotNull public PsiParameter createParameterFromText(@NotNull final String text, final PsiElement context) throws IncorrectOperationException { - final boolean resource = context instanceof PsiParameterList && - context.getParent() instanceof PsiTryStatement; - final JavaParserUtil.ParserWrapper wrapper = resource ? RESOURCE : PARAMETER; - final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, wrapper, false), context); + final DummyHolder holder = DummyHolderFactory.createHolder(myManager, new JavaDummyElement(text, PARAMETER, false), context); final PsiElement element = SourceTreeToPsiMap.treeElementToPsi(holder.getTreeElement().getFirstChildNode()); if (!(element instanceof PsiParameter)) { throw new IncorrectOperationException("Incorrect parameter \"" + text + "\"."); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java index 4270606ec5e1..7d31f4b1ff3a 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java @@ -127,9 +127,10 @@ public interface JavaElementType { IElementType THROW_STATEMENT = new JavaCompositeElementType("THROW_STATEMENT", PsiThrowStatementImpl.class); IElementType SYNCHRONIZED_STATEMENT = new JavaCompositeElementType("SYNCHRONIZED_STATEMENT", PsiSynchronizedStatementImpl.class); IElementType TRY_STATEMENT = new JavaCompositeElementType("TRY_STATEMENT", PsiTryStatementImpl.class); + IElementType RESOURCE_LIST = new JavaCompositeElementType("RESOURCE_LIST", PsiResourceListImpl.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); - IElementType CATCH_SECTION = new JavaCompositeElementType("CATCH_SECTION", PsiCatchSectionImpl.class); IElementType ANNOTATION_ARRAY_INITIALIZER = new JavaCompositeElementType("ANNOTATION_ARRAY_INITIALIZER", PsiArrayInitializerMemberValueImpl.class); IElementType NAME_VALUE_PAIR = new JavaCompositeElementType("NAME_VALUE_PAIR", PsiNameValuePairImpl.class, true); IElementType ANNOTATION_PARAMETER_LIST = new JavaCompositeElementType("ANNOTATION_PARAMETER_LIST", PsiAnnotationParameterListImpl.class, true); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiLocalVariableImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiLocalVariableImpl.java index 61872ff2f4c2..910c1f171773 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiLocalVariableImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiLocalVariableImpl.java @@ -94,7 +94,7 @@ public class PsiLocalVariableImpl extends CompositePsiElement implements PsiLoca @NotNull public PsiTypeElement getTypeElement() { ASTNode first = getTreeParent().findChildByType(LOCAL_VARIABLE); - return (PsiTypeElement)SourceTreeToPsiMap.treeElementToPsi(first.findChildByType(TYPE)); + return SourceTreeToPsiMap.treeToPsiNotNull(first.findChildByType(TYPE)); } public PsiModifierList getModifierList() { @@ -105,7 +105,8 @@ public class PsiLocalVariableImpl extends CompositePsiElement implements PsiLoca } public boolean hasModifierProperty(@NotNull String name) { - return getModifierList().hasModifierProperty(name); + final PsiModifierList modifierList = getModifierList(); + return modifierList != null && modifierList.hasModifierProperty(name); } public PsiExpression getInitializer() { @@ -304,10 +305,15 @@ public class PsiLocalVariableImpl extends CompositePsiElement implements PsiLoca } } - PsiElement parentElement = getParent(); + final PsiElement parentElement = getParent(); if (parentElement instanceof PsiDeclarationStatement) { return new LocalSearchScope(parentElement.getParent()); } + else if (parentElement instanceof PsiResourceList) { + final PsiElement tryStatement = parentElement.getParent(); + final PsiCodeBlock tryBlock = ((PsiTryStatement)tryStatement).getTryBlock(); + return new LocalSearchScope(tryBlock != null ? tryBlock : tryStatement); + } else { return getManager().getFileManager().getUseScope(this); } @@ -317,6 +323,7 @@ public class PsiLocalVariableImpl extends CompositePsiElement implements PsiLoca final RowIcon baseIcon = createLayeredIcon(Icons.VARIABLE_ICON, ElementPresentationUtil.getFlags(this, false)); return ElementPresentationUtil.addVisibilityIcon(this, flags, baseIcon); } + public PsiType getTypeNoResolve() { return getType(); } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiResourceListImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiResourceListImpl.java new file mode 100644 index 000000000000..f078c02d9514 --- /dev/null +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiResourceListImpl.java @@ -0,0 +1,58 @@ +/* + * Copyright 2000-2011 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 com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class PsiResourceListImpl extends CompositePsiElement implements PsiResourceList { + public PsiResourceListImpl() { + super(JavaElementType.RESOURCE_LIST); + } + + @NotNull + @Override + public List getResources() { + //noinspection unchecked + return PsiTreeUtil.getChildrenOfAnyType(this, PsiExpression.class, PsiLocalVariable.class); + } + + @NotNull + @Override + public List getNamedResources() { + return PsiTreeUtil.getChildrenOfTypeAsList(this, PsiLocalVariable.class); + } + + @Override + public void accept(@NotNull final PsiElementVisitor visitor) { + if (visitor instanceof JavaElementVisitor) { + ((JavaElementVisitor)visitor).visitResourceList(this); + } + else { + visitor.visitElement(this); + } + } + + @Override + public String toString() { + return "PsiResourceList:" + getText(); + } +} diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiTryStatementImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiTryStatementImpl.java index dc23a38cd074..b8bd9a9e373d 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiTryStatementImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiTryStatementImpl.java @@ -93,8 +93,8 @@ public class PsiTryStatementImpl extends CompositePsiElement implements PsiTrySt } @Override - public PsiParameterList getResourceList() { - return PsiTreeUtil.findChildOfType(this, PsiParameterList.class); + public PsiResourceList getResourceList() { + return PsiTreeUtil.findChildOfType(this, PsiResourceList.class); } public ASTNode findChildByRole(int role) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/TryWithResources.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/TryWithResources.java index 750e9bc5cb0e..36187d86a680 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/TryWithResources.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/TryWithResources.java @@ -1,31 +1,45 @@ -import java.io.*; -import java.lang.Exception; - class C { - void m0() throws Exception { - try (FileReader reader = new FileReader(new File("input.txt"))) { - reader.read(); - } catch (Exception e) { - reader = null; - } - reader = null; + static class E extends Exception { } + static class E1 extends E { } + static class E2 extends E { } + static class E3 extends E { } + + static class MyResource implements AutoCloseable { + public MyResource() throws E1 { } + public void doSomething() throws E2 { } + @Override public void close() throws E3 { } } void m1() { - try (final FileReader reader = new FileReader(new File("input.txt"))) { - reader.read(); - } - catch (IOException ignore) { } + try (MyResource r = new MyResource()) { r.doSomething(); } + catch (E1 | E2 | E3 ignore) { } - try (final FileReader reader = new FileReader(new File("input.txt"))) { - System.out.println("Try."); - } - catch (IOException ignore) { } + try (new MyResource()) { } + catch (E1 | E3 ignore) { } } - /*void m2() throws IOException { - try (final FileReader reader = new FileReader(new File("input.txt"))) { - reader.read(); + void m2() throws Exception { + try (Object r = new MyResource()) { } + + try ("resource") { } + } + + void m3() throws Exception { + try (MyResource r = new MyResource()) { + r.doSomething(); } - }*/ + catch (E e) { + r = null; + } + finally { + r = null; + } + r = null; + + MyResource r = null; + + try (MyResource r = new MyResource()) { } + + try (r = new MyResource()) { } + } } \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete10.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete10.txt index c9b9aaf68b6e..2e113f719226 100644 --- a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete10.txt +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete10.txt @@ -1,7 +1,7 @@ PsiJavaFile:TryIncomplete10.java PsiTryStatement PsiKeyword:try('try') - PsiParameterList:( + PsiResourceList:( PsiJavaToken:LPARENTH('(') PsiErrorElement:')' expected diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete11.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete11.txt index 05e296fcc190..11b2811b4174 100644 --- a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete11.txt +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete11.txt @@ -1,7 +1,7 @@ PsiJavaFile:TryIncomplete11.java PsiTryStatement PsiKeyword:try('try') - PsiParameterList:() + PsiResourceList:() PsiJavaToken:LPARENTH('(') PsiErrorElement:Resource definition expected diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete12.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete12.txt index 829a9a293c17..78a125c530b4 100644 --- a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete12.txt +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete12.txt @@ -1,7 +1,7 @@ PsiJavaFile:TryIncomplete12.java PsiTryStatement PsiKeyword:try('try') - PsiParameterList:(;) + PsiResourceList:(;) PsiJavaToken:LPARENTH('(') PsiErrorElement:Resource definition expected diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete13.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete13.txt index 5883e80baf09..598f1ac3e5c0 100644 --- a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete13.txt +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete13.txt @@ -1,18 +1,14 @@ PsiJavaFile:TryIncomplete13.java PsiTryStatement PsiKeyword:try('try') - PsiParameterList:(final ) + PsiResourceList:(final ) PsiJavaToken:LPARENTH('(') PsiModifierList:final PsiKeyword:final('final') - PsiErrorElement:Type expected + PsiErrorElement:Identifier or type expected PsiWhiteSpace(' ') - PsiTypeElement: - - PsiErrorElement:Identifier expected - PsiJavaToken:RPARENTH(')') PsiCodeBlock PsiJavaToken:LBRACE('{') - PsiJavaToken:RBRACE('}') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete14.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete14.txt index 36386acc63b2..88a8aca925b2 100644 --- a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete14.txt +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete14.txt @@ -1,18 +1,11 @@ PsiJavaFile:TryIncomplete14.java PsiTryStatement PsiKeyword:try('try') - PsiParameterList:(R) + PsiResourceList:(int) PsiJavaToken:LPARENTH('(') - PsiModifierList: - - PsiTypeElement:R - PsiJavaCodeReferenceElement:R - PsiIdentifier:R('R') - PsiReferenceParameterList - - PsiErrorElement:Identifier expected - + PsiErrorElement:Resource definition expected + PsiKeyword:int('int') PsiJavaToken:RPARENTH(')') PsiCodeBlock PsiJavaToken:LBRACE('{') - PsiJavaToken:RBRACE('}') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete15.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete15.txt index f38fe8b04bc7..297e7e00cfaf 100644 --- a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete15.txt +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete15.txt @@ -1,9 +1,9 @@ PsiJavaFile:TryIncomplete15.java PsiTryStatement PsiKeyword:try('try') - PsiParameterList:(R r) + PsiResourceList:(R r) PsiJavaToken:LPARENTH('(') - PsiParameter:r + PsiLocalVariable:r PsiModifierList: PsiTypeElement:R diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete16.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete16.txt index d18696953396..4ed639c77577 100644 --- a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete16.txt +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete16.txt @@ -1,9 +1,9 @@ PsiJavaFile:TryIncomplete16.java PsiTryStatement PsiKeyword:try('try') - PsiParameterList:(R r =) + PsiResourceList:(R r =) PsiJavaToken:LPARENTH('(') - PsiParameter:r + PsiLocalVariable:r PsiModifierList: PsiTypeElement:R diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete17.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete17.txt index 4f8626c450a0..4a4aa969fceb 100644 --- a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete17.txt +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete17.txt @@ -1,9 +1,9 @@ PsiJavaFile:TryIncomplete17.java PsiTryStatement PsiKeyword:try('try') - PsiParameterList:(R r = 0;) + PsiResourceList:(R r = 0;) PsiJavaToken:LPARENTH('(') - PsiParameter:r + PsiLocalVariable:r PsiModifierList: PsiTypeElement:R diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryNormal4.txt b/java/java-tests/testData/psi/parser-partial/statements/TryNormal4.txt index 29b541335e1f..0cf390a0fcbd 100644 --- a/java/java-tests/testData/psi/parser-partial/statements/TryNormal4.txt +++ b/java/java-tests/testData/psi/parser-partial/statements/TryNormal4.txt @@ -1,9 +1,9 @@ PsiJavaFile:TryNormal4.java PsiTryStatement PsiKeyword:try('try') - PsiParameterList:(R r = 0) + PsiResourceList:(R r = 0) PsiJavaToken:LPARENTH('(') - PsiParameter:r + PsiLocalVariable:r PsiModifierList: PsiTypeElement:R diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryNormal5.txt b/java/java-tests/testData/psi/parser-partial/statements/TryNormal5.txt index 78c53a180d3c..8c11fdeff9ca 100644 --- a/java/java-tests/testData/psi/parser-partial/statements/TryNormal5.txt +++ b/java/java-tests/testData/psi/parser-partial/statements/TryNormal5.txt @@ -1,9 +1,9 @@ PsiJavaFile:TryNormal5.java PsiTryStatement PsiKeyword:try('try') - PsiParameterList:(R1 r1 = 1; R2 r2 = 2) + PsiResourceList:(R1 r1 = 1; R2 r2 = 2) PsiJavaToken:LPARENTH('(') - PsiParameter:r1 + PsiLocalVariable:r1 PsiModifierList: PsiTypeElement:R1 @@ -20,7 +20,7 @@ PsiJavaFile:TryNormal5.java PsiJavaToken:INTEGER_LITERAL('1') PsiJavaToken:SEMICOLON(';') PsiWhiteSpace(' ') - PsiParameter:r2 + PsiLocalVariable:r2 PsiModifierList: PsiTypeElement:R2 diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryNormal6.txt b/java/java-tests/testData/psi/parser-partial/statements/TryNormal6.txt new file mode 100644 index 000000000000..c82b04a42e85 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryNormal6.txt @@ -0,0 +1,13 @@ +PsiJavaFile:TryNormal6.java + PsiTryStatement + PsiKeyword:try('try') + PsiResourceList:(r) + PsiJavaToken:LPARENTH('(') + PsiReferenceExpression:r + PsiReferenceParameterList + + PsiIdentifier:r('r') + PsiJavaToken:RPARENTH(')') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryNormal7.txt b/java/java-tests/testData/psi/parser-partial/statements/TryNormal7.txt new file mode 100644 index 000000000000..746ded3b71bb --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryNormal7.txt @@ -0,0 +1,17 @@ +PsiJavaFile:TryNormal7.java + PsiTryStatement + PsiKeyword:try('try') + PsiResourceList:(r; null) + PsiJavaToken:LPARENTH('(') + PsiReferenceExpression:r + PsiReferenceParameterList + + PsiIdentifier:r('r') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiLiteralExpression:null + PsiJavaToken:NULL_KEYWORD('null') + PsiJavaToken:RPARENTH(')') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java index 7cf68cf63d66..df12da3f6c62 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java @@ -122,6 +122,8 @@ public class StatementParserTest extends JavaParsingTestCase { public void testTryNormal3() { doParserTestJDK7("try{}catch(A|B e){}"); } public void testTryNormal4() { doParserTestJDK7("try(R r = 0){}"); } public void testTryNormal5() { doParserTestJDK7("try(R1 r1 = 1; R2 r2 = 2){}"); } + public void testTryNormal6() { doParserTestJDK7("try(r){}"); } + public void testTryNormal7() { doParserTestJDK7("try(r; null){}"); } public void testTryIncomplete0() { doParserTest("try"); } public void testTryIncomplete1() { doParserTest("try{}"); } public void testTryIncomplete2() { doParserTest("try{}catch"); } @@ -136,7 +138,7 @@ public class StatementParserTest extends JavaParsingTestCase { public void testTryIncomplete11() { doParserTestJDK7("try(){}"); } public void testTryIncomplete12() { doParserTestJDK7("try(;){}"); } public void testTryIncomplete13() { doParserTestJDK7("try(final ){}"); } - public void testTryIncomplete14() { doParserTestJDK7("try(R){}"); } + public void testTryIncomplete14() { doParserTestJDK7("try(int){}"); } public void testTryIncomplete15() { doParserTestJDK7("try(R r){}"); } public void testTryIncomplete16() { doParserTestJDK7("try(R r =){}"); } public void testTryIncomplete17() { doParserTestJDK7("try(R r = 0;){}"); } diff --git a/java/openapi/src/com/intellij/psi/JavaElementVisitor.java b/java/openapi/src/com/intellij/psi/JavaElementVisitor.java index 810755020371..6325cc5fcd59 100644 --- a/java/openapi/src/com/intellij/psi/JavaElementVisitor.java +++ b/java/openapi/src/com/intellij/psi/JavaElementVisitor.java @@ -238,7 +238,7 @@ public abstract class JavaElementVisitor extends PsiElementVisitor { * of this method we can easily stuck with exponential algorithm if the derived visitor * extends visitElement() and accepts children there. * PsiRecursiveElement visitor works that around and implements this method accordingly. - * All other visitor must decide themselves what implementation (visitReferenceElement() or visitExpreission() or none or LOG.error()) + * All other visitor must decide themselves what implementation (visitReferenceElement() or visitExpression() or none or LOG.error()) * is appropriate for them. * @param expression */ @@ -296,6 +296,10 @@ public abstract class JavaElementVisitor extends PsiElementVisitor { visitElement(section); } + public void visitResourceList(PsiResourceList resourceList) { + visitElement(resourceList); + } + public void visitTypeElement(PsiTypeElement type) { visitElement(type); } diff --git a/java/openapi/src/com/intellij/psi/PsiResourceList.java b/java/openapi/src/com/intellij/psi/PsiResourceList.java new file mode 100644 index 000000000000..55b5cb12b863 --- /dev/null +++ b/java/openapi/src/com/intellij/psi/PsiResourceList.java @@ -0,0 +1,44 @@ +/* + * Copyright 2000-2011 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; + +import java.util.List; + +/** + * Represents a resource list of try-with-resources statement (automatic resource management) introduced in JDK 7. + * + * @see PsiTryStatement#getResourceList() + * @since 10.5. + */ +public interface PsiResourceList extends PsiElement { + /** + * Returns list of resource elements. Each element is either {@link PsiLocalVariable} or {@link PsiExpression}. + * + * @return list of resource elements. + */ + @NotNull + List getResources(); + + /** + * Returns list of named resource elements only. + * + * @return list of named resource elements. + */ + @NotNull + List getNamedResources(); +} diff --git a/java/openapi/src/com/intellij/psi/PsiTryStatement.java b/java/openapi/src/com/intellij/psi/PsiTryStatement.java index 91375d343340..dd5574b5d322 100644 --- a/java/openapi/src/com/intellij/psi/PsiTryStatement.java +++ b/java/openapi/src/com/intellij/psi/PsiTryStatement.java @@ -71,5 +71,5 @@ public interface PsiTryStatement extends PsiStatement { * @return resource list, or null if the statement doesn't have it. */ @Nullable - PsiParameterList getResourceList(); + PsiResourceList getResourceList(); } diff --git a/java/openapi/src/com/intellij/psi/util/PsiUtil.java b/java/openapi/src/com/intellij/psi/util/PsiUtil.java index f34dfee9b11d..de515c55ca48 100644 --- a/java/openapi/src/com/intellij/psi/util/PsiUtil.java +++ b/java/openapi/src/com/intellij/psi/util/PsiUtil.java @@ -15,6 +15,7 @@ */ package com.intellij.psi.util; +import com.intellij.openapi.diagnostic.LogUtil; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.roots.LanguageLevelProjectExtension; import com.intellij.openapi.util.Comparing; @@ -635,6 +636,19 @@ public final class PsiUtil extends PsiUtilBase { return PsiTreeUtil.getParentOfType(element, PsiDocComment.class, true) != null; } + public static boolean isResourceInTryStatement(final PsiElement element) { + return (element instanceof PsiLocalVariable || element instanceof PsiExpression) && + element.getParent() instanceof PsiResourceList; + } + + @Nullable + public static PsiType getResourceType(final PsiElement element) { + if (element == null || !(element.getParent() instanceof PsiResourceList)) return null; + if (element instanceof PsiLocalVariable) return ((PsiLocalVariable)element).getType(); + if (element instanceof PsiExpression) return ((PsiExpression)element).getType(); + LOG.error("Unexpected resource type: " + LogUtil.objectAndClass(element)); + return null; + } private static class ParamWriteProcessor implements Processor { private volatile boolean myIsWriteRefFound = false; @@ -651,6 +665,7 @@ public final class PsiUtil extends PsiUtilBase { return myIsWriteRefFound; } } + public static boolean isAssigned(final PsiParameter parameter) { ParamWriteProcessor processor = new ParamWriteProcessor(); ReferencesSearch.search(parameter, new LocalSearchScope(parameter.getDeclarationScope()), true).forEach(processor); @@ -717,7 +732,7 @@ public final class PsiUtil extends PsiUtilBase { } } - /** + /* * Returns iterator of type parameters visible in owner. Type parameters are iterated in * inner-to-outer, right-to-left order. */ diff --git a/platform/lang-api/src/com/intellij/psi/util/PsiTreeUtil.java b/platform/lang-api/src/com/intellij/psi/util/PsiTreeUtil.java index 92a835d850a4..91063c18bf59 100644 --- a/platform/lang-api/src/com/intellij/psi/util/PsiTreeUtil.java +++ b/platform/lang-api/src/com/intellij/psi/util/PsiTreeUtil.java @@ -312,6 +312,22 @@ public class PsiTreeUtil { return null; } + @NotNull + public static List getChildrenOfAnyType(@Nullable final PsiElement element, + @Nullable final Class... classes) { + if (element == null || classes == null || classes.length == 0) return Collections.emptyList(); + + final List result = new SmartList(); + for (PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()) { + for (Class aClass : classes) { + if (aClass.isInstance(child)) { + result.add(child); + } + } + } + return result; + } + @Nullable public static T getNextSiblingOfType(@Nullable PsiElement sibling, @NotNull Class aClass) { if (sibling == null) return null;