checks for dollar-slash strings

This commit is contained in:
Maxim.Medvedev
2011-12-11 12:54:34 +04:00
parent 897d5437ef
commit fd53850860
6 changed files with 172 additions and 66 deletions
@@ -294,4 +294,8 @@ Equals=Equals
default.initializers.are.not.allowed.in.abstract.method=Default initializers are not allowed in abstract methods
groovy.does.not.support.constructor.type.arguments=Groovy does not support constructor type arguments
illegal.escape.character.in.string.literal=Illegal escape character in string literal
dollar.slash.end.expected=Dollar slash ending expected
dollar.slash.end.expected=Dollar slash ending expected
regex.cannot.be.first.argument.of.command.method.call=Regex cannot be first argument of command method call
add.parentheses=Add parentheses
add.parentheses.to.command.method.call=Add parentheses to command method call
dollar.slash.strings.are.not.allowed.in.0=Dollar-slash strings are not allowed in Groovy {0}
@@ -835,6 +835,63 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator {
checkStringLiteral(literal, text);
}
@Override
public void visitRegexExpression(GrRegex regex) {
String text = regex.getText();
String quote = GrStringUtil.getStartQuote(text);
final GroovyConfigUtils config = GroovyConfigUtils.getInstance();
if ("$/".equals(quote)) {
if (!config.isVersionAtLeast(regex, GroovyConfigUtils.GROOVY1_9)) {
myHolder
.createErrorAnnotation(regex, GroovyBundle.message("dollar.slash.strings.are.not.allowed.in.0", config.getSDKVersion(regex)));
}
if (regex.getParent() instanceof GrCommandArgumentList &&
((GrCommandArgumentList)regex.getParent()).getAllArguments()[0] == regex) {
final Annotation annotation =
myHolder.createErrorAnnotation(regex, GroovyBundle.message("regex.cannot.be.first.argument.of.command.method.call"));
annotation.registerFix(new AddParenthesesFix());
return;
}
}
for (String part : regex.getTextParts()) {
if (!GrStringUtil.parseRegexCharacters(part, new StringBuilder(part.length()), null, regex.getText().startsWith("/"))) {
myHolder.createErrorAnnotation(regex, GroovyBundle.message("illegal.escape.character.in.string.literal"));
return;
}
}
if ("/".equals(quote)) {
if (!config.isVersionAtLeast(regex, GroovyConfigUtils.GROOVY1_8)) {
if (text.contains("\n") || text.contains("\r")) {
myHolder.createErrorAnnotation(regex, GroovyBundle
.message("multiline.slashy.strings.are.not.allowed.in.groovy.0", config.getSDKVersion(regex)));
return;
}
}
}
if (regex.getInjections().length > 0) {
if (!config.isVersionAtLeast(regex, GroovyConfigUtils.GROOVY1_8)) {
myHolder.createErrorAnnotation(regex, GroovyBundle
.message("slashy.strings.with.injections.are.not.allowed.in.groovy.0", config.getSDKVersion(regex)));
}
}
}
@Override
public void visitGStringExpression(GrString gstring) {
for (String part : gstring.getTextParts()) {
if (!GrStringUtil.parseStringCharacters(part, new StringBuilder(part.length()), null)) {
myHolder.createErrorAnnotation(gstring, GroovyBundle.message("illegal.escape.character.in.string.literal"));
return;
}
}
}
private void checkStringLiteral(PsiElement literal, String text) {
StringBuilder builder = new StringBuilder(text.length());
@@ -842,71 +899,16 @@ public class GroovyAnnotator extends GroovyElementVisitor implements Annotator {
if (quote.isEmpty()) return;
String substring = text.substring(quote.length());
String[] parts;
PsiElement child = literal.getFirstChild();
if (child == null) child = literal;
IElementType elementType = child.getNode().getElementType();
boolean isSimpleString = elementType == GroovyTokenTypes.mSTRING_LITERAL ||
elementType == GroovyTokenTypes.mGSTRING_LITERAL ||
elementType == GroovyTokenTypes.mREGEX_LITERAL;
if (isSimpleString) {
parts = new String[]{substring};
}
else if (literal instanceof GrString) {
parts = ((GrString)literal).getTextParts();
}
else {
if (!GrStringUtil.parseStringCharacters(substring, new StringBuilder(text.length()), null)) {
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);
LOG.assertTrue(result);
if (!builder.toString().endsWith(quote) || substring.charAt(offsets[builder.length() - quote.length()]) == '\\') {
myHolder.createErrorAnnotation(literal, GroovyBundle.message("string.end.expected"));
return;
}
}
else {
LOG.assertTrue(literal instanceof GrString);
//absence of closing quote is registered by lexer
}
if (quote.equals("/")) {
if (!GroovyConfigUtils.getInstance().isVersionAtLeast(literal, GroovyConfigUtils.GROOVY1_8)) {
if (text.contains("\n") || text.contains("\r")) {
myHolder.createErrorAnnotation(literal, GroovyBundle.message("multiline.slashy.strings.are.not.allowed.in.groovy.0",
GroovyConfigUtils.getInstance().getSDKVersion(literal)));
return;
}
}
}
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)));
return;
}
int[] offsets = new int[substring.length() + 1];
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"));
}
}
@@ -0,0 +1,87 @@
/*
* 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.annotator.intentions;
import com.intellij.codeInsight.intention.IntentionAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.GroovyBundle;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrApplicationStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCommandArgumentList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
/**
* @author Max Medvedev
*/
public class AddParenthesesFix implements IntentionAction {
private static final Logger LOG = Logger.getInstance(AddParenthesesFix.class);
@NotNull
@Override
public String getText() {
return GroovyBundle.message("add.parentheses");
}
@NotNull
@Override
public String getFamilyName() {
return GroovyBundle.message("add.parentheses.to.command.method.call");
}
@Override
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
final int offset = editor.getCaretModel().getOffset();
final PsiElement at = file.findElementAt(offset);
final GrCommandArgumentList argList = PsiTreeUtil.getParentOfType(at, GrCommandArgumentList.class);
return argList != null;
}
@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
final int offset = editor.getCaretModel().getOffset();
final PsiElement at = file.findElementAt(offset);
final GrCommandArgumentList argList = PsiTreeUtil.getParentOfType(at, GrCommandArgumentList.class);
if (argList == null) return;
final PsiElement parent = argList.getParent();
LOG.assertTrue(parent instanceof GrApplicationStatement);
final GrExpression newExpr;
try {
newExpr = GroovyPsiElementFactory.getInstance(project)
.createExpressionFromText(((GrApplicationStatement)parent).getInvokedExpression().getText() + '(' + argList.getText() + ')');
}
catch (IncorrectOperationException e) {
LOG.error(e);
return;
}
parent.replace(newExpr);
editor.getCaretModel().moveToOffset(offset + 1);
}
@Override
public boolean startInWriteAction() {
return true;
}
}
@@ -52,6 +52,7 @@ public abstract class GroovyConfigUtils extends AbstractConfigUtils {
public static final String NO_VERSION = "<no version>";
public static final String GROOVY1_7 = "1.7";
public static final String GROOVY1_8 = "1.8";
public static final String GROOVY1_9 = "1.9";
private GroovyConfigUtils() {
}
@@ -29,6 +29,8 @@ public class GrStringUtil {
private static final String DOUBLE_QUOTES = "\"";
private static final String TRIPLE_DOUBLE_QUOTES = "\"\"\"";
private static final String SLASH = "/";
private static final String DOLLAR_SLASH = "$/";
private static final String SLASH_DOLLAR = "/$";
private GrStringUtil() {
}
@@ -179,6 +181,15 @@ public class GrStringUtil {
String quote = getStartQuote(s);
int sL = s.length();
int qL = quote.length();
if (sL >= qL * 2 && DOLLAR_SLASH.equals(quote)) {
if (s.endsWith(SLASH_DOLLAR)) {
return s.substring(qL, sL - qL);
}
else {
return s.substring(qL);
}
}
if (sL >= qL * 2 && s.endsWith(quote)) {
return s.substring(qL, sL - qL);
}
@@ -337,6 +348,7 @@ public class GrStringUtil {
if (text.startsWith(TRIPLE_DOUBLE_QUOTES)) return TRIPLE_DOUBLE_QUOTES;
if (text.startsWith(DOUBLE_QUOTES)) return DOUBLE_QUOTES;
if (text.startsWith(SLASH)) return SLASH;
if (text.startsWith(DOLLAR_SLASH)) return DOLLAR_SLASH;
return "";
}
@@ -1,14 +1,14 @@
print '\n'
print <error descr="Illegal escape character in string literal">'\y'</error>
ptint <error descr="Illegal escape character in string literal">"\n\a\t"</error>
print "\n\a${x}\t"
print <error descr="Illegal escape character in string literal">"\n\a${x}\t"</error>
print "\n${x}\t"
print "\n${x}\"<EOLError descr="String end expected"></EOLError>
print "\n\"<EOLError descr="String end expected"></EOLError>
print '''\n'''
print <error descr="Illegal escape character in string literal">'''\y'''</error>
print """\n\a${x}\t"""
ptint """""\n\a\t"""
print <error descr="Illegal escape character in string literal">"""\n\a${x}\t"""</error>
ptint <error descr="Illegal escape character in string literal">"""""\n\a\t"""</error>
print """\n${x}\t"""
print "dfg\$fg"
print 'fg\$fg'