1
1
//! A higher level attributes based on TokenTree, with also some shortcuts.
2
- use std:: { fmt, ops} ;
2
+ use std:: { borrow :: Cow , fmt, ops} ;
3
3
4
4
use base_db:: CrateId ;
5
5
use cfg:: CfgExpr ;
@@ -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 > ,
@@ -297,6 +301,18 @@ impl Attr {
297
301
}
298
302
}
299
303
304
+ pub fn string_value_unescape ( & self ) -> Option < Cow < ' _ , str > > {
305
+ match self . input . as_deref ( ) ? {
306
+ AttrInput :: Literal ( it) => match it. text . strip_prefix ( 'r' ) {
307
+ Some ( it) => {
308
+ it. trim_matches ( '#' ) . strip_prefix ( '"' ) ?. strip_suffix ( '"' ) . map ( Cow :: Borrowed )
309
+ }
310
+ None => it. text . strip_prefix ( '"' ) ?. strip_suffix ( '"' ) . and_then ( unescape) ,
311
+ } ,
312
+ _ => None ,
313
+ }
314
+ }
315
+
300
316
/// #[path(ident)]
301
317
pub fn single_ident_value ( & self ) -> Option < & tt:: Ident > {
302
318
match self . input . as_deref ( ) ? {
@@ -346,6 +362,33 @@ impl Attr {
346
362
}
347
363
}
348
364
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) ;
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) ) ,
389
+ }
390
+ }
391
+
349
392
pub fn collect_attrs (
350
393
owner : & dyn ast:: HasAttrs ,
351
394
) -> impl Iterator < Item = ( AttrId , Either < ast:: Attr , ast:: Comment > ) > {
0 commit comments