Allow injection in strings with interpolation (KT-6610)

#KT-6610 In Progress
This commit is contained in:
Nikolay Krasko
2017-05-04 19:52:48 +03:00
parent f53b9aa419
commit 30639b0a5e
3 changed files with 24 additions and 18 deletions
@@ -38,13 +38,6 @@ class KotlinStringLiteralTextEscaper(host: KtStringTemplateExpression): LiteralT
continue continue
} }
when (child) { when (child) {
is KtLiteralStringTemplateEntry -> {
val textRange = rangeInsideHost.intersection(childRange)!!.shiftRight(-childRange.startOffset)
outChars.append(child.getText(), textRange.startOffset, textRange.endOffset)
repeat(textRange.length) {
sourceOffsetsList.add(sourceOffset++)
}
}
is KtEscapeStringTemplateEntry -> { is KtEscapeStringTemplateEntry -> {
if (!rangeInsideHost.contains(childRange)) { if (!rangeInsideHost.contains(childRange)) {
//don't allow injection if its range starts or ends inside escaped sequence //don't allow injection if its range starts or ends inside escaped sequence
@@ -57,7 +50,13 @@ class KotlinStringLiteralTextEscaper(host: KtStringTemplateExpression): LiteralT
} }
sourceOffset += child.getTextLength() sourceOffset += child.getTextLength()
} }
else -> return false else -> {
val textRange = rangeInsideHost.intersection(childRange)!!.shiftRight(-childRange.startOffset)
outChars.append(child.text, textRange.startOffset, textRange.endOffset)
repeat(textRange.length) {
sourceOffsetsList.add(sourceOffset++)
}
}
} }
} }
sourceOffsetsList.add(sourceOffset) sourceOffsetsList.add(sourceOffset)
@@ -22,11 +22,10 @@ import com.intellij.psi.LiteralTextEscaper;
import com.intellij.psi.PsiLanguageInjectionHost; import com.intellij.psi.PsiLanguageInjectionHost;
import com.intellij.psi.tree.TokenSet; import com.intellij.psi.tree.TokenSet;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.kotlin.KtNodeTypes;
import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.lexer.KtTokens;
public class KtStringTemplateExpression extends KtExpressionImpl implements PsiLanguageInjectionHost { public class KtStringTemplateExpression extends KtExpressionImpl implements PsiLanguageInjectionHost {
private static final TokenSet TOKENS_SUITABLE_FOR_INJECTION = TokenSet.create(KtNodeTypes.LITERAL_STRING_TEMPLATE_ENTRY, KtNodeTypes.ESCAPE_STRING_TEMPLATE_ENTRY); private static final TokenSet CLOSE_QUOTE_TOKEN_SET = TokenSet.create(KtTokens.CLOSING_QUOTE);
public KtStringTemplateExpression(@NotNull ASTNode node) { public KtStringTemplateExpression(@NotNull ASTNode node) {
super(node); super(node);
@@ -44,14 +43,7 @@ public class KtStringTemplateExpression extends KtExpressionImpl implements PsiL
@Override @Override
public boolean isValidHost() { public boolean isValidHost() {
ASTNode node = getNode(); return getNode().getChildren(CLOSE_QUOTE_TOKEN_SET).length != 0;
ASTNode child = node.getFirstChildNode().getTreeNext();
while (child != null) {
if (child.getElementType() == KtTokens.CLOSING_QUOTE) return true;
if (!TOKENS_SUITABLE_FOR_INJECTION.contains(child.getElementType())) return false;
child = child.getTreeNext();
}
return false;
} }
@Override @Override
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtStringTemplateExpression import org.jetbrains.kotlin.psi.KtStringTemplateExpression
import org.jetbrains.kotlin.test.ConfigurationKind import org.jetbrains.kotlin.test.ConfigurationKind
import org.jetbrains.kotlin.test.KotlinTestWithEnvironment import org.jetbrains.kotlin.test.KotlinTestWithEnvironment
import org.jetbrains.kotlin.utils.keysToMap
import java.util.* import java.util.*
class StringInjectionHostTest : KotlinTestWithEnvironment() { class StringInjectionHostTest : KotlinTestWithEnvironment() {
@@ -41,6 +42,11 @@ class StringInjectionHostTest : KotlinTestWithEnvironment() {
} }
} }
fun testInterpolation1(): Unit = checkAllRanges("a \$b c")
fun testInterpolation2(): Unit = checkAllRanges("a \${b} c")
fun testInterpolation3(): Unit = checkAllRanges("a\${b}c")
fun testInterpolation4(): Unit = checkAllRanges("a \${b.foo()} c")
fun testUnclosedSimpleLiteral() { fun testUnclosedSimpleLiteral() {
assertFalse(stringExpression("\"").isValidHost) assertFalse(stringExpression("\"").isValidHost)
assertFalse(stringExpression("\"a").isValidHost) assertFalse(stringExpression("\"a").isValidHost)
@@ -124,6 +130,13 @@ class StringInjectionHostTest : KotlinTestWithEnvironment() {
assertFalse(createLiteralTextEscaper().isOneLine) assertFalse(createLiteralTextEscaper().isOneLine)
} }
private fun checkAllRanges(str: String) {
with (quoted(str)) {
checkInjection(str, (0..str.length).keysToMap { it + 1 })
assertOneLine()
}
}
private fun KtStringTemplateExpression.checkInjection( private fun KtStringTemplateExpression.checkInjection(
decoded: String, targetToSourceOffsets: Map<Int, Int>, rangeInHost: TextRange? = null decoded: String, targetToSourceOffsets: Map<Int, Int>, rangeInHost: TextRange? = null
) { ) {
@@ -132,8 +145,10 @@ class StringInjectionHostTest : KotlinTestWithEnvironment() {
val escaper = createLiteralTextEscaper() val escaper = createLiteralTextEscaper()
val chars = StringBuilder(prefix) val chars = StringBuilder(prefix)
val range = rangeInHost ?: escaper.relevantTextRange val range = rangeInHost ?: escaper.relevantTextRange
assertTrue(escaper.decode(range, chars)) assertTrue(escaper.decode(range, chars))
assertEquals(decoded, chars.substring(prefix.length)) assertEquals(decoded, chars.substring(prefix.length))
val extendedOffsets = HashMap(targetToSourceOffsets) val extendedOffsets = HashMap(targetToSourceOffsets)
val beforeStart = targetToSourceOffsets.keys.min()!! - 1 val beforeStart = targetToSourceOffsets.keys.min()!! - 1
if (beforeStart >= 0) { if (beforeStart >= 0) {