mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
type & value inference for literals
This commit is contained in:
+1
-1
@@ -168,7 +168,7 @@ public class StringLiteralCopyPasteProcessor implements CopyPastePreProcessor {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiElement findLiteralTokenType(PsiFile file, int selectionStart, int selectionEnd) {
|
||||
protected PsiElement findLiteralTokenType(PsiFile file, int selectionStart, int selectionEnd) {
|
||||
final PsiElement elementAtSelectionStart = file.findElementAt(selectionStart);
|
||||
if (elementAtSelectionStart == null) {
|
||||
return null;
|
||||
|
||||
@@ -860,17 +860,25 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator {
|
||||
return;
|
||||
}
|
||||
|
||||
for (String part : parts) {
|
||||
if (!GrStringUtil.parseStringCharacters(part, new StringBuilder(text.length()), null, !quote.equals("/"))) {
|
||||
myHolder.createErrorAnnotation(literal, GroovyBundle.message("illegal.escape.character.in.string.literal"));
|
||||
return;
|
||||
for (String part : parts) {
|
||||
if (literal instanceof GrRegex) {
|
||||
if (!GrStringUtil.parseRegexCharacters(part, new StringBuilder(text.length()), null, literal.getText().startsWith("/"))) {
|
||||
myHolder.createErrorAnnotation(literal, GroovyBundle.message("illegal.escape.character.in.string.literal"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!GrStringUtil.parseStringCharacters(part, new StringBuilder(text.length()), null)) {
|
||||
myHolder.createErrorAnnotation(literal, GroovyBundle.message("illegal.escape.character.in.string.literal"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (isSimpleString) {
|
||||
int[] offsets = new int[substring.length() + 1];
|
||||
boolean result = GrStringUtil.parseStringCharacters(substring, builder, offsets, !quote.equals("/"));
|
||||
boolean result = GrStringUtil.parseStringCharacters(substring, builder, offsets);
|
||||
LOG.assertTrue(result);
|
||||
if (!builder.toString().endsWith(quote) || substring.charAt(offsets[builder.length() - quote.length()]) == '\\') {
|
||||
myHolder.createErrorAnnotation(literal, GroovyBundle.message("string.end.expected"));
|
||||
@@ -893,7 +901,7 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator {
|
||||
}
|
||||
}
|
||||
|
||||
if (literal instanceof GrRegex) {
|
||||
if (literal instanceof GrRegex && ((GrRegex)literal).getInjections().length > 0) {
|
||||
if (!GroovyConfigUtils.getInstance().isVersionAtLeast(literal, GroovyConfigUtils.GROOVY1_8)) {
|
||||
myHolder.createErrorAnnotation(literal, GroovyBundle.message("slashy.strings.with.injections.are.not.allowed.in.groovy.0",
|
||||
GroovyConfigUtils.getInstance().getSDKVersion(literal)));
|
||||
|
||||
-1
@@ -110,7 +110,6 @@ public class TypesUtil {
|
||||
static {
|
||||
ourPrimitiveTypesToClassNames.put(mSTRING_LITERAL, JAVA_LANG_STRING);
|
||||
ourPrimitiveTypesToClassNames.put(mGSTRING_LITERAL, JAVA_LANG_STRING);
|
||||
ourPrimitiveTypesToClassNames.put(mREGEX_LITERAL, JAVA_LANG_STRING);
|
||||
ourPrimitiveTypesToClassNames.put(mNUM_INT, JAVA_LANG_INTEGER);
|
||||
ourPrimitiveTypesToClassNames.put(mNUM_LONG, JAVA_LANG_LONG);
|
||||
ourPrimitiveTypesToClassNames.put(mNUM_FLOAT, JAVA_LANG_FLOAT);
|
||||
|
||||
+1
-8
@@ -19,9 +19,7 @@ package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.litera
|
||||
import com.intellij.openapi.util.ProperTextRange;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.LiteralTextEscaper;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GrStringUtil;
|
||||
|
||||
public class GrLiteralEscaper extends LiteralTextEscaper<GrLiteralImpl> {
|
||||
@@ -36,7 +34,7 @@ public class GrLiteralEscaper extends LiteralTextEscaper<GrLiteralImpl> {
|
||||
ProperTextRange.assertProperRange(rangeInsideHost);
|
||||
String subText = rangeInsideHost.substring(myHost.getText());
|
||||
outSourceOffsets = new int[subText.length() + 1];
|
||||
return GrStringUtil.parseStringCharacters(subText, outChars, outSourceOffsets, isStrictBackSlash());
|
||||
return GrStringUtil.parseStringCharacters(subText, outChars, outSourceOffsets);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,11 +44,6 @@ public class GrLiteralEscaper extends LiteralTextEscaper<GrLiteralImpl> {
|
||||
return (result <= rangeInsideHost.getLength() ? result : rangeInsideHost.getLength()) + rangeInsideHost.getStartOffset();
|
||||
}
|
||||
|
||||
protected boolean isStrictBackSlash() {
|
||||
PsiElement child = myHost.getFirstChild();
|
||||
return child == null || myHost.getNode().getElementType() != GroovyElementTypes.REGEX;
|
||||
}
|
||||
|
||||
public boolean isOneLine() {
|
||||
final Object value = myHost.getValue();
|
||||
return value instanceof String && ((String)value).indexOf('\n') < 0;
|
||||
|
||||
+4
-20
@@ -17,7 +17,6 @@
|
||||
package org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.literals;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry;
|
||||
import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference;
|
||||
@@ -31,7 +30,6 @@ import org.jetbrains.plugins.groovy.lang.lexer.TokenSets;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrExpressionImpl;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GrStringUtil;
|
||||
|
||||
@@ -112,30 +110,16 @@ public class GrLiteralImpl extends GrAbstractLiteral implements GrLiteral, PsiLa
|
||||
}
|
||||
else if (elemType == mSTRING_LITERAL) {
|
||||
if (!text.startsWith("'")) return null;
|
||||
text = text.substring(1);
|
||||
if (text.endsWith("'")) {
|
||||
text = text.substring(0, text.length() - 1);
|
||||
}
|
||||
text = GrStringUtil.removeQuotes(text);
|
||||
StringBuilder chars = new StringBuilder(text.length());
|
||||
boolean result = GrStringUtil.parseStringCharacters(text, chars, null, true);
|
||||
boolean result = GrStringUtil.parseStringCharacters(text, chars, null);
|
||||
return result ? chars.toString() : null;
|
||||
}
|
||||
else if (elemType == mGSTRING_LITERAL) {
|
||||
if (!text.startsWith("\"")) return null;
|
||||
if (text.startsWith("\"\"\"")) {
|
||||
text = StringUtil.trimEnd(text.substring(3), "\"\"\"");
|
||||
}
|
||||
else {
|
||||
text = StringUtil.trimEnd(text.substring(1), "\"");
|
||||
}
|
||||
text = GrStringUtil.removeQuotes(text);
|
||||
StringBuilder chars = new StringBuilder(text.length());
|
||||
boolean result = GrStringUtil.parseStringCharacters(text, chars, null, true);
|
||||
return result ? chars.toString() : null;
|
||||
}
|
||||
else if (elemType == mREGEX_LITERAL) {
|
||||
text = StringUtil.trimStart(StringUtil.trimEnd(text, "/"), "/");
|
||||
StringBuilder chars = new StringBuilder(text.length());
|
||||
boolean result = GrStringUtil.parseStringCharacters(text, chars, null, false);
|
||||
boolean result = GrStringUtil.parseStringCharacters(text, chars, null);
|
||||
return result ? chars.toString() : null;
|
||||
}
|
||||
return null;
|
||||
|
||||
+19
-1
@@ -22,6 +22,9 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrRegex;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GrStringUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author ilyas
|
||||
@@ -58,7 +61,22 @@ public class GrRegexImpl extends GrStringImpl implements GrRegex {
|
||||
return null;
|
||||
}
|
||||
|
||||
return child.getText();
|
||||
final StringBuilder chars = new StringBuilder();
|
||||
final boolean isDollarSlash = child.getNode().getElementType() == GroovyTokenTypes.mREGEX_CONTENT;
|
||||
GrStringUtil.parseRegexCharacters(child.getText(), chars, null, isDollarSlash);
|
||||
return chars.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getTextParts() {
|
||||
List<PsiElement> parts = findChildrenByType(GroovyTokenTypes.mREGEX_CONTENT);
|
||||
|
||||
String[] result = new String[parts.size()];
|
||||
int i = 0;
|
||||
for (PsiElement part : parts) {
|
||||
result[i++] = part.getText();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-2
@@ -27,7 +27,7 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyElementVisitor;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrString;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrStringInjection;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.GrExpressionImpl;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GrStringUtil;
|
||||
import org.jetbrains.plugins.groovy.lang.psi.util.GroovyCommonClassNames;
|
||||
|
||||
import java.util.List;
|
||||
@@ -86,6 +86,17 @@ public class GrStringImpl extends GrAbstractLiteral implements GrString {
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return null;
|
||||
if (findChildByClass(GrStringInjection.class) != null) return null;
|
||||
|
||||
final PsiElement fchild = getFirstChild();
|
||||
if (fchild == null) return null;
|
||||
|
||||
final PsiElement content = fchild.getNextSibling();
|
||||
if (content == null || content.getNode().getElementType() != GroovyTokenTypes.mGSTRING_CONTENT) return null;
|
||||
|
||||
final String text = content.getText();
|
||||
StringBuilder chars = new StringBuilder(text.length());
|
||||
boolean result = GrStringUtil.parseStringCharacters(text, chars, null);
|
||||
return result ? chars.toString() : null;
|
||||
}
|
||||
}
|
||||
@@ -182,7 +182,9 @@ public class GrStringUtil {
|
||||
if (sL >= qL * 2 && s.endsWith(quote)) {
|
||||
return s.substring(qL, sL - qL);
|
||||
}
|
||||
return s;
|
||||
else {
|
||||
return s.substring(qL);
|
||||
}
|
||||
}
|
||||
|
||||
public static String addQuotes(String s, boolean forGString) {
|
||||
@@ -338,13 +340,95 @@ public class GrStringUtil {
|
||||
return "";
|
||||
}
|
||||
|
||||
public static boolean parseRegexCharacters(@NotNull String chars,
|
||||
@NotNull StringBuilder outChars,
|
||||
@Nullable int[] sourceOffsets,
|
||||
boolean escapeSlash) {
|
||||
assert sourceOffsets == null || sourceOffsets.length == chars.length() + 1;
|
||||
if (chars.indexOf('\\') < 0) {
|
||||
outChars.append(chars);
|
||||
if (sourceOffsets != null) {
|
||||
for (int i = 0; i < sourceOffsets.length; i++) {
|
||||
sourceOffsets[i] = i;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
final int outOffset = outChars.length();
|
||||
while (index < chars.length()) {
|
||||
char c = chars.charAt(index++);
|
||||
if (sourceOffsets != null) {
|
||||
sourceOffsets[outChars.length() - outOffset] = index - 1;
|
||||
sourceOffsets[outChars.length() + 1 - outOffset] = index;
|
||||
}
|
||||
if (c != '\\') {
|
||||
outChars.append(c);
|
||||
continue;
|
||||
}
|
||||
if (index == chars.length()) {
|
||||
outChars.append('\\');
|
||||
return true;
|
||||
}
|
||||
c = chars.charAt(index++);
|
||||
switch (c) {
|
||||
case '/':
|
||||
if (escapeSlash) {
|
||||
outChars.append(c);
|
||||
if (sourceOffsets != null) {
|
||||
sourceOffsets[outChars.length() - outOffset] = index;
|
||||
}
|
||||
}
|
||||
else {
|
||||
outChars.append('\\').append('/');
|
||||
}
|
||||
|
||||
break;
|
||||
case '\n':
|
||||
//do nothing
|
||||
if (sourceOffsets != null) {
|
||||
sourceOffsets[outChars.length() - outOffset] = index;
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
// uuuuu1234 is valid too
|
||||
while (index != chars.length() && chars.charAt(index) == 'u') {
|
||||
index++;
|
||||
}
|
||||
if (index + 4 <= chars.length()) {
|
||||
try {
|
||||
int code = Integer.parseInt(chars.substring(index, index + 4), 16);
|
||||
//line separators are invalid here
|
||||
if (code == 0x000a || code == 0x000d) return false;
|
||||
c = chars.charAt(index);
|
||||
if (c == '+' || c == '-') return false;
|
||||
outChars.append((char)code);
|
||||
index += 4;
|
||||
|
||||
if (sourceOffsets != null) {
|
||||
sourceOffsets[outChars.length() - outOffset] = index;
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
outChars.append('\\').append(c);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl#parseStringCharacters(String, StringBuilder, int[])
|
||||
*/
|
||||
public static boolean parseStringCharacters(@NotNull String chars,
|
||||
@NotNull StringBuilder outChars,
|
||||
@Nullable int[] sourceOffsets,
|
||||
final boolean strictBackSlash) {
|
||||
public static boolean parseStringCharacters(@NotNull String chars, @NotNull StringBuilder outChars, @Nullable int[] sourceOffsets) {
|
||||
assert sourceOffsets == null || sourceOffsets.length == chars.length()+1;
|
||||
if (chars.indexOf('\\') < 0) {
|
||||
outChars.append(chars);
|
||||
@@ -371,15 +455,31 @@ public class GrStringUtil {
|
||||
c = chars.charAt(index++);
|
||||
switch (c) {
|
||||
case'b':
|
||||
outChars.append('\b');
|
||||
break;
|
||||
case't':
|
||||
outChars.append('\t');
|
||||
break;
|
||||
case'n':
|
||||
outChars.append('\n');
|
||||
break;
|
||||
case'f':
|
||||
outChars.append('\f');
|
||||
break;
|
||||
case'r':
|
||||
outChars.append('\r');
|
||||
break;
|
||||
case'"':
|
||||
outChars.append('\"');
|
||||
break;
|
||||
case'\'':
|
||||
outChars.append('\'');
|
||||
break;
|
||||
case'$':
|
||||
outChars.append('$');
|
||||
break;
|
||||
case'\\':
|
||||
outChars.append(c);
|
||||
outChars.append('\\');
|
||||
break;
|
||||
case '\n':
|
||||
//do nothing
|
||||
@@ -442,9 +542,6 @@ public class GrStringUtil {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!strictBackSlash) {
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (sourceOffsets != null) {
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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 org.jetbrains.plugins.groovy.lang
|
||||
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.annotations.Nullable
|
||||
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile
|
||||
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.literals.GrLiteral
|
||||
import org.jetbrains.plugins.groovy.util.TestUtils
|
||||
|
||||
/**
|
||||
* @author Max Medvedev
|
||||
*/
|
||||
class StringValueTest extends LightCodeInsightFixtureTestCase {
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
"${TestUtils.testDataPath}stringValues/"
|
||||
}
|
||||
|
||||
private void doTest(@Nullable String expected) {
|
||||
def file = myFixture.configureByFile("${getTestName(false)}.groovy") as GroovyFile
|
||||
def literal = file.statements[0] as GrLiteral
|
||||
assertEquals expected, literal.value
|
||||
}
|
||||
|
||||
void testString0() {
|
||||
doTest("fo\nabc \r \u1234 \" \' \" ")
|
||||
}
|
||||
|
||||
void testString1() {
|
||||
doTest(null)
|
||||
}
|
||||
|
||||
void testString2() {
|
||||
doTest(null)
|
||||
}
|
||||
|
||||
void testString3() {
|
||||
doTest("fooabc")
|
||||
}
|
||||
|
||||
void testGString() {
|
||||
doTest("abc \' \" ")
|
||||
}
|
||||
|
||||
void testSlashString() {
|
||||
doTest("abc \\a \\n / abc \u1234 \$ \\\$ ")
|
||||
}
|
||||
|
||||
void _testDollarSlashString() {
|
||||
doTest("/abc \\n \\o \u1234 abc \$ / \\\$ /")
|
||||
}
|
||||
}
|
||||
@@ -12,4 +12,7 @@ ptint """""\n\a\t"""
|
||||
print """\n${x}\t"""
|
||||
print "dfg\$fg"
|
||||
print 'fg\$fg'
|
||||
print """\n${x}\"""<EOLError descr="String end expected"></EOLError>
|
||||
print (<error descr="Illegal escape character in string literal">/abc\n\r\y\o \u12 /</error>)
|
||||
print (/abc\n\r\y\o \u1234 /)
|
||||
print """\n${x}\"""
|
||||
<EOLError descr="String end expected"></EOLError>
|
||||
@@ -0,0 +1,2 @@
|
||||
$/abc \n \o \u1234 \
|
||||
abc $ / \$ /$
|
||||
@@ -0,0 +1 @@
|
||||
"abc ' \" "
|
||||
@@ -0,0 +1,2 @@
|
||||
/abc \a \n \/ \
|
||||
abc \u1234 $ \$ /
|
||||
@@ -0,0 +1 @@
|
||||
'fo\nabc \r \u1234 \" \' \" '
|
||||
@@ -0,0 +1 @@
|
||||
'fo\nabc \r \u123'
|
||||
@@ -0,0 +1 @@
|
||||
'fo\nabc \i'
|
||||
@@ -0,0 +1,2 @@
|
||||
'''foo\
|
||||
abc'''
|
||||
Reference in New Issue
Block a user