Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix expression conversion #7165

Open
wants to merge 9 commits into
base: dev/patch
Choose a base branch
from
10 changes: 5 additions & 5 deletions src/main/java/ch/njol/skript/lang/SkriptParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,6 @@ private static <T> Variable<T> parseVariable(String expr, Class<? extends T>[] r
}
}


@Nullable
@SuppressWarnings({"unchecked", "rawtypes"})
private <T> Expression<? extends T> parseSingleExpr(boolean allowUnparsedLiteral, @Nullable LogEntry error, Class<? extends T>... types) {
Expand Down Expand Up @@ -368,9 +367,9 @@ private <T> Expression<? extends T> parseSingleExpr(boolean allowUnparsedLiteral
if ((flags & PARSE_EXPRESSIONS) != 0) {
Expression<?> parsedExpression = parseExpression(types, expr);
if (parsedExpression != null) { // Expression/VariableString parsing success
Class<?> parsedReturnType = parsedExpression.getReturnType();
for (Class<? extends T> type : types) {
// Check return type against everything that expression accepts
if (parsedExpression.canReturn(type)) {
if (type.isAssignableFrom(parsedReturnType)) {
log.printLog();
return (Expression<? extends T>) parsedExpression;
}
Expand Down Expand Up @@ -540,13 +539,14 @@ private Expression<?> parseSingleExpr(boolean allowUnparsedLiteral, @Nullable Lo
if ((flags & PARSE_EXPRESSIONS) != 0) {
Expression<?> parsedExpression = parseExpression(types, expr);
if (parsedExpression != null) { // Expression/VariableString parsing success
Class<?> parsedReturnType = parsedExpression.getReturnType();
for (int i = 0; i < types.length; i++) {
Class<?> type = types[i];
if (type == null) // Ignore invalid (null) types
continue;

// Check return type against everything that expression accepts
if (parsedExpression.canReturn(type)) {
// Check return type against the expression's return type
if (type.isAssignableFrom(parsedReturnType)) {
if (!exprInfo.isPlural[i] && !parsedExpression.isSingle()) { // Wrong number of arguments
if (context == ParseContext.COMMAND) {
Skript.error(Commands.m_too_many_arguments.toString(exprInfo.classes[i].getName().getIndefiniteArticle(), exprInfo.classes[i].getName().toString()), ErrorQuality.SEMANTIC_ERROR);
Expand Down
30 changes: 20 additions & 10 deletions src/main/java/ch/njol/skript/lang/util/SimpleExpression.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.jetbrains.annotations.Nullable;
import org.skriptlang.skript.lang.converter.Converter;
import org.skriptlang.skript.lang.converter.ConverterInfo;
import org.skriptlang.skript.lang.converter.Converters;

import java.lang.reflect.Array;
import java.util.ArrayList;
Expand Down Expand Up @@ -207,29 +208,38 @@ public <R> Expression<? extends R> getConvertedExpression(Class<R>... to) {
if (CollectionUtils.containsSuperclass(to, getReturnType()))
return (Expression<? extends R>) this;

// we might be to cast some of the possible return types to R
// we might be able to cast some (or all) of the possible return types to R
// for possible return types that can't be directly cast, regular converters will be used
List<ConverterInfo<? extends T, R>> infos = new ArrayList<>();
for (Class<? extends T> type : this.possibleReturnTypes()) {
if (CollectionUtils.containsSuperclass(to, type)) { // this type is of R
// build a converter that for casting to R
// build a converter for casting to R
// safety check is present in the event that we do not get this type at runtime
final Class<R> toType = (Class<R>) type;
infos.add(new ConverterInfo<>(getReturnType(), toType, fromObject -> {
if (toType.isInstance(fromObject))
return (R) fromObject;
return null;
}, 0));
infos.add(new ConverterInfo<>(type, (Class<R>) type, fromObject -> (R) fromObject, 0));
} else { // this possible return type is not included in 'to'
// build all converters for converting the possible return type into any of the types of 'to'
for (Class<R> toType : to) {
ConverterInfo<? extends T, R> converter = Converters.getConverterInfo(type, toType);
if (converter != null)
infos.add(converter);
}
}
}
int size = infos.size();
if (size == 1) { // if there is only one info, there is no need to wrap it in a list
ConverterInfo<? extends T, R> info = infos.get(0);
// we need to remake this converter info with a runtime safety check
// this is handled by ConvertedExpression
//noinspection rawtypes
return new ConvertedExpression(this, info.getTo(), info);
return new ConvertedExpression(this, info.getTo(), new ConverterInfo<>(info.getFrom(), info.getTo(), fromObject -> {
if (info.getFrom().isInstance(fromObject))
return (R) fromObject;
return null;
}, info.getFlags()));
}
if (size > 1) {
//noinspection rawtypes
return new ConvertedExpression(this, Utils.getSuperType(infos.stream().map(ConverterInfo::getTo).toArray(Class[]::new)), infos, false);
return new ConvertedExpression(this, Utils.getSuperType(infos.stream().map(ConverterInfo::getTo).toArray(Class[]::new)), infos, true);
}

// attempt traditional conversion with proper converters
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
test "expressions sometimes dont convert":
assert formatted ({_foo} + {_bar}) is not set with "formatting nothing shouldn't throw an error"

set {_foo} to "test"
assert formatted ({_foo} + {_bar}) is not set with "formatting string + none shouldn't throw an error"

set {_foo} to 1
set {_bar} to 2
assert formatted ({_foo} + {_bar}) is not set with "formatting number + number shouldn't throw an error"

set {_foo} to "foo"
set {_bar} to "bar"
assert formatted ({_foo} + {_bar}) is "foobar" with "formatting strings doesn't work"
Loading