IDEA-66564 (parse indeterminate FP literals in compiled annotations, ultimate fix)

This commit is contained in:
Roman Shevchenko
2011-04-18 18:49:31 +02:00
parent 03bfc64512
commit e182ea6fee
8 changed files with 275 additions and 62 deletions
@@ -53,7 +53,7 @@ public class ClassFileStubBuilder implements BinaryFileStubBuilder {
}
public int getStubVersion() {
return JavaFileElementType.STUB_VERSION + 2;
return JavaFileElementType.STUB_VERSION + 3;
}
@@ -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();
}
}
@@ -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);
}
}
}
@@ -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
@@ -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;
}
}
@@ -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);
}
}
}
}
@@ -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;
@@ -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();