Project Coin try-with-resource support, take 2

This commit is contained in:
Roman Shevchenko
2011-02-21 19:55:45 +01:00
parent 55151743c5
commit e84bc3af06
31 changed files with 404 additions and 174 deletions
@@ -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.<PsiClassType>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<PsiClassType> unhandled = getUnhandledCloserExceptions(element, resourceClass, topElement);
if (unhandledExceptions == null) {
unhandledExceptions = unhandled;
}
else {
unhandledExceptions.addAll(unhandled);
}
}
}
}
if (unhandledExceptions != null) {
if (foundExceptions == null) {
foundExceptions = new THashSet<PsiClassType>();
@@ -309,15 +326,28 @@ public class ExceptionUtil {
}
@NotNull
public static List<PsiClassType> getUnhandledExceptions(PsiCallExpression methodCall, PsiElement topElement) {
public static List<PsiClassType> 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<PsiSubstitutor>() {
public PsiSubstitutor compute() {
return result.getSubstitutor();
}
}));
final PsiMethod method = (PsiMethod)result.getElement();
final PsiSubstitutor substitutor = ApplicationManager.getApplication().runReadAction(new Computable<PsiSubstitutor>() {
public PsiSubstitutor compute() {
return result.getSubstitutor();
}
});
return getUnhandledExceptions(method, methodCall, topElement, substitutor);
}
@NotNull
public static List<PsiClassType> 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<PsiClassType> getUnhandledExceptions(PsiMethod method,
PsiElement element,
@@ -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;
@@ -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));
}
}
}
}
@@ -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<PsiBuilder.Marker, Boolean> 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"));
}
}
}
@@ -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"));
}
@@ -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<PsiClassType> 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) ;
@@ -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<PsiLocalVariable> 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;
@@ -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 + "\".");
@@ -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);
@@ -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();
}
@@ -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<? extends PsiElement> getResources() {
//noinspection unchecked
return PsiTreeUtil.getChildrenOfAnyType(this, PsiExpression.class, PsiLocalVariable.class);
}
@NotNull
@Override
public List<PsiLocalVariable> 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();
}
}
@@ -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) {
@@ -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) {
<error descr="Cannot resolve symbol 'reader'">reader</error> = null;
}
<error descr="Cannot resolve symbol 'reader'">reader</error> = 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 (<error descr="Incompatible types. Found: 'java.lang.Object', required: 'java.lang.AutoCloseable'">Object r = new MyResource()</error>) { }
try (<error descr="Incompatible types. Found: 'java.lang.String', required: 'java.lang.AutoCloseable'">"resource"</error>) { }
}
void m3() throws Exception {
try (MyResource r = new MyResource()) {
r.doSomething();
}
}*/
catch (E e) {
<error descr="Cannot resolve symbol 'r'">r</error> = null;
}
finally {
<error descr="Cannot resolve symbol 'r'">r</error> = null;
}
<error descr="Cannot resolve symbol 'r'">r</error> = null;
MyResource r = null;
try (MyResource <error descr="Variable 'r' is already defined in the scope">r</error> = new MyResource()) { }
try (r = new MyResource()) { }
}
}
@@ -1,7 +1,7 @@
PsiJavaFile:TryIncomplete10.java
PsiTryStatement
PsiKeyword:try('try')
PsiParameterList:(
PsiResourceList:(
PsiJavaToken:LPARENTH('(')
PsiErrorElement:')' expected
<empty list>
@@ -1,7 +1,7 @@
PsiJavaFile:TryIncomplete11.java
PsiTryStatement
PsiKeyword:try('try')
PsiParameterList:()
PsiResourceList:()
PsiJavaToken:LPARENTH('(')
PsiErrorElement:Resource definition expected
<empty list>
@@ -1,7 +1,7 @@
PsiJavaFile:TryIncomplete12.java
PsiTryStatement
PsiKeyword:try('try')
PsiParameterList:(;)
PsiResourceList:(;)
PsiJavaToken:LPARENTH('(')
PsiErrorElement:Resource definition expected
<empty list>
@@ -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
<empty list>
PsiWhiteSpace(' ')
PsiTypeElement:
<empty list>
PsiErrorElement:Identifier expected
<empty list>
PsiJavaToken:RPARENTH(')')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
PsiJavaToken:RBRACE('}')
@@ -1,18 +1,11 @@
PsiJavaFile:TryIncomplete14.java
PsiTryStatement
PsiKeyword:try('try')
PsiParameterList:(R)
PsiResourceList:(int)
PsiJavaToken:LPARENTH('(')
PsiModifierList:
<empty list>
PsiTypeElement:R
PsiJavaCodeReferenceElement:R
PsiIdentifier:R('R')
PsiReferenceParameterList
<empty list>
PsiErrorElement:Identifier expected
<empty list>
PsiErrorElement:Resource definition expected
PsiKeyword:int('int')
PsiJavaToken:RPARENTH(')')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
PsiJavaToken:RBRACE('}')
@@ -1,9 +1,9 @@
PsiJavaFile:TryIncomplete15.java
PsiTryStatement
PsiKeyword:try('try')
PsiParameterList:(R r)
PsiResourceList:(R r)
PsiJavaToken:LPARENTH('(')
PsiParameter:r
PsiLocalVariable:r
PsiModifierList:
<empty list>
PsiTypeElement:R
@@ -1,9 +1,9 @@
PsiJavaFile:TryIncomplete16.java
PsiTryStatement
PsiKeyword:try('try')
PsiParameterList:(R r =)
PsiResourceList:(R r =)
PsiJavaToken:LPARENTH('(')
PsiParameter:r
PsiLocalVariable:r
PsiModifierList:
<empty list>
PsiTypeElement:R
@@ -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:
<empty list>
PsiTypeElement:R
@@ -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:
<empty list>
PsiTypeElement:R
@@ -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:
<empty list>
PsiTypeElement:R1
@@ -20,7 +20,7 @@ PsiJavaFile:TryNormal5.java
PsiJavaToken:INTEGER_LITERAL('1')
PsiJavaToken:SEMICOLON(';')
PsiWhiteSpace(' ')
PsiParameter:r2
PsiLocalVariable:r2
PsiModifierList:
<empty list>
PsiTypeElement:R2
@@ -0,0 +1,13 @@
PsiJavaFile:TryNormal6.java
PsiTryStatement
PsiKeyword:try('try')
PsiResourceList:(r)
PsiJavaToken:LPARENTH('(')
PsiReferenceExpression:r
PsiReferenceParameterList
<empty list>
PsiIdentifier:r('r')
PsiJavaToken:RPARENTH(')')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,17 @@
PsiJavaFile:TryNormal7.java
PsiTryStatement
PsiKeyword:try('try')
PsiResourceList:(r; null)
PsiJavaToken:LPARENTH('(')
PsiReferenceExpression:r
PsiReferenceParameterList
<empty list>
PsiIdentifier:r('r')
PsiJavaToken:SEMICOLON(';')
PsiWhiteSpace(' ')
PsiLiteralExpression:null
PsiJavaToken:NULL_KEYWORD('null')
PsiJavaToken:RPARENTH(')')
PsiCodeBlock
PsiJavaToken:LBRACE('{')
PsiJavaToken:RBRACE('}')
@@ -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;){}"); }
@@ -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);
}
@@ -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<? extends PsiElement> getResources();
/**
* Returns list of named resource elements only.
*
* @return list of named resource elements.
*/
@NotNull
List<PsiLocalVariable> getNamedResources();
}
@@ -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();
}
@@ -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<PsiReference> {
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.
*/
@@ -312,6 +312,22 @@ public class PsiTreeUtil {
return null;
}
@NotNull
public static List<? extends PsiElement> getChildrenOfAnyType(@Nullable final PsiElement element,
@Nullable final Class<? extends PsiElement>... classes) {
if (element == null || classes == null || classes.length == 0) return Collections.emptyList();
final List<PsiElement> result = new SmartList<PsiElement>();
for (PsiElement child = element.getFirstChild(); child != null; child = child.getNextSibling()) {
for (Class<? extends PsiElement> aClass : classes) {
if (aClass.isInstance(child)) {
result.add(child);
}
}
}
return result;
}
@Nullable
public static <T extends PsiElement> T getNextSiblingOfType(@Nullable PsiElement sibling, @NotNull Class<T> aClass) {
if (sibling == null) return null;