Change the handling of invalid single quote to improve error messages. (#8370)

* add value_not_exposed report

* Change handling of invalid single-quote.

* fix typo
This commit is contained in:
Fabian Schmalzried 2025-11-11 13:42:35 +01:00 committed by GitHub
parent 856b909a57
commit a55b048dad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 242 additions and 225 deletions

View file

@ -2943,7 +2943,8 @@ fn canonicalizeDeclWithAnnotation(
fn parseSingleQuoteCodepoint( fn parseSingleQuoteCodepoint(
inner_text: []const u8, inner_text: []const u8,
) ?u21 { ) u21 {
// tokenizer checks for valid single quote codepoints, so every error case is unreachable here
const escaped = inner_text[0] == '\\'; const escaped = inner_text[0] == '\\';
if (escaped) { if (escaped) {
@ -2951,13 +2952,9 @@ fn parseSingleQuoteCodepoint(
switch (c) { switch (c) {
'u' => { 'u' => {
const hex_code = inner_text[3 .. inner_text.len - 1]; const hex_code = inner_text[3 .. inner_text.len - 1];
const codepoint = std.fmt.parseInt(u21, hex_code, 16) catch { const codepoint = std.fmt.parseInt(u21, hex_code, 16) catch unreachable;
return null;
};
if (!std.unicode.utf8ValidCodepoint(codepoint)) { std.debug.assert(std.unicode.utf8ValidCodepoint(codepoint));
return null;
}
return codepoint; return codepoint;
}, },
@ -2973,26 +2970,16 @@ fn parseSingleQuoteCodepoint(
't' => { 't' => {
return '\t'; return '\t';
}, },
else => { else => unreachable,
return null;
},
} }
} else { } else {
const view = std.unicode.Utf8View.init(inner_text) catch |err| switch (err) { const view = std.unicode.Utf8View.init(inner_text) catch unreachable;
error.InvalidUtf8 => {
return null;
},
};
var iterator = view.iterator(); var iterator = view.iterator();
if (iterator.nextCodepoint()) |codepoint| { const codepoint = iterator.nextCodepoint().?;
std.debug.assert(iterator.nextCodepoint() == null); std.debug.assert(iterator.nextCodepoint() == null);
return codepoint; return codepoint;
} else {
// only single valid utf8 codepoint can be here after tokenization
unreachable;
}
} }
} }
@ -3034,34 +3021,30 @@ fn canonicalizeSingleQuote(
// Resolve to a string slice from the source // Resolve to a string slice from the source
const token_text = self.parse_ir.resolve(token); const token_text = self.parse_ir.resolve(token);
std.debug.assert(token_text[0] == '\'' and token_text[token_text.len - 1] == '\'');
if (parseSingleQuoteCodepoint(token_text[1 .. token_text.len - 1])) |codepoint| { const codepoint = parseSingleQuoteCodepoint(token_text[1 .. token_text.len - 1]);
const value_content = CIR.IntValue{ const value_content = CIR.IntValue{
.bytes = @bitCast(@as(u128, @intCast(codepoint))), .bytes = @bitCast(@as(u128, @intCast(codepoint))),
.kind = .u128, .kind = .u128,
}; };
if (comptime Idx == Expr.Idx) { if (comptime Idx == Expr.Idx) {
const expr_idx = try self.env.addExpr(CIR.Expr{ const expr_idx = try self.env.addExpr(CIR.Expr{
.e_num = .{ .e_num = .{
.value = value_content,
.kind = .int_unbound,
},
}, region);
return expr_idx;
} else if (comptime Idx == Pattern.Idx) {
const pat_idx = try self.env.addPattern(Pattern{ .num_literal = .{
.value = value_content, .value = value_content,
.kind = .int_unbound, .kind = .int_unbound,
} }, region); },
return pat_idx; }, region);
} else { return expr_idx;
@compileError("Unsupported Idx type"); } else if (comptime Idx == Pattern.Idx) {
} const pat_idx = try self.env.addPattern(Pattern{ .num_literal = .{
.value = value_content,
.kind = .int_unbound,
} }, region);
return pat_idx;
} else {
@compileError("Unsupported Idx type");
} }
return try self.env.pushMalformed(Idx, Diagnostic{ .invalid_single_quote = .{
.region = region,
} });
} }
fn canonicalizeRecordField( fn canonicalizeRecordField(

View file

@ -30,9 +30,6 @@ pub const Diagnostic = union(enum) {
invalid_num_literal: struct { invalid_num_literal: struct {
region: Region, region: Region,
}, },
invalid_single_quote: struct {
region: Region,
},
empty_tuple: struct { empty_tuple: struct {
region: Region, region: Region,
}, },

View file

@ -954,7 +954,7 @@ pub fn diagnosticToReport(self: *Self, diagnostic: CIR.Diagnostic, allocator: st
// Format the message to match origin/main // Format the message to match origin/main
try report.document.addText("The type "); try report.document.addText("The type ");
try report.document.addInlineCode(type_name); try report.document.addInlineCode(type_name);
try report.document.addReflowingText(" is not an exposed by the module "); try report.document.addReflowingText(" is not exposed by the module ");
try report.document.addInlineCode(module_name); try report.document.addInlineCode(module_name);
try report.document.addReflowingText("."); try report.document.addReflowingText(".");
try report.document.addLineBreak(); try report.document.addLineBreak();
@ -973,6 +973,33 @@ pub fn diagnosticToReport(self: *Self, diagnostic: CIR.Diagnostic, allocator: st
break :blk report; break :blk report;
}, },
.value_not_exposed => |data| blk: {
const region_info = self.calcRegionInfo(data.region);
var report = Report.init(allocator, "VALUE NOT EXPOSED", .runtime_error);
// Format the message to match origin/main
try report.document.addText("The value ");
try report.document.addInlineCode(self.getIdent(data.value_name));
try report.document.addReflowingText(" is not exposed by the module ");
try report.document.addInlineCode(self.getIdent(data.module_name));
try report.document.addReflowingText(".");
try report.document.addLineBreak();
try report.document.addLineBreak();
try report.document.addReflowingText("You're attempting to use this value here:");
try report.document.addLineBreak();
const owned_filename = try report.addOwnedString(filename);
try report.document.addSourceRegion(
region_info,
.error_highlight,
owned_filename,
self.getSourceAll(),
self.getLineStartsAll(),
);
break :blk report;
},
.module_not_found => |data| blk: { .module_not_found => |data| blk: {
const region_info = self.calcRegionInfo(data.region); const region_info = self.calcRegionInfo(data.region);

View file

@ -151,7 +151,6 @@ pub const Tag = enum {
// diagnostic indices stored in malformed nodes. // diagnostic indices stored in malformed nodes.
diag_not_implemented, diag_not_implemented,
diag_invalid_num_literal, diag_invalid_num_literal,
diag_invalid_single_quote,
diag_empty_single_quote, diag_empty_single_quote,
diag_empty_tuple, diag_empty_tuple,
diag_ident_already_in_scope, diag_ident_already_in_scope,

View file

@ -128,7 +128,7 @@ pub fn deinit(store: *NodeStore) void {
/// when adding/removing variants from ModuleEnv unions. Update these when modifying the unions. /// when adding/removing variants from ModuleEnv unions. Update these when modifying the unions.
/// ///
/// Count of the diagnostic nodes in the ModuleEnv /// Count of the diagnostic nodes in the ModuleEnv
pub const MODULEENV_DIAGNOSTIC_NODE_COUNT = 59; pub const MODULEENV_DIAGNOSTIC_NODE_COUNT = 58;
/// Count of the expression nodes in the ModuleEnv /// Count of the expression nodes in the ModuleEnv
pub const MODULEENV_EXPR_NODE_COUNT = 35; pub const MODULEENV_EXPR_NODE_COUNT = 35;
/// Count of the statement nodes in the ModuleEnv /// Count of the statement nodes in the ModuleEnv
@ -2670,10 +2670,6 @@ pub fn addDiagnostic(store: *NodeStore, reason: CIR.Diagnostic) Allocator.Error!
node.tag = .diag_invalid_num_literal; node.tag = .diag_invalid_num_literal;
region = r.region; region = r.region;
}, },
.invalid_single_quote => |r| {
node.tag = .diag_invalid_single_quote;
region = r.region;
},
.empty_tuple => |r| { .empty_tuple => |r| {
node.tag = .diag_empty_tuple; node.tag = .diag_empty_tuple;
region = r.region; region = r.region;
@ -3029,9 +3025,6 @@ pub fn getDiagnostic(store: *const NodeStore, diagnostic: CIR.Diagnostic.Idx) CI
.diag_invalid_num_literal => return CIR.Diagnostic{ .invalid_num_literal = .{ .diag_invalid_num_literal => return CIR.Diagnostic{ .invalid_num_literal = .{
.region = store.getRegionAt(node_idx), .region = store.getRegionAt(node_idx),
} }, } },
.diag_invalid_single_quote => return CIR.Diagnostic{ .invalid_single_quote = .{
.region = store.getRegionAt(node_idx),
} },
.diag_empty_tuple => return CIR.Diagnostic{ .empty_tuple = .{ .diag_empty_tuple => return CIR.Diagnostic{ .empty_tuple = .{
.region = store.getRegionAt(node_idx), .region = store.getRegionAt(node_idx),
} }, } },

View file

@ -686,12 +686,6 @@ test "NodeStore round trip - Diagnostics" {
}, },
}); });
try diagnostics.append(gpa, CIR.Diagnostic{
.invalid_single_quote = .{
.region = rand_region(),
},
});
try diagnostics.append(gpa, CIR.Diagnostic{ try diagnostics.append(gpa, CIR.Diagnostic{
.f64_pattern_literal = .{ .f64_pattern_literal = .{
.region = rand_region(), .region = rand_region(),

View file

@ -142,6 +142,9 @@ pub fn tokenizeDiagnosticToReport(self: *AST, diagnostic: tokenize.Diagnostic, a
.NonPrintableUnicodeInStrLiteral => "NON-PRINTABLE UNICODE IN STRING-LIKE LITERAL", .NonPrintableUnicodeInStrLiteral => "NON-PRINTABLE UNICODE IN STRING-LIKE LITERAL",
.InvalidUtf8InSource => "INVALID UTF-8", .InvalidUtf8InSource => "INVALID UTF-8",
.DollarInMiddleOfIdentifier => "STRAY DOLLAR SIGN", .DollarInMiddleOfIdentifier => "STRAY DOLLAR SIGN",
.SingleQuoteTooLong => "SINGLE QUOTE TOO LONG",
.SingleQuoteEmpty => "SINGLE QUOTE EMPTY",
.SingleQuoteUnclosed => "UNCLOSED SINGLE QUOTE",
}; };
const body = switch (diagnostic.tag) { const body = switch (diagnostic.tag) {
@ -155,6 +158,8 @@ pub fn tokenizeDiagnosticToReport(self: *AST, diagnostic: tokenize.Diagnostic, a
.NonPrintableUnicodeInStrLiteral => "Non-printable Unicode characters are not allowed in string-like literals.", .NonPrintableUnicodeInStrLiteral => "Non-printable Unicode characters are not allowed in string-like literals.",
.InvalidUtf8InSource => "Invalid UTF-8 encoding found in source code. Roc source files must be valid UTF-8.", .InvalidUtf8InSource => "Invalid UTF-8 encoding found in source code. Roc source files must be valid UTF-8.",
.DollarInMiddleOfIdentifier => "Dollar sign ($) is only allowed at the very beginning of a name, not in the middle or at the end.", .DollarInMiddleOfIdentifier => "Dollar sign ($) is only allowed at the very beginning of a name, not in the middle or at the end.",
.SingleQuoteTooLong, .SingleQuoteEmpty => "Single-quoted literals must contain exactly one valid UTF-8 codepoint.",
.SingleQuoteUnclosed => "This single-quoted literal is missing a closing quote.",
}; };
var report = reporting.Report.init(allocator, title, .runtime_error); var report = reporting.Report.init(allocator, title, .runtime_error);

View file

@ -50,10 +50,7 @@ pub const Token = struct {
StringPart, StringPart,
MalformedStringPart, // malformed, but should be treated similar to a StringPart in the parser MalformedStringPart, // malformed, but should be treated similar to a StringPart in the parser
SingleQuote, SingleQuote,
MalformedSingleQuoteUnclosed, // malformed, but should be treated similar to a SingleQuote in the parser MalformedSingleQuote, // malformed, but should be treated similar to a SingleQuote in the parser
MalformedSingleQuoteEmpty, // malformed, but should be treated similar to a SingleQuote in the parser
MalformedSingleQuoteTooLong, // malformed, but should be treated similar to a SingleQuote in the parser
MalformedSingleQuoteInvalidEscapeSequence, // malformed, but should be treated similar to a SingleQuote in the parser
Int, Int,
MalformedNumberBadSuffix, // malformed, but should be treated similar to an int in the parser MalformedNumberBadSuffix, // malformed, but should be treated similar to an int in the parser
MalformedNumberUnicodeSuffix, // malformed, but should be treated similar to an int in the parser MalformedNumberUnicodeSuffix, // malformed, but should be treated similar to an int in the parser
@ -315,10 +312,7 @@ pub const Token = struct {
.MalformedOpaqueNameWithoutName, .MalformedOpaqueNameWithoutName,
.MalformedUnicodeIdent, .MalformedUnicodeIdent,
.MalformedUnknownToken, .MalformedUnknownToken,
.MalformedSingleQuoteUnclosed, .MalformedSingleQuote,
.MalformedSingleQuoteEmpty,
.MalformedSingleQuoteTooLong,
.MalformedSingleQuoteInvalidEscapeSequence,
.MalformedStringPart, .MalformedStringPart,
=> true, => true,
}; };
@ -486,6 +480,9 @@ pub const Diagnostic = struct {
NonPrintableUnicodeInStrLiteral, NonPrintableUnicodeInStrLiteral,
InvalidUtf8InSource, InvalidUtf8InSource,
DollarInMiddleOfIdentifier, DollarInMiddleOfIdentifier,
SingleQuoteTooLong,
SingleQuoteEmpty,
SingleQuoteUnclosed,
}; };
}; };
@ -924,6 +921,16 @@ pub const Cursor = struct {
return error.InvalidUnicodeEscapeSequence; return error.InvalidUnicodeEscapeSequence;
} }
} }
const hex_code = self.buf[hex_start .. self.pos - 1];
const codepoint = std.fmt.parseInt(u21, hex_code, 16) catch {
self.pushMessage(.InvalidUnicodeEscapeSequence, escape_start, self.pos);
return error.InvalidUnicodeEscapeSequence;
};
if (!std.unicode.utf8ValidCodepoint(codepoint)) {
self.pushMessage(.InvalidUnicodeEscapeSequence, escape_start, self.pos);
return error.InvalidUnicodeEscapeSequence;
}
}, },
else => { else => {
// Include the character after the backslash in the error region // Include the character after the backslash in the error region
@ -941,7 +948,9 @@ pub const Cursor = struct {
TooLong, TooLong,
Invalid, Invalid,
}; };
std.debug.assert(self.peek() == '\''); std.debug.assert(self.peek() == '\'');
const start = self.pos;
// Skip the initial quote. // Skip the initial quote.
self.pos += 1; self.pos += 1;
@ -959,7 +968,8 @@ pub const Cursor = struct {
switch (state) { switch (state) {
.Empty => switch (c) { .Empty => switch (c) {
'\'' => { '\'' => {
return .MalformedSingleQuoteEmpty; self.pushMessage(.SingleQuoteEmpty, start, self.pos);
return .MalformedSingleQuote;
}, },
'\\' => { '\\' => {
state = .Enough; state = .Enough;
@ -983,20 +993,22 @@ pub const Cursor = struct {
}, },
.TooLong => switch (c) { .TooLong => switch (c) {
'\'' => { '\'' => {
return .MalformedSingleQuoteTooLong; self.pushMessage(.SingleQuoteTooLong, start, self.pos);
return .MalformedSingleQuote;
}, },
else => {}, else => {},
}, },
.Invalid => switch (c) { .Invalid => switch (c) {
'\'' => { '\'' => {
return .MalformedSingleQuoteInvalidEscapeSequence; return .MalformedSingleQuote;
}, },
else => {}, else => {},
}, },
} }
} }
return .MalformedSingleQuoteUnclosed; self.pushMessage(.SingleQuoteUnclosed, start, self.pos);
return .MalformedSingleQuote;
} }
/// Chomps a UTF-8 codepoint and advances the cursor position. /// Chomps a UTF-8 codepoint and advances the cursor position.
@ -2277,10 +2289,7 @@ fn rebuildBufferForTesting(buf: []const u8, tokens: *TokenizedBuffer, alloc: std
.MalformedNamedUnderscoreUnicode, .MalformedNamedUnderscoreUnicode,
.MalformedOpaqueNameUnicode, .MalformedOpaqueNameUnicode,
.MalformedOpaqueNameWithoutName, .MalformedOpaqueNameWithoutName,
.MalformedSingleQuoteEmpty, .MalformedSingleQuote,
.MalformedSingleQuoteTooLong,
.MalformedSingleQuoteUnclosed,
.MalformedSingleQuoteInvalidEscapeSequence,
.MalformedStringPart, .MalformedStringPart,
=> { => {
return error.Unsupported; return error.Unsupported;

View file

@ -8,27 +8,34 @@ type=expr
"\u(FFFFFF)" "\u(FFFFFF)"
~~~ ~~~
# EXPECTED # EXPECTED
NIL INVALID UNICODE ESCAPE SEQUENCE - unicode_overflow_str.md:1:2:1:12
# PROBLEMS # PROBLEMS
NIL **INVALID UNICODE ESCAPE SEQUENCE**
This Unicode escape sequence is not valid.
**unicode_overflow_str.md:1:2:1:12:**
```roc
"\u(FFFFFF)"
```
^^^^^^^^^^
# TOKENS # TOKENS
~~~zig ~~~zig
StringStart,StringPart,StringEnd, StringStart,MalformedStringPart,StringEnd,
EndOfFile, EndOfFile,
~~~ ~~~
# PARSE # PARSE
~~~clojure ~~~clojure
(e-string (e-string)
(e-string-part (raw "\u(FFFFFF)")))
~~~ ~~~
# FORMATTED # FORMATTED
~~~roc ~~~roc
NO CHANGE ""
~~~ ~~~
# CANONICALIZE # CANONICALIZE
~~~clojure ~~~clojure
(e-string (e-string)
(e-literal (string "\u(FFFFFF)")))
~~~ ~~~
# TYPES # TYPES
~~~clojure ~~~clojure

View file

@ -11,6 +11,7 @@ mule []
vavar t= ' vavar t= '
~~~ ~~~
# EXPECTED # EXPECTED
UNCLOSED SINGLE QUOTE - fuzz_crash_031.md:4:10:4:11
PARSE ERROR - fuzz_crash_031.md:1:1:1:5 PARSE ERROR - fuzz_crash_031.md:1:1:1:5
PARSE ERROR - fuzz_crash_031.md:1:6:1:7 PARSE ERROR - fuzz_crash_031.md:1:6:1:7
PARSE ERROR - fuzz_crash_031.md:1:7:1:8 PARSE ERROR - fuzz_crash_031.md:1:7:1:8
@ -19,6 +20,16 @@ UNEXPECTED TOKEN IN EXPRESSION - fuzz_crash_031.md:4:10:4:11
UNRECOGNIZED SYNTAX - fuzz_crash_031.md:4:10:4:11 UNRECOGNIZED SYNTAX - fuzz_crash_031.md:4:10:4:11
MISSING MAIN! FUNCTION - fuzz_crash_031.md:1:1:4:11 MISSING MAIN! FUNCTION - fuzz_crash_031.md:1:1:4:11
# PROBLEMS # PROBLEMS
**UNCLOSED SINGLE QUOTE**
This single-quoted literal is missing a closing quote.
**fuzz_crash_031.md:4:10:4:11:**
```roc
vavar t= '
```
^
**PARSE ERROR** **PARSE ERROR**
A parsing error occurred: `statement_unexpected_token` A parsing error occurred: `statement_unexpected_token`
This is an unexpected parsing error. Please check your syntax. This is an unexpected parsing error. Please check your syntax.
@ -104,7 +115,7 @@ vavar t= '
# TOKENS # TOKENS
~~~zig ~~~zig
LowerIdent,OpenSquare,CloseSquare, LowerIdent,OpenSquare,CloseSquare,
LowerIdent,LowerIdent,OpAssign,MalformedSingleQuoteUnclosed, LowerIdent,LowerIdent,OpAssign,MalformedSingleQuote,
EndOfFile, EndOfFile,
~~~ ~~~
# PARSE # PARSE

View file

@ -9,9 +9,20 @@ module[}('
) )
~~~ ~~~
# EXPECTED # EXPECTED
UNCLOSED SINGLE QUOTE - fuzz_crash_039.md:1:10:1:11
PARSE ERROR - fuzz_crash_039.md:1:8:1:9 PARSE ERROR - fuzz_crash_039.md:1:8:1:9
PARSE ERROR - fuzz_crash_039.md:3:1:3:1 PARSE ERROR - fuzz_crash_039.md:3:1:3:1
# PROBLEMS # PROBLEMS
**UNCLOSED SINGLE QUOTE**
This single-quoted literal is missing a closing quote.
**fuzz_crash_039.md:1:10:1:11:**
```roc
module[}('
```
^
**PARSE ERROR** **PARSE ERROR**
A parsing error occurred: `exposed_item_unexpected_token` A parsing error occurred: `exposed_item_unexpected_token`
This is an unexpected parsing error. Please check your syntax. This is an unexpected parsing error. Please check your syntax.
@ -36,7 +47,7 @@ This is an unexpected parsing error. Please check your syntax.
# TOKENS # TOKENS
~~~zig ~~~zig
KwModule,OpenSquare,CloseCurly,NoSpaceOpenRound,MalformedSingleQuoteUnclosed, KwModule,OpenSquare,CloseCurly,NoSpaceOpenRound,MalformedSingleQuote,
CloseRound, CloseRound,
EndOfFile, EndOfFile,
~~~ ~~~

View file

@ -14,6 +14,7 @@ x = (
'\u(', '\u(',
'\u()', '\u()',
'\u(1F680)', '\u(1F680)',
'\u(EDA0B5)'
'\u(K)', '\u(K)',
'\\', '\\',
'\'', '\'',
@ -32,27 +33,24 @@ INVALID UNICODE ESCAPE SEQUENCE - unicode_single_quotes.md:5:6:5:8
INVALID UNICODE ESCAPE SEQUENCE - unicode_single_quotes.md:6:6:6:8 INVALID UNICODE ESCAPE SEQUENCE - unicode_single_quotes.md:6:6:6:8
INVALID UNICODE ESCAPE SEQUENCE - unicode_single_quotes.md:7:6:7:9 INVALID UNICODE ESCAPE SEQUENCE - unicode_single_quotes.md:7:6:7:9
INVALID UNICODE ESCAPE SEQUENCE - unicode_single_quotes.md:8:6:8:10 INVALID UNICODE ESCAPE SEQUENCE - unicode_single_quotes.md:8:6:8:10
INVALID UNICODE ESCAPE SEQUENCE - unicode_single_quotes.md:10:6:10:11 INVALID UNICODE ESCAPE SEQUENCE - unicode_single_quotes.md:10:6:10:16
INVALID ESCAPE SEQUENCE - unicode_single_quotes.md:21:2:22:1 INVALID UNICODE ESCAPE SEQUENCE - unicode_single_quotes.md:11:6:11:11
SINGLE QUOTE EMPTY - unicode_single_quotes.md:14:5:14:7
SINGLE QUOTE TOO LONG - unicode_single_quotes.md:15:5:15:11
UNCLOSED SINGLE QUOTE - unicode_single_quotes.md:16:5:16:9
UNCLOSED SINGLE QUOTE - unicode_single_quotes.md:19:5:19:7
INVALID ESCAPE SEQUENCE - unicode_single_quotes.md:22:2:23:1
UNCLOSED SINGLE QUOTE - unicode_single_quotes.md:22:1:22:3
UNEXPECTED TOKEN IN EXPRESSION - unicode_single_quotes.md:5:5:5:9 UNEXPECTED TOKEN IN EXPRESSION - unicode_single_quotes.md:5:5:5:9
UNEXPECTED TOKEN IN EXPRESSION - unicode_single_quotes.md:6:5:6:10 UNEXPECTED TOKEN IN EXPRESSION - unicode_single_quotes.md:6:5:6:10
UNEXPECTED TOKEN IN EXPRESSION - unicode_single_quotes.md:7:5:7:10 UNEXPECTED TOKEN IN EXPRESSION - unicode_single_quotes.md:7:5:7:10
UNEXPECTED TOKEN IN EXPRESSION - unicode_single_quotes.md:8:5:8:11 UNEXPECTED TOKEN IN EXPRESSION - unicode_single_quotes.md:8:5:8:11
UNEXPECTED TOKEN IN EXPRESSION - unicode_single_quotes.md:10:5:10:12 UNEXPECTED TOKEN IN EXPRESSION - unicode_single_quotes.md:10:5:10:17
UNEXPECTED TOKEN IN EXPRESSION - unicode_single_quotes.md:13:5:13:7 PARSE ERROR - unicode_single_quotes.md:17:1:17:2
UNEXPECTED TOKEN IN EXPRESSION - unicode_single_quotes.md:14:5:14:11 UNEXPECTED TOKEN IN EXPRESSION - unicode_single_quotes.md:19:5:19:7
UNEXPECTED TOKEN IN EXPRESSION - unicode_single_quotes.md:15:5:15:9 PARSE ERROR - unicode_single_quotes.md:22:1:22:3
UNEXPECTED TOKEN IN EXPRESSION - unicode_single_quotes.md:18:5:18:7 UNRECOGNIZED SYNTAX - unicode_single_quotes.md:17:1:17:2
PARSE ERROR - unicode_single_quotes.md:21:1:21:3 UNRECOGNIZED SYNTAX - unicode_single_quotes.md:19:5:19:7
INVALID TUPLE ELEMENT - :0:0:0:0
INVALID TUPLE ELEMENT - :0:0:0:0
INVALID TUPLE ELEMENT - :0:0:0:0
INVALID TUPLE ELEMENT - :0:0:0:0
INVALID TUPLE ELEMENT - :0:0:0:0
INVALID TUPLE ELEMENT - :0:0:0:0
INVALID TUPLE ELEMENT - :0:0:0:0
INVALID TUPLE ELEMENT - :0:0:0:0
UNRECOGNIZED SYNTAX - unicode_single_quotes.md:18:5:18:7
# PROBLEMS # PROBLEMS
**INVALID UNICODE ESCAPE SEQUENCE** **INVALID UNICODE ESCAPE SEQUENCE**
This Unicode escape sequence is not valid. This Unicode escape sequence is not valid.
@ -97,23 +95,83 @@ This Unicode escape sequence is not valid.
**INVALID UNICODE ESCAPE SEQUENCE** **INVALID UNICODE ESCAPE SEQUENCE**
This Unicode escape sequence is not valid. This Unicode escape sequence is not valid.
**unicode_single_quotes.md:10:6:10:11:** **unicode_single_quotes.md:10:6:10:16:**
```roc
'\u(EDA0B5)'
```
^^^^^^^^^^
**INVALID UNICODE ESCAPE SEQUENCE**
This Unicode escape sequence is not valid.
**unicode_single_quotes.md:11:6:11:11:**
```roc ```roc
'\u(K)', '\u(K)',
``` ```
^^^^^ ^^^^^
**SINGLE QUOTE EMPTY**
Single-quoted literals must contain exactly one valid UTF-8 codepoint.
**unicode_single_quotes.md:14:5:14:7:**
```roc
'',
```
^^
**SINGLE QUOTE TOO LONG**
Single-quoted literals must contain exactly one valid UTF-8 codepoint.
**unicode_single_quotes.md:15:5:15:11:**
```roc
'long',
```
^^^^^^
**UNCLOSED SINGLE QUOTE**
This single-quoted literal is missing a closing quote.
**unicode_single_quotes.md:16:5:16:9:**
```roc
'\',
```
^^^^
**UNCLOSED SINGLE QUOTE**
This single-quoted literal is missing a closing quote.
**unicode_single_quotes.md:19:5:19:7:**
```roc
y = 'u
```
^^
**INVALID ESCAPE SEQUENCE** **INVALID ESCAPE SEQUENCE**
This escape sequence is not recognized. This escape sequence is not recognized.
**unicode_single_quotes.md:21:2:22:1:** **unicode_single_quotes.md:22:2:23:1:**
```roc ```roc
'\ '\
``` ```
**UNCLOSED SINGLE QUOTE**
This single-quoted literal is missing a closing quote.
**unicode_single_quotes.md:22:1:22:3:**
```roc
'\
```
^^
**UNEXPECTED TOKEN IN EXPRESSION** **UNEXPECTED TOKEN IN EXPRESSION**
The token **'\u'** is not expected in an expression. The token **'\u'** is not expected in an expression.
Expressions can be identifiers, literals, function calls, or operators. Expressions can be identifiers, literals, function calls, or operators.
@ -159,54 +217,32 @@ Expressions can be identifiers, literals, function calls, or operators.
**UNEXPECTED TOKEN IN EXPRESSION** **UNEXPECTED TOKEN IN EXPRESSION**
The token **'\u(K)'** is not expected in an expression. The token **'\u(EDA0B5)'** is not expected in an expression.
Expressions can be identifiers, literals, function calls, or operators. Expressions can be identifiers, literals, function calls, or operators.
**unicode_single_quotes.md:10:5:10:12:** **unicode_single_quotes.md:10:5:10:17:**
```roc ```roc
'\u(K)', '\u(EDA0B5)'
``` ```
^^^^^^^ ^^^^^^^^^^^^
**UNEXPECTED TOKEN IN EXPRESSION** **PARSE ERROR**
The token **''** is not expected in an expression. A parsing error occurred: `expected_expr_close_round_or_comma`
Expressions can be identifiers, literals, function calls, or operators. This is an unexpected parsing error. Please check your syntax.
**unicode_single_quotes.md:13:5:13:7:** **unicode_single_quotes.md:17:1:17:2:**
```roc ```roc
'', )
``` ```
^^ ^
**UNEXPECTED TOKEN IN EXPRESSION**
The token **'long'** is not expected in an expression.
Expressions can be identifiers, literals, function calls, or operators.
**unicode_single_quotes.md:14:5:14:11:**
```roc
'long',
```
^^^^^^
**UNEXPECTED TOKEN IN EXPRESSION**
The token **'\',** is not expected in an expression.
Expressions can be identifiers, literals, function calls, or operators.
**unicode_single_quotes.md:15:5:15:9:**
```roc
'\',
```
^^^^
**UNEXPECTED TOKEN IN EXPRESSION** **UNEXPECTED TOKEN IN EXPRESSION**
The token **'u** is not expected in an expression. The token **'u** is not expected in an expression.
Expressions can be identifiers, literals, function calls, or operators. Expressions can be identifiers, literals, function calls, or operators.
**unicode_single_quotes.md:18:5:18:7:** **unicode_single_quotes.md:19:5:19:7:**
```roc ```roc
y = 'u y = 'u
``` ```
@ -217,41 +253,28 @@ y = 'u
A parsing error occurred: `statement_unexpected_token` A parsing error occurred: `statement_unexpected_token`
This is an unexpected parsing error. Please check your syntax. This is an unexpected parsing error. Please check your syntax.
**unicode_single_quotes.md:21:1:21:3:** **unicode_single_quotes.md:22:1:22:3:**
```roc ```roc
'\ '\
``` ```
^^ ^^
**INVALID TUPLE ELEMENT** **UNRECOGNIZED SYNTAX**
This tuple element is malformed or contains invalid syntax. I don't recognize this syntax.
**INVALID TUPLE ELEMENT** **unicode_single_quotes.md:17:1:17:2:**
This tuple element is malformed or contains invalid syntax. ```roc
)
```
^
**INVALID TUPLE ELEMENT** This might be a syntax error, an unsupported language feature, or a typo.
This tuple element is malformed or contains invalid syntax.
**INVALID TUPLE ELEMENT**
This tuple element is malformed or contains invalid syntax.
**INVALID TUPLE ELEMENT**
This tuple element is malformed or contains invalid syntax.
**INVALID TUPLE ELEMENT**
This tuple element is malformed or contains invalid syntax.
**INVALID TUPLE ELEMENT**
This tuple element is malformed or contains invalid syntax.
**INVALID TUPLE ELEMENT**
This tuple element is malformed or contains invalid syntax.
**UNRECOGNIZED SYNTAX** **UNRECOGNIZED SYNTAX**
I don't recognize this syntax. I don't recognize this syntax.
**unicode_single_quotes.md:18:5:18:7:** **unicode_single_quotes.md:19:5:19:7:**
```roc ```roc
y = 'u y = 'u
``` ```
@ -265,20 +288,21 @@ LowerIdent,OpAssign,OpenRound,
SingleQuote,Comma, SingleQuote,Comma,
SingleQuote,Comma, SingleQuote,Comma,
SingleQuote,Comma, SingleQuote,Comma,
MalformedSingleQuoteInvalidEscapeSequence,Comma, MalformedSingleQuote,Comma,
MalformedSingleQuoteInvalidEscapeSequence,Comma, MalformedSingleQuote,Comma,
MalformedSingleQuoteInvalidEscapeSequence,Comma, MalformedSingleQuote,Comma,
MalformedSingleQuoteInvalidEscapeSequence,Comma, MalformedSingleQuote,Comma,
SingleQuote,Comma, SingleQuote,Comma,
MalformedSingleQuoteInvalidEscapeSequence,Comma, MalformedSingleQuote,
MalformedSingleQuote,Comma,
SingleQuote,Comma, SingleQuote,Comma,
SingleQuote,Comma, SingleQuote,Comma,
MalformedSingleQuoteEmpty,Comma, MalformedSingleQuote,Comma,
MalformedSingleQuoteTooLong,Comma, MalformedSingleQuote,Comma,
MalformedSingleQuoteUnclosed, MalformedSingleQuote,
CloseRound, CloseRound,
LowerIdent,OpAssign,MalformedSingleQuoteUnclosed, LowerIdent,OpAssign,MalformedSingleQuote,
MalformedSingleQuoteUnclosed, MalformedSingleQuote,
EndOfFile, EndOfFile,
~~~ ~~~
# PARSE # PARSE
@ -288,21 +312,7 @@ EndOfFile,
(statements (statements
(s-decl (s-decl
(p-ident (raw "x")) (p-ident (raw "x"))
(e-tuple (e-malformed (reason "expected_expr_close_round_or_comma")))
(e-single-quote (raw "'a'"))
(e-single-quote (raw "'é'"))
(e-single-quote (raw "'🚀'"))
(e-malformed (reason "expr_unexpected_token"))
(e-malformed (reason "expr_unexpected_token"))
(e-malformed (reason "expr_unexpected_token"))
(e-malformed (reason "expr_unexpected_token"))
(e-single-quote (raw "'\u(1F680)'"))
(e-malformed (reason "expr_unexpected_token"))
(e-single-quote (raw "'\\'"))
(e-single-quote (raw "'\''"))
(e-malformed (reason "expr_unexpected_token"))
(e-malformed (reason "expr_unexpected_token"))
(e-malformed (reason "expr_unexpected_token"))))
(s-decl (s-decl
(p-ident (raw "y")) (p-ident (raw "y"))
(e-malformed (reason "expr_unexpected_token"))) (e-malformed (reason "expr_unexpected_token")))
@ -310,22 +320,8 @@ EndOfFile,
~~~ ~~~
# FORMATTED # FORMATTED
~~~roc ~~~roc
x = ( x =
'a',
'é',
'🚀',
,
,
,
,
'\u(1F680)',
,
'\\',
'\'',
,
,
,
)
y = y =
@ -336,22 +332,7 @@ y =
(can-ir (can-ir
(d-let (d-let
(p-assign (ident "x")) (p-assign (ident "x"))
(e-tuple (e-runtime-error (tag "expr_not_canonicalized")))
(elems
(e-num (value "97"))
(e-num (value "233"))
(e-num (value "128640"))
(e-runtime-error (tag "tuple_elem_not_canonicalized"))
(e-runtime-error (tag "tuple_elem_not_canonicalized"))
(e-runtime-error (tag "tuple_elem_not_canonicalized"))
(e-runtime-error (tag "tuple_elem_not_canonicalized"))
(e-num (value "128640"))
(e-runtime-error (tag "tuple_elem_not_canonicalized"))
(e-num (value "92"))
(e-num (value "39"))
(e-runtime-error (tag "tuple_elem_not_canonicalized"))
(e-runtime-error (tag "tuple_elem_not_canonicalized"))
(e-runtime-error (tag "tuple_elem_not_canonicalized")))))
(d-let (d-let
(p-assign (ident "y")) (p-assign (ident "y"))
(e-runtime-error (tag "expr_not_canonicalized")))) (e-runtime-error (tag "expr_not_canonicalized"))))
@ -360,9 +341,9 @@ y =
~~~clojure ~~~clojure
(inferred-types (inferred-types
(defs (defs
(patt (type "(Num(Int(_size)), Num(Int(_size2)), Num(Int(_size3)), Error, Error, Error, Error, Num(Int(_size4)), Error, Num(Int(_size5)), Num(Int(_size6)), Error, Error, Error)")) (patt (type "Error"))
(patt (type "Error"))) (patt (type "Error")))
(expressions (expressions
(expr (type "(Num(Int(_size)), Num(Int(_size2)), Num(Int(_size3)), Error, Error, Error, Error, Num(Int(_size4)), Error, Num(Int(_size5)), Num(Int(_size6)), Error, Error, Error)")) (expr (type "Error"))
(expr (type "Error")))) (expr (type "Error"))))
~~~ ~~~