From e182ea6feee2ab8a7b964574ee88f8e9e1031366 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Mon, 18 Apr 2011 18:06:04 +0200 Subject: [PATCH] IDEA-66564 (parse indeterminate FP literals in compiled annotations, ultimate fix) --- .../impl/compiled/ClassFileStubBuilder.java | 2 +- .../compiled/ClsBinaryExpressionImpl.java | 126 ++++++++++++++++++ .../psi/impl/compiled/ClsJavaTokenImpl.java | 78 +++++++++++ .../compiled/ClsLiteralExpressionImpl.java | 4 +- .../psi/impl/compiled/ClsParsingUtil.java | 26 +++- .../compiled/ClsPrefixExpressionImpl.java | 67 ++++------ .../psi/impl/compiled/ClsStubBuilder.java | 28 ++-- .../intellij/psi/ClsRepositoryUseTest.java | 6 +- 8 files changed, 275 insertions(+), 62 deletions(-) create mode 100644 java/java-impl/src/com/intellij/psi/impl/compiled/ClsBinaryExpressionImpl.java create mode 100644 java/java-impl/src/com/intellij/psi/impl/compiled/ClsJavaTokenImpl.java diff --git a/java/java-impl/src/com/intellij/psi/impl/compiled/ClassFileStubBuilder.java b/java/java-impl/src/com/intellij/psi/impl/compiled/ClassFileStubBuilder.java index 0f39171b98ee..3e5c1be661c0 100644 --- a/java/java-impl/src/com/intellij/psi/impl/compiled/ClassFileStubBuilder.java +++ b/java/java-impl/src/com/intellij/psi/impl/compiled/ClassFileStubBuilder.java @@ -53,7 +53,7 @@ public class ClassFileStubBuilder implements BinaryFileStubBuilder { } public int getStubVersion() { - return JavaFileElementType.STUB_VERSION + 2; + return JavaFileElementType.STUB_VERSION + 3; } diff --git a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsBinaryExpressionImpl.java b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsBinaryExpressionImpl.java new file mode 100644 index 000000000000..92c3e9b56653 --- /dev/null +++ b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsBinaryExpressionImpl.java @@ -0,0 +1,126 @@ +/* + * 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.compiled; + +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.psi.impl.source.tree.JavaElementType; +import com.intellij.psi.impl.source.tree.TreeElement; +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NotNull; + +public class ClsBinaryExpressionImpl extends ClsElementImpl implements PsiBinaryExpression { + private final ClsElementImpl myParent; + private final PsiExpression myLOperand; + private final PsiJavaToken myOperation; + private final PsiExpression myROperand; + + public ClsBinaryExpressionImpl(ClsElementImpl parent, + ClsLiteralExpressionImpl lOperand, + ClsJavaTokenImpl operation, + ClsLiteralExpressionImpl rOperand) { + myParent = parent; + myLOperand = lOperand; + myOperation = operation; + myROperand = rOperand; + lOperand.setParent(this); + operation.setParent(this); + rOperand.setParent(this); + } + + public ClsBinaryExpressionImpl(ClsElementImpl parent, + ClsPrefixExpressionImpl lOperand, + ClsJavaTokenImpl operation, + ClsLiteralExpressionImpl rOperand) { + myParent = parent; + myLOperand = lOperand; + myOperation = operation; + myROperand = rOperand; + lOperand.setParent(this); + operation.setParent(this); + rOperand.setParent(this); + } + + @Override + public void appendMirrorText(int indentLevel, StringBuilder buffer) { + buffer.append(getText()); + } + + @Override + public void setMirror(@NotNull TreeElement element) { + setMirrorCheckingType(element, JavaElementType.BINARY_EXPRESSION); + } + + @Override + public String getText() { + return StringUtil.join(myLOperand.getText(), " ", myOperation.getText(), " ", myROperand.getText()); + } + + @NotNull + @Override + public PsiElement[] getChildren() { + return new PsiElement[]{myLOperand, myOperation, myROperand}; + } + + @Override + public PsiElement getParent() { + return myParent; + } + + @Override + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof JavaElementVisitor) { + ((JavaElementVisitor)visitor).visitBinaryExpression(this); + } + else { + visitor.visitElement(this); + } + } + + @NotNull + @Override + public PsiExpression getLOperand() { + return myLOperand; + } + + @NotNull + @Override + public PsiExpression getROperand() { + return myROperand; + } + + @NotNull + @Override + public PsiJavaToken getOperationSign() { + return myOperation; + } + + @NotNull + @Override + public IElementType getOperationTokenType() { + return myOperation.getTokenType(); + } + + @Override + public PsiType getType() { + return myLOperand.getType(); + } + + @Override + public String toString() { + return "PsiBinaryExpression:" + getText(); + } +} diff --git a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsJavaTokenImpl.java b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsJavaTokenImpl.java new file mode 100644 index 000000000000..884bd72136e0 --- /dev/null +++ b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsJavaTokenImpl.java @@ -0,0 +1,78 @@ +/* + * 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.compiled; + +import com.intellij.psi.*; +import com.intellij.psi.impl.source.tree.TreeElement; +import com.intellij.psi.tree.IElementType; +import org.jetbrains.annotations.NotNull; + +public class ClsJavaTokenImpl extends ClsElementImpl implements PsiJavaToken { + private ClsElementImpl myParent; + private final IElementType myTokenType; + private final String myTokenText; + + public ClsJavaTokenImpl(ClsElementImpl parent, IElementType tokenType, String tokenText) { + myParent = parent; + myTokenType = tokenType; + myTokenText = tokenText; + } + + void setParent(ClsElementImpl parent) { + myParent = parent; + } + + @Override + public IElementType getTokenType() { + return myTokenType; + } + + @Override + public String getText() { + return myTokenText; + } + + @NotNull + @Override + public PsiElement[] getChildren() { + return EMPTY_ARRAY; + } + + @Override + public PsiElement getParent() { + return myParent; + } + + @Override + public void appendMirrorText(final int indentLevel, final StringBuilder buffer) { + buffer.append(getText()); + } + + @Override + public void setMirror(@NotNull TreeElement element) { + setMirrorCheckingType(element, myTokenType); + } + + @Override + public void accept(@NotNull PsiElementVisitor visitor) { + if (visitor instanceof JavaElementVisitor) { + ((JavaElementVisitor)visitor).visitJavaToken(this); + } + else { + visitor.visitElement(this); + } + } +} diff --git a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsLiteralExpressionImpl.java b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsLiteralExpressionImpl.java index 0a80cd27aec8..d2a89da39356 100644 --- a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsLiteralExpressionImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsLiteralExpressionImpl.java @@ -16,7 +16,7 @@ package com.intellij.psi.impl.compiled; import com.intellij.psi.*; -import com.intellij.psi.impl.source.tree.ElementType; +import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.impl.source.tree.TreeElement; import org.jetbrains.annotations.NotNull; @@ -58,7 +58,7 @@ public class ClsLiteralExpressionImpl extends ClsElementImpl implements PsiLiter } public void setMirror(@NotNull TreeElement element) { - setMirrorCheckingType(element, ElementType.LITERAL_EXPRESSION); + setMirrorCheckingType(element, JavaElementType.LITERAL_EXPRESSION); } @NotNull diff --git a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsParsingUtil.java b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsParsingUtil.java index 1b2bcb518d72..4e6e697acce3 100644 --- a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsParsingUtil.java +++ b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsParsingUtil.java @@ -122,11 +122,10 @@ public class ClsParsingUtil { return new ClsLiteralExpressionImpl(parent, expr.getText(), expr.getType(), ((PsiLiteralExpression)expr).getValue()); } else if (expr instanceof PsiPrefixExpression) { - final PsiExpression operand = ((PsiPrefixExpression) expr).getOperand(); - final ClsLiteralExpressionImpl literal = (ClsLiteralExpressionImpl) psiToClsExpression(operand, null); - final ClsPrefixExpressionImpl prefixExpression = new ClsPrefixExpressionImpl(parent, literal); - literal.setParent(prefixExpression); - return prefixExpression; + final PsiPrefixExpression prefixExpr = (PsiPrefixExpression)expr; + final ClsJavaTokenImpl operation = new ClsJavaTokenImpl(null, prefixExpr.getOperationTokenType(), prefixExpr.getOperationSign().getText()); + final ClsLiteralExpressionImpl literal = (ClsLiteralExpressionImpl) psiToClsExpression(prefixExpr.getOperand(), null); + return new ClsPrefixExpressionImpl(parent, operation, literal); } else if (expr instanceof PsiClassObjectAccessExpression) { final String canonicalClassText = ((PsiClassObjectAccessExpression)expr).getOperand().getType().getCanonicalText(); @@ -135,14 +134,27 @@ public class ClsParsingUtil { else if (expr instanceof PsiReferenceExpression) { return new ClsReferenceExpressionImpl(parent, (PsiReferenceExpression)expr); } + else if (expr instanceof PsiBinaryExpression) { + final PsiBinaryExpression binaryExpr = (PsiBinaryExpression)expr; + final PsiExpression lOperand = psiToClsExpression(binaryExpr.getLOperand(), null); + final ClsJavaTokenImpl operation = new ClsJavaTokenImpl(null, binaryExpr.getOperationTokenType(), binaryExpr.getOperationSign().getText()); + final PsiExpression rOperand = psiToClsExpression(binaryExpr.getROperand(), null); + if (lOperand instanceof ClsLiteralExpressionImpl) { + return new ClsBinaryExpressionImpl(parent, (ClsLiteralExpressionImpl)lOperand, operation, (ClsLiteralExpressionImpl)rOperand); + } + else if (lOperand instanceof ClsPrefixExpressionImpl) { + return new ClsBinaryExpressionImpl(parent, (ClsPrefixExpressionImpl)lOperand, operation, (ClsLiteralExpressionImpl)rOperand); + } + } else { final PsiConstantEvaluationHelper evaluator = JavaPsiFacade.getInstance(expr.getProject()).getConstantEvaluationHelper(); final Object value = evaluator.computeConstantExpression(expr); if (value != null) { return new ClsLiteralExpressionImpl(parent, expr.getText(), expr.getType(), value); } - LOG.error("Unable to compute expression value: " + expr); - return null; } + + LOG.error("Unable to compute expression value: " + expr); + return null; } } diff --git a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsPrefixExpressionImpl.java b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsPrefixExpressionImpl.java index a3fbcd5f3028..5e7824aab71c 100644 --- a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsPrefixExpressionImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsPrefixExpressionImpl.java @@ -15,6 +15,7 @@ */ package com.intellij.psi.impl.compiled; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.impl.source.tree.TreeElement; @@ -22,55 +23,72 @@ import com.intellij.psi.tree.IElementType; import org.jetbrains.annotations.NotNull; public class ClsPrefixExpressionImpl extends ClsElementImpl implements PsiPrefixExpression { - private final ClsElementImpl myParent; + private ClsElementImpl myParent; + private final PsiJavaToken myOperation; private final PsiExpression myOperand; - private final MySign mySign = new MySign(); - - public ClsPrefixExpressionImpl(ClsElementImpl parent, PsiExpression operand) { + public ClsPrefixExpressionImpl(ClsElementImpl parent, ClsJavaTokenImpl operation, ClsLiteralExpressionImpl operand) { myParent = parent; + myOperation = operation; myOperand = operand; + operation.setParent(this); + operand.setParent(this); } + void setParent(ClsElementImpl parent) { + myParent = parent; + } + + @NotNull + @Override public PsiExpression getOperand() { return myOperand; } @NotNull + @Override public PsiJavaToken getOperationSign() { - return mySign; + return myOperation; } @NotNull + @Override public IElementType getOperationTokenType() { - return getOperationSign().getTokenType(); + return myOperation.getTokenType(); } + @Override public PsiType getType() { return myOperand.getType(); } + @Override public PsiElement getParent() { return myParent; } @NotNull + @Override public PsiElement[] getChildren() { - return new PsiElement[]{getOperationSign(), getOperand()}; + return new PsiElement[]{myOperation, myOperand}; } + @Override public String getText() { - return "-" + myOperand.getText(); + return StringUtil.join(myOperation.getText(), myOperand.getText()); } + @Override public void appendMirrorText(final int indentLevel, final StringBuilder buffer) { buffer.append(getText()); } + @Override public void setMirror(@NotNull TreeElement element) { setMirrorCheckingType(element, JavaElementType.PREFIX_EXPRESSION); } + @Override public void accept(@NotNull PsiElementVisitor visitor) { if (visitor instanceof JavaElementVisitor) { ((JavaElementVisitor)visitor).visitPrefixExpression(this); @@ -80,39 +98,8 @@ public class ClsPrefixExpressionImpl extends ClsElementImpl implements PsiPrefix } } + @Override public String toString() { return "PsiPrefixExpression:" + getText(); } - - private class MySign extends ClsElementImpl implements PsiJavaToken { - public IElementType getTokenType() { - return JavaTokenType.MINUS; - } - - @NotNull - public PsiElement[] getChildren() { - return EMPTY_ARRAY; - } - - public PsiElement getParent() { - return ClsPrefixExpressionImpl.this; - } - - public void appendMirrorText(final int indentLevel, final StringBuilder buffer) { - buffer.append("-"); - } - - public void setMirror(@NotNull TreeElement element) { - setMirrorCheckingType(element, JavaTokenType.MINUS); - } - - public void accept(@NotNull PsiElementVisitor visitor) { - if (visitor instanceof JavaElementVisitor) { - ((JavaElementVisitor)visitor).visitJavaToken(this); - } - else { - visitor.visitElement(this); - } - } - } } diff --git a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsStubBuilder.java b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsStubBuilder.java index 21d8d81452d5..50c1cf33e946 100644 --- a/java/java-impl/src/com/intellij/psi/impl/compiled/ClsStubBuilder.java +++ b/java/java-impl/src/com/intellij/psi/impl/compiled/ClsStubBuilder.java @@ -55,6 +55,14 @@ import java.util.regex.Pattern; public class ClsStubBuilder { private static final Pattern REGEX_PATTERN = Pattern.compile("(?<=[^\\$])\\${1}(?=[^\\$])"); + public static final String DOUBLE_POSITIVE_INF = "1.0 / 0.0"; + public static final String DOUBLE_NEGATIVE_INF = "-1.0 / 0.0"; + public static final String DOUBLE_NAN = "0.0d / 0.0"; + + public static final String FLOAT_POSITIVE_INF = "1.0f / 0.0"; + public static final String FLOAT_NEGATIVE_INF = "-1.0f / 0.0"; + public static final String FLOAT_NAN = "0.0f / 0.0"; + private ClsStubBuilder() { } @Nullable @@ -679,26 +687,28 @@ public class ClsStubBuilder { if (value instanceof Long) return value.toString() + "L"; if (value instanceof Double) { - final double v = ((Double)value).doubleValue(); - if (Double.isInfinite(v)) { - return StringUtil.join(CommonClassNames.JAVA_LANG_DOUBLE, ".", (v > 0 ? "POSITIVE_INFINITY" : "NEGATIVE_INFINITY")); + final double d = ((Double)value).doubleValue(); + if (Double.isInfinite(d)) { + return d > 0 ? DOUBLE_POSITIVE_INF : DOUBLE_NEGATIVE_INF; } - else if (Double.isNaN(v)) { - return StringUtil.join(CommonClassNames.JAVA_LANG_DOUBLE, ".", "NaN"); + else if (Double.isNaN(d)) { + return DOUBLE_NAN; } - return Double.toString(v); + return Double.toString(d); } if (value instanceof Float) { final float v = ((Float)value).floatValue(); if (Float.isInfinite(v)) { - return StringUtil.join(CommonClassNames.JAVA_LANG_FLOAT, ".", (v > 0 ? "POSITIVE_INFINITY" : "NEGATIVE_INFINITY")); + return v > 0 ? FLOAT_POSITIVE_INF : FLOAT_NEGATIVE_INF; } else if (Float.isNaN(v)) { - return StringUtil.join(CommonClassNames.JAVA_LANG_FLOAT, ".", "NaN"); + return FLOAT_NAN; + } + else { + return Float.toString(v) + "f"; } - return Float.toString(v) + "f"; } return null; diff --git a/java/java-tests/testSrc/com/intellij/psi/ClsRepositoryUseTest.java b/java/java-tests/testSrc/com/intellij/psi/ClsRepositoryUseTest.java index 73e361f29f84..5dd23cd3f91e 100644 --- a/java/java-tests/testSrc/com/intellij/psi/ClsRepositoryUseTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/ClsRepositoryUseTest.java @@ -664,9 +664,9 @@ public class ClsRepositoryUseTest extends PsiTestCase{ assert method instanceof PsiAnnotationMethod : method; try { final PsiAnnotationMemberValue defaultValue = ((PsiAnnotationMethod)method).getDefaultValue(); - assert defaultValue instanceof PsiReferenceExpression : defaultValue; - final String type = method.getName().startsWith("f") ? "Float." : "Double"; - assert defaultValue.getText().contains(type) : defaultValue; + assert defaultValue instanceof PsiBinaryExpression : defaultValue; + final PsiPrimitiveType type = method.getName().startsWith("f") ? PsiType.FLOAT : PsiType.DOUBLE; + assertEquals(type, ((PsiBinaryExpression)defaultValue).getType()); } catch (Exception e) { final String valueText = ((PsiMethodStub)((StubBasedPsiElement)method).getStub()).getDefaultValueText();