@@ -8,6 +8,7 @@ use intern::Interned;
8
8
use mbe:: { syntax_node_to_token_tree, DelimiterKind , Punct } ;
9
9
use smallvec:: { smallvec, SmallVec } ;
10
10
use span:: { Span , SyntaxContextId } ;
11
+ use syntax:: unescape;
11
12
use syntax:: { ast, format_smolstr, match_ast, AstNode , AstToken , SmolStr , SyntaxNode } ;
12
13
use triomphe:: ThinArc ;
13
14
@@ -54,8 +55,7 @@ impl RawAttrs {
54
55
Attr {
55
56
id,
56
57
input : Some ( Interned :: new ( AttrInput :: Literal ( tt:: Literal {
57
- // FIXME: Escape quotes from comment content
58
- text : SmolStr :: new ( format_smolstr ! ( "\" {doc}\" " , ) ) ,
58
+ text : SmolStr :: new ( format_smolstr ! ( "\" {}\" " , Self :: escape_chars( doc) ) ) ,
59
59
span,
60
60
} ) ) ) ,
61
61
path : Interned :: new ( ModPath :: from ( crate :: name!( doc) ) ) ,
@@ -74,6 +74,10 @@ impl RawAttrs {
74
74
RawAttrs { entries }
75
75
}
76
76
77
+ fn escape_chars ( s : & str ) -> String {
78
+ s. replace ( '\\' , r#"\\"# ) . replace ( '"' , r#"\""# )
79
+ }
80
+
77
81
pub fn from_attrs_owner (
78
82
db : & dyn ExpandDatabase ,
79
83
owner : InFile < & dyn ast:: HasAttrs > ,
@@ -303,9 +307,7 @@ impl Attr {
303
307
Some ( it) => {
304
308
it. trim_matches ( '#' ) . strip_prefix ( '"' ) ?. strip_suffix ( '"' ) . map ( Cow :: Borrowed )
305
309
}
306
- None => {
307
- it. text . strip_prefix ( '"' ) ?. strip_suffix ( '"' ) . and_then ( unescape) . map ( Cow :: Owned )
308
- }
310
+ None => it. text . strip_prefix ( '"' ) ?. strip_suffix ( '"' ) . and_then ( unescape) ,
309
311
} ,
310
312
_ => None ,
311
313
}
@@ -360,37 +362,31 @@ impl Attr {
360
362
}
361
363
}
362
364
363
- fn unescape ( s : & str ) -> Option < String > {
364
- let mut res = String :: with_capacity ( s. len ( ) ) ;
365
- let mut chars = s. chars ( ) ;
366
-
367
- while let Some ( c) = chars. next ( ) {
368
- if c == '\\' {
369
- match chars. next ( ) ? {
370
- 'n' => res. push ( '\n' ) ,
371
- 'r' => res. push ( '\r' ) ,
372
- 't' => res. push ( '\t' ) ,
373
- '\\' => res. push ( '\\' ) ,
374
- '\'' => res. push ( '\'' ) ,
375
- '"' => res. push ( '"' ) ,
376
- '0' => res. push ( '\0' ) ,
377
- 'x' => {
378
- let hex = chars. by_ref ( ) . take ( 2 ) . collect :: < String > ( ) ;
379
- let c = u8:: from_str_radix ( & hex, 16 ) . ok ( ) ?;
380
- res. push ( c as char ) ;
381
- }
382
- 'u' => {
383
- let hex = chars. by_ref ( ) . take ( 4 ) . collect :: < String > ( ) ;
384
- let c = u32:: from_str_radix ( & hex, 16 ) . ok ( ) ?;
385
- res. push ( char:: from_u32 ( c) ?) ;
386
- }
387
- _ => return None ,
388
- }
389
- } else {
390
- res. push ( c) ;
365
+ fn unescape ( s : & str ) -> Option < Cow < ' _ , str > > {
366
+ let mut buf = String :: new ( ) ;
367
+ let mut prev_end = 0 ;
368
+ let mut has_error = false ;
369
+ unescape:: unescape_unicode ( s, unescape:: Mode :: Str , & mut |char_range, unescaped_char| match (
370
+ unescaped_char,
371
+ buf. capacity ( ) == 0 ,
372
+ ) {
373
+ ( Ok ( c) , false ) => buf. push ( c) ,
374
+ ( Ok ( _) , true ) if char_range. len ( ) == 1 && char_range. start == prev_end => {
375
+ prev_end = char_range. end
376
+ }
377
+ ( Ok ( c) , true ) => {
378
+ buf. reserve_exact ( s. len ( ) ) ;
379
+ buf. push_str ( & s[ ..prev_end] ) ;
380
+ buf. push ( c) ;
391
381
}
382
+ ( Err ( _) , _) => has_error = true ,
383
+ } ) ;
384
+
385
+ match ( has_error, buf. capacity ( ) == 0 ) {
386
+ ( true , _) => None ,
387
+ ( false , false ) => Some ( Cow :: Owned ( buf) ) ,
388
+ ( false , true ) => Some ( Cow :: Borrowed ( s) ) ,
392
389
}
393
- Some ( res)
394
390
}
395
391
396
392
pub fn collect_attrs (
0 commit comments