[FIR] Implement INCORRECT_CHARACTER_LITERAL, EMPTY_CHARACTER_LITERAL, TOO_MANY_CHARACTERS_IN_CHARACTER_LITERAL, ILLEGAL_ESCAPE, ILLEGAL_ESCAPE_SEQUENCE diagnostics, fix tests
This commit is contained in:
committed by
TeamCityServer
parent
91d42fe345
commit
404c69ded7
+1
-1
@@ -7,7 +7,7 @@ class B: A() {
|
||||
<!UNRESOLVED_REFERENCE!>invoke<!>()
|
||||
|
||||
<!SUPER_IS_NOT_AN_EXPRESSION!>super<!> {
|
||||
<!UNRESOLVED_REFERENCE!>println<!>(<!ILLEGAL_CONST_EXPRESSION!>'weird'<!>)
|
||||
<!UNRESOLVED_REFERENCE!>println<!>(<!TOO_MANY_CHARACTERS_IN_CHARACTER_LITERAL!>'weird'<!>)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -63,6 +63,10 @@ object DIAGNOSTICS_LIST : DiagnosticList() {
|
||||
val NESTED_CLASS_NOT_ALLOWED by error<FirSourceElement, KtNamedDeclaration>(PositioningStrategy.DECLARATION_NAME) {
|
||||
parameter<String>("declaration")
|
||||
}
|
||||
val INCORRECT_CHARACTER_LITERAL by error<FirSourceElement, PsiElement>()
|
||||
val EMPTY_CHARACTER_LITERAL by error<FirSourceElement, PsiElement>()
|
||||
val TOO_MANY_CHARACTERS_IN_CHARACTER_LITERAL by error<FirSourceElement, PsiElement>()
|
||||
val ILLEGAL_ESCAPE by error<FirSourceElement, PsiElement>()
|
||||
}
|
||||
|
||||
val UNRESOLVED by object : DiagnosticGroup("Unresolved") {
|
||||
|
||||
@@ -79,6 +79,10 @@ object FirErrors {
|
||||
val VARIABLE_EXPECTED by error0<FirSourceElement, PsiElement>()
|
||||
val DELEGATION_IN_INTERFACE by error0<FirSourceElement, PsiElement>()
|
||||
val NESTED_CLASS_NOT_ALLOWED by error1<FirSourceElement, KtNamedDeclaration, String>(SourceElementPositioningStrategies.DECLARATION_NAME)
|
||||
val INCORRECT_CHARACTER_LITERAL by error0<FirSourceElement, PsiElement>()
|
||||
val EMPTY_CHARACTER_LITERAL by error0<FirSourceElement, PsiElement>()
|
||||
val TOO_MANY_CHARACTERS_IN_CHARACTER_LITERAL by error0<FirSourceElement, PsiElement>()
|
||||
val ILLEGAL_ESCAPE by error0<FirSourceElement, PsiElement>()
|
||||
|
||||
// Unresolved
|
||||
val HIDDEN by error1<FirSourceElement, PsiElement, AbstractFirBasedSymbol<*>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
|
||||
|
||||
+4
@@ -175,6 +175,10 @@ private fun ConeSimpleDiagnostic.getFactory(): FirDiagnosticFactory0<FirSourceEl
|
||||
DiagnosticKind.IllegalProjectionUsage -> FirErrors.ILLEGAL_PROJECTION_USAGE
|
||||
DiagnosticKind.MissingStdlibClass -> FirErrors.MISSING_STDLIB_CLASS
|
||||
DiagnosticKind.Other -> FirErrors.OTHER_ERROR
|
||||
DiagnosticKind.IncorrectCharacterLiteral -> FirErrors.INCORRECT_CHARACTER_LITERAL
|
||||
DiagnosticKind.EmptyCharacterLiteral -> FirErrors.EMPTY_CHARACTER_LITERAL
|
||||
DiagnosticKind.TooManyCharactersInCharacterLiteral -> FirErrors.TOO_MANY_CHARACTERS_IN_CHARACTER_LITERAL
|
||||
DiagnosticKind.IllegalEscape -> FirErrors.ILLEGAL_ESCAPE
|
||||
else -> throw IllegalArgumentException("Unsupported diagnostic kind: $kind at $javaClass")
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ abstract class BaseConverter(
|
||||
override val LighterASTNode.unescapedValue: String
|
||||
get() {
|
||||
val escape = this.asText
|
||||
return escapedStringToCharacter(escape)?.toString()
|
||||
return escapedStringToCharacter(escape).value?.toString()
|
||||
?: escape.replace("\\", "").replace("u", "\\u")
|
||||
}
|
||||
|
||||
|
||||
+8
-3
@@ -332,13 +332,18 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
|
||||
ConeSimpleDiagnostic("Incorrect double: $text", DiagnosticKind.IllegalConstExpression)
|
||||
)
|
||||
}
|
||||
CHARACTER_CONSTANT ->
|
||||
CHARACTER_CONSTANT -> {
|
||||
val characterWithDiagnostic = text.parseCharacter()
|
||||
buildConstOrErrorExpression(
|
||||
sourceElement,
|
||||
ConstantValueKind.Char,
|
||||
text.parseCharacter(),
|
||||
ConeSimpleDiagnostic("Incorrect character: $text", DiagnosticKind.IllegalConstExpression)
|
||||
characterWithDiagnostic.value,
|
||||
ConeSimpleDiagnostic(
|
||||
"Incorrect character: $text",
|
||||
characterWithDiagnostic.getDiagnostic() ?: DiagnosticKind.IllegalConstExpression
|
||||
)
|
||||
)
|
||||
}
|
||||
BOOLEAN_CONSTANT ->
|
||||
buildConstExpression(
|
||||
sourceElement,
|
||||
|
||||
+41
-21
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.fir.declarations.FirVariable
|
||||
import org.jetbrains.kotlin.fir.declarations.builder.*
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyAccessor
|
||||
import org.jetbrains.kotlin.fir.diagnostics.DiagnosticKind
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.*
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.FirSingleExpressionBlock
|
||||
@@ -40,30 +41,30 @@ import org.jetbrains.kotlin.types.ConstantValueKind
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
fun String.parseCharacter(): Char? {
|
||||
fun String.parseCharacter(): CharacterWithDiagnostic {
|
||||
// Strip the quotes
|
||||
if (length < 2 || this[0] != '\'' || this[length - 1] != '\'') {
|
||||
return null
|
||||
return CharacterWithDiagnostic(DiagnosticKind.IncorrectCharacterLiteral)
|
||||
}
|
||||
val text = substring(1, length - 1) // now there're no quotes
|
||||
val text = substring(1, length - 1) // now there is no quotes
|
||||
|
||||
if (text.isEmpty()) {
|
||||
return null
|
||||
return CharacterWithDiagnostic(DiagnosticKind.EmptyCharacterLiteral)
|
||||
}
|
||||
|
||||
return if (text[0] != '\\') {
|
||||
// No escape
|
||||
if (text.length == 1) {
|
||||
text[0]
|
||||
CharacterWithDiagnostic(text[0])
|
||||
} else {
|
||||
null
|
||||
CharacterWithDiagnostic(DiagnosticKind.TooManyCharactersInCharacterLiteral)
|
||||
}
|
||||
} else {
|
||||
escapedStringToCharacter(text)
|
||||
}
|
||||
}
|
||||
|
||||
fun escapedStringToCharacter(text: String): Char? {
|
||||
fun escapedStringToCharacter(text: String): CharacterWithDiagnostic {
|
||||
assert(text.isNotEmpty() && text[0] == '\\') {
|
||||
"Only escaped sequences must be passed to this routine: $text"
|
||||
}
|
||||
@@ -73,40 +74,59 @@ fun escapedStringToCharacter(text: String): Char? {
|
||||
when (escape.length) {
|
||||
0 -> {
|
||||
// bare slash
|
||||
return null
|
||||
return CharacterWithDiagnostic(DiagnosticKind.IllegalEscape)
|
||||
}
|
||||
1 -> {
|
||||
// one-char escape
|
||||
return translateEscape(escape[0]) ?: return null
|
||||
return translateEscape(escape[0])
|
||||
}
|
||||
5 -> {
|
||||
// unicode escape
|
||||
if (escape[0] == 'u') {
|
||||
try {
|
||||
val intValue = Integer.valueOf(escape.substring(1), 16)
|
||||
return intValue.toInt().toChar()
|
||||
return CharacterWithDiagnostic(intValue.toInt().toChar())
|
||||
} catch (e: NumberFormatException) {
|
||||
// Will be reported below
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
return CharacterWithDiagnostic(DiagnosticKind.IllegalEscape)
|
||||
}
|
||||
|
||||
internal fun translateEscape(c: Char): Char? =
|
||||
internal fun translateEscape(c: Char): CharacterWithDiagnostic =
|
||||
when (c) {
|
||||
't' -> '\t'
|
||||
'b' -> '\b'
|
||||
'n' -> '\n'
|
||||
'r' -> '\r'
|
||||
'\'' -> '\''
|
||||
'\"' -> '\"'
|
||||
'\\' -> '\\'
|
||||
'$' -> '$'
|
||||
else -> null
|
||||
't' -> CharacterWithDiagnostic('\t')
|
||||
'b' -> CharacterWithDiagnostic('\b')
|
||||
'n' -> CharacterWithDiagnostic('\n')
|
||||
'r' -> CharacterWithDiagnostic('\r')
|
||||
'\'' -> CharacterWithDiagnostic('\'')
|
||||
'\"' -> CharacterWithDiagnostic('\"')
|
||||
'\\' -> CharacterWithDiagnostic('\\')
|
||||
'$' -> CharacterWithDiagnostic('$')
|
||||
else -> CharacterWithDiagnostic(DiagnosticKind.IllegalEscape)
|
||||
}
|
||||
|
||||
class CharacterWithDiagnostic {
|
||||
private val diagnostic: DiagnosticKind?
|
||||
val value: Char?
|
||||
|
||||
constructor(diagnostic: DiagnosticKind) {
|
||||
this.diagnostic = diagnostic
|
||||
this.value = null
|
||||
}
|
||||
|
||||
constructor(value: Char) {
|
||||
this.diagnostic = null
|
||||
this.value = value
|
||||
}
|
||||
|
||||
fun getDiagnostic(): DiagnosticKind? {
|
||||
return diagnostic
|
||||
}
|
||||
}
|
||||
|
||||
fun IElementType.toBinaryName(): Name? {
|
||||
return OperatorConventions.BINARY_OPERATION_NAMES[this]
|
||||
}
|
||||
|
||||
@@ -30,5 +30,11 @@ enum class DiagnosticKind {
|
||||
UnknownCallableKind,
|
||||
IllegalProjectionUsage,
|
||||
MissingStdlibClass,
|
||||
|
||||
IncorrectCharacterLiteral,
|
||||
EmptyCharacterLiteral,
|
||||
TooManyCharactersInCharacterLiteral,
|
||||
IllegalEscape,
|
||||
|
||||
Other
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user