@@ -28,6 +28,8 @@ pub fn expand_syntax_ext(
28
28
} ;
29
29
let mut string_accumulator = String :: new ( ) ;
30
30
let mut string_pos = vec ! [ ] ;
31
+ let mut int_pos = vec ! [ ] ;
32
+ let mut bool_pos = vec ! [ ] ;
31
33
let mut b_accumulator: Vec < u8 > = vec ! [ ] ;
32
34
let mut b_pos: Vec < Span > = vec ! [ ] ;
33
35
// We don't support mixing things with byte str literals, but do a best effort to fill in a
@@ -42,6 +44,8 @@ pub fn expand_syntax_ext(
42
44
| ast:: LitKind :: FloatUnsuffixed ( ref s) => {
43
45
string_accumulator. push_str ( & s. as_str ( ) ) ;
44
46
string_pos. push ( e. span ) ;
47
+ // If we ever allow `concat!("", b"")`, we should probably add a warn by default
48
+ // lint to this code.
45
49
unified_accumulator. extend ( s. to_string ( ) . into_bytes ( ) ) ;
46
50
}
47
51
ast:: LitKind :: Char ( c) => {
@@ -53,13 +57,19 @@ pub fn expand_syntax_ext(
53
57
| ast:: LitKind :: Int ( i, ast:: LitIntType :: Signed ( _) )
54
58
| ast:: LitKind :: Int ( i, ast:: LitIntType :: Unsuffixed ) => {
55
59
string_accumulator. push_str ( & i. to_string ( ) ) ;
56
- string_pos. push ( e. span ) ;
60
+ int_pos. push ( e. span ) ;
61
+ // If we ever allow `concat!()` mixing byte literals with integers, we need to
62
+ // define the appropriate behavior for it. Consistently considering them as
63
+ // "machine width" would be bug-prone. Taking the smallest possible size for the
64
+ // literal is probably what people _that don't think about it_ would expect, but
65
+ // would be inconsistent. Another option is only to accept the literals if they
66
+ // would fit in a `u8`.
57
67
unified_accumulator. extend ( i. to_bytes ( ) . iter ( ) ) ;
58
68
}
59
69
ast:: LitKind :: Bool ( b) => {
60
70
string_accumulator. push_str ( & b. to_string ( ) ) ;
61
- string_pos . push ( e. span ) ;
62
- unified_accumulator . push ( b as u8 ) ;
71
+ bool_pos . push ( e. span ) ;
72
+ // would `concat!(true, b"asdf")` ever make sense?
63
73
}
64
74
ast:: LitKind :: Byte ( byte) => {
65
75
b_accumulator. push ( byte) ;
@@ -71,8 +81,10 @@ pub fn expand_syntax_ext(
71
81
b_pos. push ( e. span ) ;
72
82
unified_accumulator. extend ( b_str. iter ( ) ) ;
73
83
}
74
- } ,
84
+ }
75
85
_ => {
86
+ // Consider the possibility of allowing `concat!(b"asdf", [1, 2, 3, 4])`, given
87
+ // that every single element of the array is a valid `u8`.
76
88
missing_literal. push ( e. span ) ;
77
89
}
78
90
}
@@ -84,26 +96,54 @@ pub fn expand_syntax_ext(
84
96
}
85
97
let sp = sp. apply_mark ( cx. current_expansion . mark ) ;
86
98
// Do not allow mixing "" and b"", but return the joint b"" to avoid further errors
87
- if string_accumulator. len ( ) > 0 && b_accumulator. len ( ) > 0 {
99
+ if b_pos. len ( ) > 0 && ( string_pos. len ( ) > 0 || int_pos. len ( ) > 0 || bool_pos. len ( ) > 0 ) {
100
+ let mut mixings = vec ! [ ] ;
101
+ if string_pos. len ( ) > 0 {
102
+ mixings. push ( "string" ) ;
103
+ }
104
+ if int_pos. len ( ) > 0 {
105
+ mixings. push ( "numeric" ) ;
106
+ }
107
+ if bool_pos. len ( ) > 0 {
108
+ mixings. push ( "boolean" ) ;
109
+ }
88
110
let mut err = cx. struct_span_err (
89
111
b_pos. clone ( ) ,
90
- "cannot concatenate a byte string literal with string literals" ,
112
+ "cannot concatenate a byte string literal with other literals" ,
91
113
) ;
114
+ if mixings. len ( ) > 0 && ( int_pos. len ( ) > 0 || bool_pos. len ( ) > 0 ) {
115
+ let msg = if mixings. len ( ) >= 2 {
116
+ format ! (
117
+ "{} or {}" ,
118
+ mixings[ 0 ..mixings. len( ) - 1 ] . join( ", " ) ,
119
+ mixings. last( ) . unwrap( ) ,
120
+ )
121
+ } else {
122
+ mixings[ 0 ] . to_string ( )
123
+ } ;
124
+ err. note ( & format ! ( "we don't support mixing {} literals and byte strings" , msg) ) ;
125
+ }
126
+ if string_pos. len ( ) > 0 && int_pos. len ( ) == 0 && bool_pos. len ( ) == 0 {
127
+ err. multipart_suggestion (
128
+ "we don't support mixing string and byte string literals, use only byte strings" ,
129
+ string_pos
130
+ . iter ( )
131
+ . map ( |pos| ( pos. shrink_to_lo ( ) , "b" . to_string ( ) ) )
132
+ . collect ( ) ,
133
+ ) ;
134
+ }
92
135
for pos in & b_pos {
93
136
err. span_label ( * pos, "byte string literal" ) ;
94
137
}
95
138
for pos in & string_pos {
96
139
err. span_label ( * pos, "string literal" ) ;
97
-
98
140
}
99
- err. help ( "do not mix byte string literals and string literals" ) ;
100
- err. multipart_suggestion (
101
- "you can use byte string literals" ,
102
- string_pos
103
- . iter ( )
104
- . map ( |pos| ( pos. shrink_to_lo ( ) , "b" . to_string ( ) ) )
105
- . collect ( ) ,
106
- ) ;
141
+ for pos in & int_pos {
142
+ err. span_label ( * pos, "numeric literal" ) ;
143
+ }
144
+ for pos in & bool_pos {
145
+ err. span_label ( * pos, "boolean literal" ) ;
146
+ }
107
147
err. emit ( ) ;
108
148
base:: MacEager :: expr ( cx. expr_byte_str ( sp, unified_accumulator) )
109
149
} else if b_accumulator. len ( ) > 0 {
0 commit comments