Skip to content

Commit

Permalink
[GR-18163] Fix EncodingError message
Browse files Browse the repository at this point in the history
PullRequest: truffleruby/3634
  • Loading branch information
andrykonchin committed Feb 8, 2023
2 parents e11a36d + cf50df8 commit 145d23e
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 6 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ Compatibility:
* Support writing to `RData.dfree` for native extensions (#2830, #2732, #2165, @eregon).
* Fix `IO#write` and support multiple arguments with different encodings (#2829, @andrykonchin).
* Fix `Array` methods `reject`, `reject!`, `inject`, `map`, `select`, `each_index` and handle a case when array is modified by a passed block like CRuby does (#2822, andrykonchin, @eregon).
* Fix `EncodingError` exception message when Symbol has invalid encoding (#2850, @andrykonchin).
* Raise `EncodingError` at parse time when Hash literal contains a Symbol key with invalid encoding (#2848, @andrykonchin).

Performance:

Expand Down
2 changes: 1 addition & 1 deletion spec/ruby/core/string/shared/to_sym.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,6 @@
invalid_utf8.should_not.valid_encoding?
-> {
invalid_utf8.send(@method)
}.should raise_error(EncodingError, /invalid/)
}.should raise_error(EncodingError, 'invalid symbol in encoding UTF-8 :"\xC3"')
end
end
16 changes: 16 additions & 0 deletions spec/ruby/language/hash_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,22 @@ def h.to_hash; {:b => 2, :c => 3}; end
utf8_hash.keys.first.should == usascii_hash.keys.first
usascii_hash.keys.first.encoding.should == Encoding::US_ASCII
end

it "raises an EncodingError at parse time when Symbol key with invalid bytes" do
ScratchPad.record []
-> {
eval 'ScratchPad << 1; {:"\xC3" => 1}'
}.should raise_error(EncodingError, 'invalid symbol in encoding UTF-8 :"\xC3"')
ScratchPad.recorded.should == []
end

it "raises an EncodingError at parse time when Symbol key with invalid bytes and 'key: value' syntax used" do
ScratchPad.record []
-> {
eval 'ScratchPad << 1; {"\xC3": 1}'
}.should raise_error(EncodingError, 'invalid symbol in encoding UTF-8 :"\xC3"')
ScratchPad.recorded.should == []
end
end

describe "The ** operator" do
Expand Down
4 changes: 2 additions & 2 deletions spec/ruby/language/symbol_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,11 @@
%I{a b #{"c"}}.should == [:a, :b, :c]
end

it "with invalid bytes raises an EncodingError at parse time" do
it "raises an EncodingError at parse time when Symbol with invalid bytes" do
ScratchPad.record []
-> {
eval 'ScratchPad << 1; :"\xC3"'
}.should raise_error(EncodingError, /invalid/)
}.should raise_error(EncodingError, 'invalid symbol in encoding UTF-8 :"\xC3"')
ScratchPad.recorded.should == []
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -1111,8 +1111,9 @@ public RubyException regexpError(String message, Node currentNode) {
// Encoding conversion errors.

@TruffleBoundary
public RubyException encodingError(String message, Node currentNode) {
public RubyException encodingError(Object string, RubyEncoding encoding, Node currentNode) {
RubyClass exceptionClass = context.getCoreLibrary().encodingErrorClass;
String message = StringUtils.format("invalid symbol in encoding %s :%s", encoding, inspect(string));
RubyString errorMessage = StringOperations.createUTF8String(context, language, message);

return ExceptionOperations.createRubyException(context, exceptionClass, errorMessage, currentNode, null);
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/truffleruby/core/string/StringNodes.java
Original file line number Diff line number Diff line change
Expand Up @@ -2618,7 +2618,7 @@ protected RubySymbol toSymBroken(Object string, boolean preserveSymbol,
@Cached RubyStringLibrary strings,
@Bind("strings.getTString(string)") AbstractTruffleString tstring,
@Bind("strings.getEncoding(string)") RubyEncoding encoding) {
throw new RaiseException(getContext(), coreExceptions().encodingError("invalid encoding symbol", this));
throw new RaiseException(getContext(), coreExceptions().encodingError(string, encoding, this));
}
}

Expand Down
25 changes: 25 additions & 0 deletions src/main/java/org/truffleruby/parser/BodyTranslator.java
Original file line number Diff line number Diff line change
Expand Up @@ -1307,13 +1307,38 @@ public RubyNode visitDStrNode(DStrParseNode node) {
public RubyNode visitDSymbolNode(DSymbolParseNode node) {
SourceIndexLength sourceSection = node.getPosition();

// A special case for a symbol in Hash literal {"key": value}
if (node.size() == 1 && node.getLast() instanceof StrParseNode) {
final StrParseNode strParseNode = (StrParseNode) node.getLast();
final SymbolParseNode symbolParseNode = asSymbol(sourceSection, strParseNode);
return visitSymbolNode(symbolParseNode);
}

final RubyNode stringNode = translateInterpolatedString(sourceSection, node.getEncoding(), node.children());

final RubyNode ret = StringToSymbolNodeGen.create(stringNode);
ret.unsafeSetSourceSection(sourceSection);
return addNewlineIfNeeded(node, ret);
}

/** Same as {@link ParserSupport#asSymbol(SourceIndexLength position, ParseNode value)} but without needing a
* ParserSupport instance and only for StrParseNode argument. */
private SymbolParseNode asSymbol(SourceIndexLength position, StrParseNode value) {
final SymbolParseNode symbolParseNode = new SymbolParseNode(position, value.getValue(),
value.encoding);

if (!symbolParseNode.getTString().isValidUncached(symbolParseNode.getRubyEncoding().tencoding)) {
final RubyContext context = RubyLanguage.getCurrentContext();
throw new RaiseException(
RubyLanguage.getCurrentContext(),
context.getCoreExceptions().encodingError(
symbolParseNode.getTString(),
symbolParseNode.getRubyEncoding(),
null));
}
return symbolParseNode;
}

private RubyNode translateInterpolatedString(SourceIndexLength sourceSection,
Encoding encoding, ParseNode[] childNodes) {
final ToSNode[] children = new ToSNode[childNodes.length];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1259,7 +1259,10 @@ private void checkSymbolCodeRange(SymbolParseNode symbolParseNode) {
if (!symbolParseNode.getTString().isValidUncached(symbolParseNode.getRubyEncoding().tencoding)) {
throw new RaiseException(
RubyLanguage.getCurrentContext(),
getConfiguration().getContext().getCoreExceptions().encodingError("invalid encoding symbol", null));
getConfiguration().getContext().getCoreExceptions().encodingError(
symbolParseNode.getTString(),
symbolParseNode.getRubyEncoding(),
null));
}
}

Expand Down

0 comments on commit 145d23e

Please sign in to comment.