1 // Copyright 2012-2015 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.
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.
11 #![crate_name = "rustc_save_analysis"]
12 #![unstable(feature = "rustc_private", issue = "27812")]
13 #![crate_type = "dylib"]
14 #![crate_type = "rlib"]
15 #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
16 html_favicon_url
= "https://doc.rust-lang.org/favicon.ico",
17 html_root_url
= "https://doc.rust-lang.org/nightly/")]
18 #![cfg_attr(not(stage0), deny(warnings))]
20 #![feature(custom_attribute)]
21 #![allow(unused_attributes)]
22 #![feature(rustc_private)]
23 #![feature(staged_api)]
25 #[macro_use] extern crate rustc;
27 #[macro_use] extern crate log;
28 #[macro_use] extern crate syntax;
30 use rustc
::hir
::{self, lowering}
;
31 use rustc
::hir
::map
::NodeItem
;
32 use rustc
::hir
::def
::Def
;
33 use rustc
::hir
::def_id
::DefId
;
34 use rustc
::session
::config
::CrateType
::CrateTypeExecutable
;
35 use rustc
::ty
::{self, TyCtxt}
;
38 use std
::fs
::{self, File}
;
39 use std
::path
::{Path, PathBuf}
;
41 use syntax
::ast
::{self, NodeId, PatKind}
;
42 use syntax
::codemap
::*;
43 use syntax
::parse
::token
::{self, keywords}
;
44 use syntax
::visit
::{self, Visitor}
;
45 use syntax
::print
::pprust
::ty_to_string
;
54 pub use self::csv_dumper
::CsvDumper
;
55 pub use self::data
::*;
56 pub use self::dump
::Dump
;
57 pub use self::dump_visitor
::DumpVisitor
;
58 use self::span_utils
::SpanUtils
;
60 // FIXME this is legacy code and should be removed
64 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
73 pub struct SaveContext
<'l
, 'tcx
: 'l
> {
74 tcx
: &'l TyCtxt
<'tcx
>,
75 lcx
: &'l lowering
::LoweringContext
<'l
>,
76 span_utils
: SpanUtils
<'l
>,
79 macro_rules
! option_try(
80 ($e
:expr
) => (match $e { Some(e) => e, None => return None }
)
83 impl<'l
, 'tcx
: 'l
> SaveContext
<'l
, 'tcx
> {
84 pub fn new(tcx
: &'l TyCtxt
<'tcx
>,
85 lcx
: &'l lowering
::LoweringContext
<'l
>)
86 -> SaveContext
<'l
, 'tcx
> {
87 let span_utils
= SpanUtils
::new(&tcx
.sess
);
88 SaveContext
::from_span_utils(tcx
, lcx
, span_utils
)
91 pub fn from_span_utils(tcx
: &'l TyCtxt
<'tcx
>,
92 lcx
: &'l lowering
::LoweringContext
<'l
>,
93 span_utils
: SpanUtils
<'l
>)
94 -> SaveContext
<'l
, 'tcx
> {
98 span_utils
: span_utils
,
102 // List external crates used by the current crate.
103 pub fn get_external_crates(&self) -> Vec
<CrateData
> {
104 let mut result
= Vec
::new();
106 for n
in self.tcx
.sess
.cstore
.crates() {
107 result
.push(CrateData
{
108 name
: (&self.tcx
.sess
.cstore
.crate_name(n
)[..]).to_owned(),
116 pub fn get_item_data(&self, item
: &ast
::Item
) -> Option
<Data
> {
118 ast
::ItemKind
::Fn(..) => {
119 let name
= self.tcx
.node_path_str(item
.id
);
120 let qualname
= format
!("::{}", name
);
121 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Fn
);
122 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
123 Some(Data
::FunctionData(FunctionData
{
128 span
: sub_span
.unwrap(),
129 scope
: self.enclosing_scope(item
.id
),
132 ast
::ItemKind
::Static(ref typ
, mt
, ref expr
) => {
133 let qualname
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
135 // If the variable is immutable, save the initialising expression.
136 let (value
, keyword
) = match mt
{
137 ast
::Mutability
::Mutable
=> (String
::from("<mutable>"), keywords
::Mut
),
138 ast
::Mutability
::Immutable
=> {
139 (self.span_utils
.snippet(expr
.span
), keywords
::Static
)
143 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keyword
);
144 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
145 Some(Data
::VariableData(VariableData
{
147 name
: item
.ident
.to_string(),
149 span
: sub_span
.unwrap(),
150 scope
: self.enclosing_scope(item
.id
),
152 type_value
: ty_to_string(&typ
),
155 ast
::ItemKind
::Const(ref typ
, ref expr
) => {
156 let qualname
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
157 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Const
);
158 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
159 Some(Data
::VariableData(VariableData
{
161 name
: item
.ident
.to_string(),
163 span
: sub_span
.unwrap(),
164 scope
: self.enclosing_scope(item
.id
),
165 value
: self.span_utils
.snippet(expr
.span
),
166 type_value
: ty_to_string(&typ
),
169 ast
::ItemKind
::Mod(ref m
) => {
170 let qualname
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
172 let cm
= self.tcx
.sess
.codemap();
173 let filename
= cm
.span_to_filename(m
.inner
);
175 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Mod
);
176 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
177 Some(Data
::ModData(ModData
{
179 name
: item
.ident
.to_string(),
181 span
: sub_span
.unwrap(),
182 scope
: self.enclosing_scope(item
.id
),
186 ast
::ItemKind
::Enum(..) => {
187 let enum_name
= format
!("::{}", self.tcx
.node_path_str(item
.id
));
188 let val
= self.span_utils
.snippet(item
.span
);
189 let sub_span
= self.span_utils
.sub_span_after_keyword(item
.span
, keywords
::Enum
);
190 filter
!(self.span_utils
, sub_span
, item
.span
, None
);
191 Some(Data
::EnumData(EnumData
{
194 span
: sub_span
.unwrap(),
196 scope
: self.enclosing_scope(item
.id
),
199 ast
::ItemKind
::Impl(_
, _
, _
, ref trait_ref
, ref typ
, _
) => {
200 let mut type_data
= None
;
203 let parent
= self.enclosing_scope(item
.id
);
206 // Common case impl for a struct or something basic.
207 ast
::TyKind
::Path(None
, ref path
) => {
208 sub_span
= self.span_utils
.sub_span_for_type_name(path
.span
);
209 filter
!(self.span_utils
, sub_span
, path
.span
, None
);
210 type_data
= self.lookup_ref_id(typ
.id
).map(|id
| {
212 span
: sub_span
.unwrap(),
215 qualname
: String
::new() // FIXME: generate the real qualname
220 // Less useful case, impl for a compound type.
222 sub_span
= self.span_utils
.sub_span_for_type_name(span
).or(Some(span
));
226 let trait_data
= trait_ref
.as_ref()
227 .and_then(|tr
| self.get_trait_ref_data(tr
, parent
));
229 filter
!(self.span_utils
, sub_span
, typ
.span
, None
);
230 Some(Data
::ImplData(ImplData2
{
232 span
: sub_span
.unwrap(),
234 trait_ref
: trait_data
,
245 pub fn get_field_data(&self, field
: &ast
::StructField
,
246 scope
: NodeId
) -> Option
<VariableData
> {
247 if let Some(ident
) = field
.ident
{
248 let qualname
= format
!("::{}::{}", self.tcx
.node_path_str(scope
), ident
);
249 let typ
= self.tcx
.node_types().get(&field
.id
).unwrap().to_string();
250 let sub_span
= self.span_utils
.sub_span_before_token(field
.span
, token
::Colon
);
251 filter
!(self.span_utils
, sub_span
, field
.span
, None
);
254 name
: ident
.to_string(),
256 span
: sub_span
.unwrap(),
258 value
: "".to_owned(),
266 // FIXME would be nice to take a MethodItem here, but the ast provides both
267 // trait and impl flavours, so the caller must do the disassembly.
268 pub fn get_method_data(&self, id
: ast
::NodeId
,
269 name
: ast
::Name
, span
: Span
) -> Option
<FunctionData
> {
270 // The qualname for a method is the trait name or name of the struct in an impl in
271 // which the method is declared in, followed by the method's name.
272 let qualname
= match self.tcx
.impl_of_method(self.tcx
.map
.local_def_id(id
)) {
273 Some(impl_id
) => match self.tcx
.map
.get_if_local(impl_id
) {
274 Some(NodeItem(item
)) => {
276 hir
::ItemImpl(_
, _
, _
, _
, ref ty
, _
) => {
277 let mut result
= String
::from("<");
278 result
.push_str(&rustc
::hir
::print
::ty_to_string(&ty
));
280 match self.tcx
.trait_of_item(self.tcx
.map
.local_def_id(id
)) {
282 result
.push_str(" as ");
283 result
.push_str(&self.tcx
.item_path_str(def_id
));
287 result
.push_str(">");
292 "Container {:?} for method {} not an impl?",
300 "Container {:?} for method {} is not a node item {:?}",
306 None
=> match self.tcx
.trait_of_item(self.tcx
.map
.local_def_id(id
)) {
308 match self.tcx
.map
.get_if_local(def_id
) {
309 Some(NodeItem(_
)) => {
310 format
!("::{}", self.tcx
.item_path_str(def_id
))
314 "Could not find container {:?} for \
315 method {}, got {:?}",
323 span_bug
!(span
, "Could not find container for method {}", id
);
328 let qualname
= format
!("{}::{}", qualname
, name
);
330 let def_id
= self.tcx
.map
.local_def_id(id
);
331 let decl_id
= self.tcx
.trait_item_of_item(def_id
).and_then(|new_id
| {
332 let new_def_id
= new_id
.def_id();
333 if new_def_id
!= def_id
{
340 let sub_span
= self.span_utils
.sub_span_after_keyword(span
, keywords
::Fn
);
341 filter
!(self.span_utils
, sub_span
, span
, None
);
344 name
: name
.to_string(),
346 declaration
: decl_id
,
347 span
: sub_span
.unwrap(),
348 scope
: self.enclosing_scope(id
),
352 pub fn get_trait_ref_data(&self,
353 trait_ref
: &ast
::TraitRef
,
355 -> Option
<TypeRefData
> {
356 self.lookup_ref_id(trait_ref
.ref_id
).and_then(|def_id
| {
357 let span
= trait_ref
.path
.span
;
358 let sub_span
= self.span_utils
.sub_span_for_type_name(span
).or(Some(span
));
359 filter
!(self.span_utils
, sub_span
, span
, None
);
361 span
: sub_span
.unwrap(),
363 ref_id
: Some(def_id
),
364 qualname
: String
::new() // FIXME: generate the real qualname
369 pub fn get_expr_data(&self, expr
: &ast
::Expr
) -> Option
<Data
> {
370 let hir_node
= lowering
::lower_expr(self.lcx
, expr
);
371 let ty
= self.tcx
.expr_ty_adjusted_opt(&hir_node
);
372 if ty
.is_none() || ty
.unwrap().sty
== ty
::TyError
{
376 ast
::ExprKind
::Field(ref sub_ex
, ident
) => {
377 let hir_node
= lowering
::lower_expr(self.lcx
, sub_ex
);
378 match self.tcx
.expr_ty_adjusted(&hir_node
).sty
{
379 ty
::TyStruct(def
, _
) => {
380 let f
= def
.struct_variant().field_named(ident
.node
.name
);
381 let sub_span
= self.span_utils
.span_for_last_ident(expr
.span
);
382 filter
!(self.span_utils
, sub_span
, expr
.span
, None
);
383 return Some(Data
::VariableRefData(VariableRefData
{
384 name
: ident
.node
.to_string(),
385 span
: sub_span
.unwrap(),
386 scope
: self.enclosing_scope(expr
.id
),
391 debug
!("Expected struct type, found {:?}", ty
);
396 ast
::ExprKind
::Struct(ref path
, _
, _
) => {
397 let hir_node
= lowering
::lower_expr(self.lcx
, expr
);
398 match self.tcx
.expr_ty_adjusted(&hir_node
).sty
{
399 ty
::TyStruct(def
, _
) => {
400 let sub_span
= self.span_utils
.span_for_last_ident(path
.span
);
401 filter
!(self.span_utils
, sub_span
, path
.span
, None
);
402 Some(Data
::TypeRefData(TypeRefData
{
403 span
: sub_span
.unwrap(),
404 scope
: self.enclosing_scope(expr
.id
),
405 ref_id
: Some(def
.did
),
406 qualname
: String
::new() // FIXME: generate the real qualname
410 // FIXME ty could legitimately be a TyEnum, but then we will fail
411 // later if we try to look up the fields.
412 debug
!("expected TyStruct, found {:?}", ty
);
417 ast
::ExprKind
::MethodCall(..) => {
418 let method_call
= ty
::MethodCall
::expr(expr
.id
);
419 let method_id
= self.tcx
.tables
.borrow().method_map
[&method_call
].def_id
;
420 let (def_id
, decl_id
) = match self.tcx
.impl_or_trait_item(method_id
).container() {
421 ty
::ImplContainer(_
) => (Some(method_id
), None
),
422 ty
::TraitContainer(_
) => (None
, Some(method_id
)),
424 let sub_span
= self.span_utils
.sub_span_for_meth_name(expr
.span
);
425 filter
!(self.span_utils
, sub_span
, expr
.span
, None
);
426 let parent
= self.enclosing_scope(expr
.id
);
427 Some(Data
::MethodCallData(MethodCallData
{
428 span
: sub_span
.unwrap(),
434 ast
::ExprKind
::Path(_
, ref path
) => {
435 self.get_path_data(expr
.id
, path
)
444 pub fn get_path_data(&self, id
: NodeId
, path
: &ast
::Path
) -> Option
<Data
> {
445 let def_map
= self.tcx
.def_map
.borrow();
446 if !def_map
.contains_key(&id
) {
447 span_bug
!(path
.span
, "def_map has no key for {} in visit_expr", id
);
449 let def
= def_map
.get(&id
).unwrap().full_def();
450 let sub_span
= self.span_utils
.span_for_last_ident(path
.span
);
451 filter
!(self.span_utils
, sub_span
, path
.span
, None
);
457 Def
::AssociatedConst(..) |
458 Def
::Variant(..) => {
459 Some(Data
::VariableRefData(VariableRefData
{
460 name
: self.span_utils
.snippet(sub_span
.unwrap()),
461 span
: sub_span
.unwrap(),
462 scope
: self.enclosing_scope(id
),
463 ref_id
: def
.def_id(),
466 Def
::Struct(def_id
) |
468 Def
::TyAlias(def_id
) |
470 Def
::TyParam(_
, _
, def_id
, _
) => {
471 Some(Data
::TypeRefData(TypeRefData
{
472 span
: sub_span
.unwrap(),
473 ref_id
: Some(def_id
),
474 scope
: self.enclosing_scope(id
),
475 qualname
: String
::new() // FIXME: generate the real qualname
478 Def
::Method(decl_id
) => {
479 let sub_span
= self.span_utils
.sub_span_for_meth_name(path
.span
);
480 filter
!(self.span_utils
, sub_span
, path
.span
, None
);
481 let def_id
= if decl_id
.is_local() {
482 let ti
= self.tcx
.impl_or_trait_item(decl_id
);
483 match ti
.container() {
484 ty
::TraitContainer(def_id
) => {
488 .find(|mr
| mr
.name() == ti
.name() && self.trait_method_has_body(mr
))
489 .map(|mr
| mr
.def_id())
491 ty
::ImplContainer(def_id
) => {
492 let impl_items
= self.tcx
.impl_items
.borrow();
493 Some(impl_items
.get(&def_id
)
497 self.tcx
.impl_or_trait_item(mr
.def_id()).name() ==
507 Some(Data
::MethodCallData(MethodCallData
{
508 span
: sub_span
.unwrap(),
509 scope
: self.enclosing_scope(id
),
511 decl_id
: Some(decl_id
),
515 Some(Data
::FunctionCallData(FunctionCallData
{
517 span
: sub_span
.unwrap(),
518 scope
: self.enclosing_scope(id
),
521 Def
::Mod(def_id
) => {
522 Some(Data
::ModRefData(ModRefData
{
523 ref_id
: Some(def_id
),
524 span
: sub_span
.unwrap(),
525 scope
: self.enclosing_scope(id
),
526 qualname
: String
::new() // FIXME: generate the real qualname
533 fn trait_method_has_body(&self, mr
: &ty
::ImplOrTraitItem
) -> bool
{
534 let def_id
= mr
.def_id();
535 if let Some(node_id
) = self.tcx
.map
.as_local_node_id(def_id
) {
536 let trait_item
= self.tcx
.map
.expect_trait_item(node_id
);
537 if let hir
::TraitItem_
::MethodTraitItem(_
, Some(_
)) = trait_item
.node
{
547 pub fn get_field_ref_data(&self,
548 field_ref
: &ast
::Field
,
549 variant
: ty
::VariantDef
,
551 -> Option
<VariableRefData
> {
552 let f
= variant
.field_named(field_ref
.ident
.node
.name
);
553 // We don't really need a sub-span here, but no harm done
554 let sub_span
= self.span_utils
.span_for_last_ident(field_ref
.ident
.span
);
555 filter
!(self.span_utils
, sub_span
, field_ref
.ident
.span
, None
);
556 Some(VariableRefData
{
557 name
: field_ref
.ident
.node
.to_string(),
558 span
: sub_span
.unwrap(),
564 /// Attempt to return MacroUseData for any AST node.
566 /// For a given piece of AST defined by the supplied Span and NodeId,
567 /// returns None if the node is not macro-generated or the span is malformed,
568 /// else uses the expansion callsite and callee to return some MacroUseData.
569 pub fn get_macro_use_data(&self, span
: Span
, id
: NodeId
) -> Option
<MacroUseData
> {
570 if !generated_code(span
) {
573 // Note we take care to use the source callsite/callee, to handle
574 // nested expansions and ensure we only generate data for source-visible
576 let callsite
= self.tcx
.sess
.codemap().source_callsite(span
);
577 let callee
= self.tcx
.sess
.codemap().source_callee(span
);
578 let callee
= option_try
!(callee
);
579 let callee_span
= option_try
!(callee
.span
);
581 // Ignore attribute macros, their spans are usually mangled
582 if let MacroAttribute(_
) = callee
.format
{
586 // If the callee is an imported macro from an external crate, need to get
587 // the source span and name from the session, as their spans are localized
588 // when read in, and no longer correspond to the source.
589 if let Some(mac
) = self.tcx
.sess
.imported_macro_spans
.borrow().get(&callee_span
) {
590 let &(ref mac_name
, mac_span
) = mac
;
591 return Some(MacroUseData
{
593 name
: mac_name
.clone(),
594 callee_span
: mac_span
,
595 scope
: self.enclosing_scope(id
),
597 qualname
: String
::new()// FIXME: generate the real qualname
603 name
: callee
.name().to_string(),
604 callee_span
: callee_span
,
605 scope
: self.enclosing_scope(id
),
607 qualname
: String
::new() // FIXME: generate the real qualname
611 pub fn get_data_for_id(&self, _id
: &NodeId
) -> Data
{
616 fn lookup_ref_id(&self, ref_id
: NodeId
) -> Option
<DefId
> {
617 if !self.tcx
.def_map
.borrow().contains_key(&ref_id
) {
618 bug
!("def_map has no key for {} in lookup_type_ref", ref_id
);
620 let def
= self.tcx
.def_map
.borrow().get(&ref_id
).unwrap().full_def();
622 Def
::PrimTy(_
) | Def
::SelfTy(..) => None
,
623 _
=> Some(def
.def_id()),
628 pub fn enclosing_scope(&self, id
: NodeId
) -> NodeId
{
629 self.tcx
.map
.get_enclosing_scope(id
).unwrap_or(0)
633 // An AST visitor for collecting paths from patterns.
634 struct PathCollector
{
635 // The Row field identifies the kind of pattern.
636 collected_paths
: Vec
<(NodeId
, ast
::Path
, ast
::Mutability
, recorder
::Row
)>,
640 fn new() -> PathCollector
{
641 PathCollector { collected_paths: vec![] }
645 impl<'v
> Visitor
<'v
> for PathCollector
{
646 fn visit_pat(&mut self, p
: &ast
::Pat
) {
648 PatKind
::Struct(ref path
, _
, _
) => {
649 self.collected_paths
.push((p
.id
, path
.clone(),
650 ast
::Mutability
::Mutable
, recorder
::TypeRef
));
652 PatKind
::TupleStruct(ref path
, _
) |
653 PatKind
::Path(ref path
) |
654 PatKind
::QPath(_
, ref path
) => {
655 self.collected_paths
.push((p
.id
, path
.clone(),
656 ast
::Mutability
::Mutable
, recorder
::VarRef
));
658 PatKind
::Ident(bm
, ref path1
, _
) => {
659 debug
!("PathCollector, visit ident in pat {}: {:?} {:?}",
663 let immut
= match bm
{
664 // Even if the ref is mut, you can't change the ref, only
665 // the data pointed at, so showing the initialising expression
666 // is still worthwhile.
667 ast
::BindingMode
::ByRef(_
) => ast
::Mutability
::Immutable
,
668 ast
::BindingMode
::ByValue(mt
) => mt
,
670 // collect path for either visit_local or visit_arm
671 let path
= ast
::Path
::from_ident(path1
.span
, path1
.node
);
672 self.collected_paths
.push((p
.id
, path
, immut
, recorder
::VarRef
));
676 visit
::walk_pat(self, p
);
680 pub fn process_crate
<'l
, 'tcx
>(tcx
: &'l TyCtxt
<'tcx
>,
681 lcx
: &'l lowering
::LoweringContext
<'l
>,
683 analysis
: &ty
::CrateAnalysis
,
685 odir
: Option
<&Path
>) {
686 let _ignore
= tcx
.dep_graph
.in_ignore();
688 assert
!(analysis
.glob_map
.is_some());
690 info
!("Dumping crate {}", cratename
);
692 // find a path to dump our data to
693 let mut root_path
= match env
::var_os("DXR_RUST_TEMP_FOLDER") {
694 Some(val
) => PathBuf
::from(val
),
696 Some(val
) => val
.join("dxr"),
697 None
=> PathBuf
::from("dxr-temp"),
701 if let Err(e
) = fs
::create_dir_all(&root_path
) {
702 tcx
.sess
.err(&format
!("Could not create directory {}: {}",
708 let disp
= root_path
.display();
709 info
!("Writing output to {}", disp
);
712 // Create output file.
713 let executable
= tcx
.sess
.crate_types
.borrow().iter().any(|ct
| *ct
== CrateTypeExecutable
);
714 let mut out_name
= if executable
{
719 out_name
.push_str(&cratename
);
720 out_name
.push_str(&tcx
.sess
.opts
.cg
.extra_filename
);
721 out_name
.push_str(".csv");
722 root_path
.push(&out_name
);
723 let mut output_file
= File
::create(&root_path
).unwrap_or_else(|e
| {
724 let disp
= root_path
.display();
725 tcx
.sess
.fatal(&format
!("Could not open {}: {}", disp
, e
));
729 let utils
= SpanUtils
::new(&tcx
.sess
);
730 let mut dumper
= CsvDumper
::new(&mut output_file
, utils
);
731 let mut visitor
= DumpVisitor
::new(tcx
, lcx
, analysis
, &mut dumper
);
732 // FIXME: we don't write anything!
734 visitor
.dump_crate_info(cratename
, krate
);
735 visit
::walk_crate(&mut visitor
, krate
);
738 // Utility functions for the module.
740 // Helper function to escape quotes in a string
741 fn escape(s
: String
) -> String
{
742 s
.replace("\"", "\"\"")
745 // Helper function to determine if a span came from a
746 // macro expansion or syntax extension.
747 pub fn generated_code(span
: Span
) -> bool
{
748 span
.expn_id
!= NO_EXPANSION
|| span
== DUMMY_SP