Throw ParseException for unsupported character in SpEL expression

Prior to this commit, the SpEL Tokenizer threw an IllegalStateException
when an unsupported character was detected in a SpEL expression;
however, the Javadoc for ExpressionParser.parseExpression() states that
it throws a ParseException if an exception occurred during parsing.

This commit addresses that issue by throwing a SpelParseException for
an unsupported character in a SpEL expression, using a new
UNSUPPORTED_CHARACTER enum constant in SpelMessage.

Closes gh-33767
This commit is contained in:
Sam Brannen 2024-10-22 13:03:08 +02:00
parent d22924c728
commit c98f314665
4 changed files with 18 additions and 17 deletions

View File

@ -291,7 +291,11 @@ public enum SpelMessage {
/** @since 6.0.13 */ /** @since 6.0.13 */
NEGATIVE_REPEATED_TEXT_COUNT(Kind.ERROR, 1081, NEGATIVE_REPEATED_TEXT_COUNT(Kind.ERROR, 1081,
"Repeat count ''{0}'' must not be negative"); "Repeat count ''{0}'' must not be negative"),
/** @since 6.1.15 */
UNSUPPORTED_CHARACTER(Kind.ERROR, 1082,
"Unsupported character ''{0}'' ({1}) encountered in expression");
private final Kind kind; private final Kind kind;

View File

@ -266,9 +266,7 @@ class Tokenizer {
raiseParseException(this.pos, SpelMessage.UNEXPECTED_ESCAPE_CHAR); raiseParseException(this.pos, SpelMessage.UNEXPECTED_ESCAPE_CHAR);
break; break;
default: default:
throw new IllegalStateException( raiseParseException(this.pos + 1, SpelMessage.UNSUPPORTED_CHARACTER, ch, (int) ch);
"Unsupported character '%s' (%d) encountered at position %d in expression."
.formatted(ch, (int) ch, (this.pos + 1)));
} }
} }
} }

View File

@ -42,7 +42,7 @@ public abstract class AbstractExpressionTests {
protected static final boolean SHOULD_NOT_BE_WRITABLE = false; protected static final boolean SHOULD_NOT_BE_WRITABLE = false;
protected final ExpressionParser parser = new SpelExpressionParser(); protected final SpelExpressionParser parser = new SpelExpressionParser();
protected final StandardEvaluationContext context = TestScenarioCreator.getTestEvaluationContext(); protected final StandardEvaluationContext context = TestScenarioCreator.getTestEvaluationContext();

View File

@ -18,12 +18,12 @@ package org.springframework.expression.spel;
import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.springframework.expression.spel.standard.SpelExpression; import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
/** /**
* Parse some expressions and check we get the AST we expect. * Parse some expressions and check we get the AST we expect.
@ -34,10 +34,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
* @author Andy Clement * @author Andy Clement
* @author Sam Brannen * @author Sam Brannen
*/ */
class ParsingTests { class ParsingTests extends AbstractExpressionTests {
private final SpelExpressionParser parser = new SpelExpressionParser();
@Nested @Nested
class Miscellaneous { class Miscellaneous {
@ -104,12 +101,14 @@ class ParsingTests {
parseCheck("have乐趣()"); parseCheck("have乐趣()");
} }
@Test @ParameterizedTest(name = "expression = ''{0}''")
void unsupportedCharactersInIdentifiers() { @CsvSource(textBlock = """
// Invalid syntax apple~banana, ~, 6
assertThatIllegalStateException() map[c], , 5
.isThrownBy(() -> parser.parseRaw("apple~banana")) A § B, §, 3
.withMessage("Unsupported character '~' (126) encountered at position 6 in expression."); """)
void unsupportedCharacter(String expression, char ch, int position) {
parseAndCheckError(expression, SpelMessage.UNSUPPORTED_CHARACTER, position, ch, (int) ch);
} }
@Test @Test