@@ -8,6 +8,7 @@ use intern::Interned;
88use mbe:: { syntax_node_to_token_tree, DelimiterKind , Punct } ;
99use smallvec:: { smallvec, SmallVec } ;
1010use span:: { Span , SyntaxContextId } ;
11+ use syntax:: unescape;
1112use syntax:: { ast, format_smolstr, match_ast, AstNode , AstToken , SmolStr , SyntaxNode } ;
1213use triomphe:: ThinArc ;
1314
@@ -54,8 +55,7 @@ impl RawAttrs {
5455 Attr {
5556 id,
5657 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) ) ) ,
5959 span,
6060 } ) ) ) ,
6161 path : Interned :: new ( ModPath :: from ( crate :: name!( doc) ) ) ,
@@ -74,6 +74,10 @@ impl RawAttrs {
7474 RawAttrs { entries }
7575 }
7676
77+ fn escape_chars ( s : & str ) -> String {
78+ s. replace ( '\\' , r#"\\"# ) . replace ( '"' , r#"\""# )
79+ }
80+
7781 pub fn from_attrs_owner (
7882 db : & dyn ExpandDatabase ,
7983 owner : InFile < & dyn ast:: HasAttrs > ,
@@ -303,9 +307,7 @@ impl Attr {
303307 Some ( it) => {
304308 it. trim_matches ( '#' ) . strip_prefix ( '"' ) ?. strip_suffix ( '"' ) . map ( Cow :: Borrowed )
305309 }
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) ,
309311 } ,
310312 _ => None ,
311313 }
@@ -360,37 +362,31 @@ impl Attr {
360362 }
361363}
362364
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) ;
391381 }
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) ) ,
392389 }
393- Some ( res)
394390}
395391
396392pub fn collect_attrs (
0 commit comments