Merge pull request #6858 from travv0/no-star-after-dot-star
don't allow a token starting with an asterisk directly following .*
This commit is contained in:
@@ -972,7 +972,7 @@ fn tokenizeAndPrintRaw(docgen_tokenizer: *Tokenizer, out: anytype, source_token:
|
||||
.Tilde,
|
||||
=> try writeEscaped(out, src[token.loc.start..token.loc.end]),
|
||||
|
||||
.Invalid, .Invalid_ampersands => return parseError(
|
||||
.Invalid, .Invalid_ampersands, .Invalid_periodasterisks => return parseError(
|
||||
docgen_tokenizer,
|
||||
source_token,
|
||||
"syntax error",
|
||||
|
||||
@@ -171,6 +171,7 @@ pub const Error = union(enum) {
|
||||
ExpectedBlockOrField: ExpectedBlockOrField,
|
||||
DeclBetweenFields: DeclBetweenFields,
|
||||
InvalidAnd: InvalidAnd,
|
||||
AsteriskAfterPointerDereference: AsteriskAfterPointerDereference,
|
||||
|
||||
pub fn render(self: *const Error, tokens: []const Token.Id, stream: anytype) !void {
|
||||
switch (self.*) {
|
||||
@@ -222,6 +223,7 @@ pub const Error = union(enum) {
|
||||
.ExpectedBlockOrField => |*x| return x.render(tokens, stream),
|
||||
.DeclBetweenFields => |*x| return x.render(tokens, stream),
|
||||
.InvalidAnd => |*x| return x.render(tokens, stream),
|
||||
.AsteriskAfterPointerDereference => |*x| return x.render(tokens, stream),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,6 +277,7 @@ pub const Error = union(enum) {
|
||||
.ExpectedBlockOrField => |x| return x.token,
|
||||
.DeclBetweenFields => |x| return x.token,
|
||||
.InvalidAnd => |x| return x.token,
|
||||
.AsteriskAfterPointerDereference => |x| return x.token,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,6 +326,7 @@ pub const Error = union(enum) {
|
||||
pub const ExtraAllowZeroQualifier = SimpleError("Extra allowzero qualifier");
|
||||
pub const DeclBetweenFields = SimpleError("Declarations are not allowed between container fields");
|
||||
pub const InvalidAnd = SimpleError("`&&` is invalid. Note that `and` is boolean AND.");
|
||||
pub const AsteriskAfterPointerDereference = SimpleError("`.*` can't be followed by `*`. Are you missing a space?");
|
||||
|
||||
pub const ExpectedCall = struct {
|
||||
node: *Node,
|
||||
|
||||
@@ -2701,6 +2701,19 @@ const Parser = struct {
|
||||
return &node.base;
|
||||
}
|
||||
|
||||
if (p.eatToken(.Invalid_periodasterisks)) |period_asterisk| {
|
||||
try p.errors.append(p.gpa, .{
|
||||
.AsteriskAfterPointerDereference = .{ .token = period_asterisk },
|
||||
});
|
||||
const node = try p.arena.allocator.create(Node.SimpleSuffixOp);
|
||||
node.* = .{
|
||||
.base = .{ .tag = .Deref },
|
||||
.lhs = lhs,
|
||||
.rtoken = period_asterisk,
|
||||
};
|
||||
return &node.base;
|
||||
}
|
||||
|
||||
if (p.eatToken(.Period)) |period| {
|
||||
if (try p.parseIdentifier()) |identifier| {
|
||||
const node = try p.arena.allocator.create(Node.SimpleInfixOp);
|
||||
|
||||
@@ -219,6 +219,24 @@ test "recovery: invalid global error set access" {
|
||||
});
|
||||
}
|
||||
|
||||
test "recovery: invalid asterisk after pointer dereference" {
|
||||
try testError(
|
||||
\\test "" {
|
||||
\\ var sequence = "repeat".*** 10;
|
||||
\\}
|
||||
, &[_]Error{
|
||||
.AsteriskAfterPointerDereference,
|
||||
});
|
||||
try testError(
|
||||
\\test "" {
|
||||
\\ var sequence = "repeat".** 10&&a;
|
||||
\\}
|
||||
, &[_]Error{
|
||||
.AsteriskAfterPointerDereference,
|
||||
.InvalidAnd,
|
||||
});
|
||||
}
|
||||
|
||||
test "recovery: missing semicolon after if, for, while stmt" {
|
||||
try testError(
|
||||
\\test "" {
|
||||
|
||||
@@ -78,6 +78,7 @@ pub const Token = struct {
|
||||
pub const Id = enum {
|
||||
Invalid,
|
||||
Invalid_ampersands,
|
||||
Invalid_periodasterisks,
|
||||
Identifier,
|
||||
StringLiteral,
|
||||
MultilineStringLiteralLine,
|
||||
@@ -201,6 +202,7 @@ pub const Token = struct {
|
||||
return switch (id) {
|
||||
.Invalid => "Invalid",
|
||||
.Invalid_ampersands => "&&",
|
||||
.Invalid_periodasterisks => ".**",
|
||||
.Identifier => "Identifier",
|
||||
.StringLiteral => "StringLiteral",
|
||||
.MultilineStringLiteralLine => "MultilineStringLiteralLine",
|
||||
@@ -403,6 +405,7 @@ pub const Tokenizer = struct {
|
||||
angle_bracket_angle_bracket_right,
|
||||
period,
|
||||
period_2,
|
||||
period_asterisk,
|
||||
saw_at_sign,
|
||||
};
|
||||
|
||||
@@ -979,9 +982,7 @@ pub const Tokenizer = struct {
|
||||
state = .period_2;
|
||||
},
|
||||
'*' => {
|
||||
result.id = .PeriodAsterisk;
|
||||
self.index += 1;
|
||||
break;
|
||||
state = .period_asterisk;
|
||||
},
|
||||
else => {
|
||||
result.id = .Period;
|
||||
@@ -1001,6 +1002,17 @@ pub const Tokenizer = struct {
|
||||
},
|
||||
},
|
||||
|
||||
.period_asterisk => switch (c) {
|
||||
'*' => {
|
||||
result.id = .Invalid_periodasterisks;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = .PeriodAsterisk;
|
||||
break;
|
||||
},
|
||||
},
|
||||
|
||||
.slash => switch (c) {
|
||||
'/' => {
|
||||
state = .line_comment_start;
|
||||
@@ -1376,6 +1388,9 @@ pub const Tokenizer = struct {
|
||||
.period_2 => {
|
||||
result.id = .Ellipsis2;
|
||||
},
|
||||
.period_asterisk => {
|
||||
result.id = .PeriodAsterisk;
|
||||
},
|
||||
.pipe => {
|
||||
result.id = .Pipe;
|
||||
},
|
||||
@@ -1762,6 +1777,31 @@ test "correctly parse pointer assignment" {
|
||||
});
|
||||
}
|
||||
|
||||
test "correctly parse pointer dereference followed by asterisk" {
|
||||
testTokenize("\"b\".* ** 10", &[_]Token.Id{
|
||||
.StringLiteral,
|
||||
.PeriodAsterisk,
|
||||
.AsteriskAsterisk,
|
||||
.IntegerLiteral,
|
||||
});
|
||||
|
||||
testTokenize("(\"b\".*)** 10", &[_]Token.Id{
|
||||
.LParen,
|
||||
.StringLiteral,
|
||||
.PeriodAsterisk,
|
||||
.RParen,
|
||||
.AsteriskAsterisk,
|
||||
.IntegerLiteral,
|
||||
});
|
||||
|
||||
testTokenize("\"b\".*** 10", &[_]Token.Id{
|
||||
.StringLiteral,
|
||||
.Invalid_periodasterisks,
|
||||
.AsteriskAsterisk,
|
||||
.IntegerLiteral,
|
||||
});
|
||||
}
|
||||
|
||||
test "tokenizer - range literals" {
|
||||
testTokenize("0...9", &[_]Token.Id{ .IntegerLiteral, .Ellipsis3, .IntegerLiteral });
|
||||
testTokenize("'0'...'9'", &[_]Token.Id{ .CharLiteral, .Ellipsis3, .CharLiteral });
|
||||
|
||||
@@ -223,6 +223,7 @@ enum TokenizeState {
|
||||
TokenizeStateSawGreaterThanGreaterThan,
|
||||
TokenizeStateSawDot,
|
||||
TokenizeStateSawDotDot,
|
||||
TokenizeStateSawDotStar,
|
||||
TokenizeStateSawAtSign,
|
||||
TokenizeStateCharCode,
|
||||
TokenizeStateError,
|
||||
@@ -566,9 +567,8 @@ void tokenize(Buf *buf, Tokenization *out) {
|
||||
set_token_id(&t, t.cur_tok, TokenIdEllipsis2);
|
||||
break;
|
||||
case '*':
|
||||
t.state = TokenizeStateStart;
|
||||
t.state = TokenizeStateSawDotStar;
|
||||
set_token_id(&t, t.cur_tok, TokenIdDotStar);
|
||||
end_token(&t);
|
||||
break;
|
||||
default:
|
||||
t.pos -= 1;
|
||||
@@ -591,6 +591,18 @@ void tokenize(Buf *buf, Tokenization *out) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case TokenizeStateSawDotStar:
|
||||
switch (c) {
|
||||
case '*':
|
||||
tokenize_error(&t, "`.*` can't be followed by `*`. Are you missing a space?");
|
||||
break;
|
||||
default:
|
||||
t.pos -= 1;
|
||||
end_token(&t);
|
||||
t.state = TokenizeStateStart;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case TokenizeStateSawGreaterThan:
|
||||
switch (c) {
|
||||
case '=':
|
||||
@@ -1481,6 +1493,7 @@ void tokenize(Buf *buf, Tokenization *out) {
|
||||
case TokenizeStateSawGreaterThan:
|
||||
case TokenizeStateSawGreaterThanGreaterThan:
|
||||
case TokenizeStateSawDot:
|
||||
case TokenizeStateSawDotStar:
|
||||
case TokenizeStateSawAtSign:
|
||||
case TokenizeStateSawStarPercent:
|
||||
case TokenizeStateSawPlusPercent:
|
||||
|
||||
@@ -8210,4 +8210,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:4:9: error: expected type '*c_void', found '?*c_void'",
|
||||
});
|
||||
|
||||
cases.add("Issue #6823: don't allow .* to be followed by **",
|
||||
\\fn foo() void {
|
||||
\\ var sequence = "repeat".*** 10;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:30: error: `.*` can't be followed by `*`. Are you missing a space?",
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user