Skip to content

Commit 29a63fb

Browse files
committed
Teach concat!() to concatenate byte str literals
1 parent 70cac59 commit 29a63fb

File tree

5 files changed

+92
-12
lines changed

5 files changed

+92
-12
lines changed

src/libsyntax/ast.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1300,6 +1300,11 @@ impl LitKind {
13001300
}
13011301
}
13021302

1303+
/// Returns a `LitKind::ByteStr` from `content`.
1304+
pub fn new_byte_str(content: Vec<u8>) -> LitKind {
1305+
LitKind::ByteStr(Lrc::new(content))
1306+
}
1307+
13031308
/// Returns true if this is a numeric literal.
13041309
pub fn is_numeric(&self) -> bool {
13051310
match *self {

src/libsyntax_ext/concat.rs

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,42 +13,54 @@ use syntax::ext::base;
1313
use syntax::ext::build::AstBuilder;
1414
use syntax::symbol::Symbol;
1515
use syntax::tokenstream;
16-
use syntax_pos;
16+
use syntax_pos::Span;
1717

1818
use std::string::String;
1919

2020
pub fn expand_syntax_ext(
2121
cx: &mut base::ExtCtxt,
22-
sp: syntax_pos::Span,
22+
sp: Span,
2323
tts: &[tokenstream::TokenTree],
2424
) -> Box<dyn base::MacResult + 'static> {
2525
let es = match base::get_exprs_from_tts(cx, sp, tts) {
2626
Some(e) => e,
2727
None => return base::DummyResult::expr(sp),
2828
};
29-
let mut accumulator = String::new();
29+
let mut string_accumulator = String::new();
30+
let mut string_pos = vec![];
31+
let mut b_accumulator: Vec<u8> = vec![];
32+
let mut b_pos: Vec<Span> = vec![];
3033
let mut missing_literal = vec![];
3134
for e in es {
3235
match e.node {
3336
ast::ExprKind::Lit(ref lit) => match lit.node {
3437
ast::LitKind::Str(ref s, _)
3538
| ast::LitKind::Float(ref s, _)
3639
| ast::LitKind::FloatUnsuffixed(ref s) => {
37-
accumulator.push_str(&s.as_str());
40+
string_accumulator.push_str(&s.as_str());
41+
string_pos.push(e.span);
3842
}
3943
ast::LitKind::Char(c) => {
40-
accumulator.push(c);
44+
string_accumulator.push(c);
45+
string_pos.push(e.span);
4146
}
4247
ast::LitKind::Int(i, ast::LitIntType::Unsigned(_))
4348
| ast::LitKind::Int(i, ast::LitIntType::Signed(_))
4449
| ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) => {
45-
accumulator.push_str(&i.to_string());
50+
string_accumulator.push_str(&i.to_string());
51+
string_pos.push(e.span);
4652
}
4753
ast::LitKind::Bool(b) => {
48-
accumulator.push_str(&b.to_string());
54+
string_accumulator.push_str(&b.to_string());
55+
string_pos.push(e.span);
4956
}
50-
ast::LitKind::Byte(..) | ast::LitKind::ByteStr(..) => {
51-
cx.span_err(e.span, "cannot concatenate a byte string literal");
57+
ast::LitKind::Byte(byte) => {
58+
b_accumulator.push(byte);
59+
b_pos.push(e.span);
60+
}
61+
ast::LitKind::ByteStr(ref b_str) => {
62+
b_accumulator.extend(b_str.iter());
63+
b_pos.push(e.span);
5264
}
5365
},
5466
_ => {
@@ -61,6 +73,36 @@ pub fn expand_syntax_ext(
6173
err.note("only literals (like `\"foo\"`, `42` and `3.14`) can be passed to `concat!()`");
6274
err.emit();
6375
}
76+
// Do not allow mixing "" and b""
77+
if string_accumulator.len() > 0 && b_accumulator.len() > 0 {
78+
let mut err = cx.struct_span_err(
79+
b_pos.clone(),
80+
"cannot concatenate a byte string literal with string literals",
81+
);
82+
for pos in &b_pos {
83+
err.span_label(*pos, "byte string literal");
84+
}
85+
for pos in &string_pos {
86+
err.span_label(*pos, "string literal");
87+
88+
}
89+
err.help("do not mix byte string literals and string literals");
90+
err.multipart_suggestion(
91+
"you can use byte string literals",
92+
string_pos
93+
.iter()
94+
.map(|pos| (pos.shrink_to_lo(), "b".to_string()))
95+
.collect(),
96+
);
97+
err.emit();
98+
}
6499
let sp = sp.apply_mark(cx.current_expansion.mark);
65-
base::MacEager::expr(cx.expr_str(sp, Symbol::intern(&accumulator)))
100+
if b_accumulator.len() > 0 {
101+
base::MacEager::expr(cx.expr_lit(
102+
sp,
103+
ast::LitKind::new_byte_str(b_accumulator),
104+
))
105+
} else {
106+
base::MacEager::expr(cx.expr_str(sp, Symbol::intern(&string_accumulator)))
107+
}
66108
}

src/test/compile-fail/concat.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
// except according to those terms.
1010

1111
fn main() {
12-
concat!(b'f'); //~ ERROR: cannot concatenate a byte string literal
13-
concat!(b"foo"); //~ ERROR: cannot concatenate a byte string literal
12+
concat!(b'f');
13+
concat!(b"foo");
1414
concat!(foo); //~ ERROR: expected a literal
1515
concat!(foo()); //~ ERROR: expected a literal
1616
}

src/test/ui/byte-concat.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
fn main() {
12+
let _ = concat!(b"abc", b"def");
13+
let _ = concat!("abc", b"def", "ghi", b"jkl");
14+
//~^ ERROR cannot concatenate a byte string literal with string literals
15+
}

src/test/ui/byte-concat.stderr

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
error: cannot concatenate a byte string literal with string literals
2+
--> $DIR/byte-concat.rs:13:28
3+
|
4+
LL | let _ = concat!("abc", b"def", "ghi", b"jkl");
5+
| ----- ^^^^^^ ----- ^^^^^^ byte string literal
6+
| | | |
7+
| | | string literal
8+
| | byte string literal
9+
| string literal
10+
|
11+
= help: do not mix byte string literals and string literals
12+
help: you can use byte string literals
13+
|
14+
LL | let _ = concat!(b"abc", b"def", b"ghi", b"jkl");
15+
| ^ ^
16+
17+
error: aborting due to previous error
18+

0 commit comments

Comments
 (0)