rust/src/librustc_save_analysis/lib.rs

1119 lines
44 KiB
Rust
Raw Normal View History

#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
2019-02-10 01:13:30 -06:00
#![feature(nll)]
2019-12-22 16:42:04 -06:00
#![recursion_limit = "256"]
mod dump_visitor;
2019-12-22 16:42:04 -06:00
mod dumper;
#[macro_use]
mod span_utils;
mod sig;
use rustc::middle::cstore::ExternCrate;
2019-12-22 16:42:04 -06:00
use rustc::middle::privacy::AccessLevels;
use rustc::session::config::{CrateType, Input, OutputType};
use rustc::ty::{self, DefIdTree, TyCtxt};
2019-02-06 10:02:00 -06:00
use rustc::{bug, span_bug};
use rustc_ast_pretty::pprust::{self, param_to_string, ty_to_string};
use rustc_codegen_utils::link::{filename_for_metadata, out_filename};
use rustc_hir as hir;
use rustc_hir::def::{CtorOf, DefKind as HirDefKind, Res};
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_hir::Node;
use rustc_span::source_map::Spanned;
use rustc_span::*;
use syntax::ast::{self, Attribute, NodeId, PatKind, DUMMY_NODE_ID};
use syntax::util::comments::strip_doc_comment_decoration;
use syntax::visit::{self, Visitor};
2018-02-02 01:29:59 -06:00
use std::cell::Cell;
use std::default::Default;
use std::env;
use std::fs::File;
use std::io::BufWriter;
use std::path::{Path, PathBuf};
2015-07-08 19:23:29 -05:00
use dump_visitor::DumpVisitor;
use span_utils::SpanUtils;
use rls_data::config::Config;
2019-12-22 16:42:04 -06:00
use rls_data::{
Analysis, Def, DefKind, ExternalCrateData, GlobalCrateId, Impl, ImplKind, MacroRef, Ref,
RefKind, Relation, RelationKind, SpanData,
};
2018-09-24 09:38:54 -05:00
2019-02-06 10:02:00 -06:00
use log::{debug, error, info};
pub struct SaveContext<'l, 'tcx> {
2019-06-13 16:48:52 -05:00
tcx: TyCtxt<'tcx>,
2017-01-25 15:24:00 -06:00
tables: &'l ty::TypeckTables<'tcx>,
/// Used as a fallback when nesting the typeck tables during item processing
/// (if these are not available for that item, e.g. don't own a body)
empty_tables: &'l ty::TypeckTables<'tcx>,
access_levels: &'l AccessLevels,
2016-04-17 17:30:55 -05:00
span_utils: SpanUtils<'tcx>,
config: Config,
2018-02-02 01:29:59 -06:00
impl_counter: Cell<u32>,
}
#[derive(Debug)]
pub enum Data {
RefData(Ref),
DefData(Def),
2018-02-02 01:29:59 -06:00
RelationData(Relation, Impl),
}
impl<'l, 'tcx> SaveContext<'l, 'tcx> {
fn span_from_span(&self, span: Span) -> SpanData {
2017-11-07 15:43:05 -06:00
use rls_span::{Column, Row};
2018-08-18 05:14:09 -05:00
let cm = self.tcx.sess.source_map();
2017-07-31 15:04:34 -05:00
let start = cm.lookup_char_pos(span.lo());
let end = cm.lookup_char_pos(span.hi());
SpanData {
file_name: start.file.name.to_string().into(),
2017-07-31 15:04:34 -05:00
byte_start: span.lo().0,
byte_end: span.hi().0,
line_start: Row::new_one_indexed(start.line as u32),
line_end: Row::new_one_indexed(end.line as u32),
column_start: Column::new_one_indexed(start.col.0 as u32 + 1),
column_end: Column::new_one_indexed(end.col.0 as u32 + 1),
}
}
// Returns path to the compilation output (e.g., libfoo-12345678.rmeta)
pub fn compilation_output(&self, crate_name: &str) -> PathBuf {
let sess = &self.tcx.sess;
// Save-analysis is emitted per whole session, not per each crate type
let crate_type = sess.crate_types.borrow()[0];
let outputs = &*self.tcx.output_filenames(LOCAL_CRATE);
if outputs.outputs.contains_key(&OutputType::Metadata) {
filename_for_metadata(sess, crate_name, outputs)
} else if outputs.outputs.should_codegen() {
out_filename(sess, crate_type, outputs, crate_name)
} else {
// Otherwise it's only a DepInfo, in which case we return early and
// not even reach the analysis stage.
unreachable!()
}
}
// List external crates used by the current crate.
pub fn get_external_crates(&self) -> Vec<ExternalCrateData> {
let mut result = Vec::with_capacity(self.tcx.crates().len());
for &n in self.tcx.crates().iter() {
let span = match self.tcx.extern_crate(n.as_def_id()) {
Some(&ExternCrate { span, .. }) => span,
2016-04-25 03:54:00 -05:00
None => {
debug!("skipping crate {}, no data", n);
2016-04-25 03:54:00 -05:00
continue;
}
};
2018-08-18 05:14:09 -05:00
let lo_loc = self.span_utils.sess.source_map().lookup_char_pos(span.lo());
result.push(ExternalCrateData {
// FIXME: change file_name field to PathBuf in rls-data
// https://github.com/nrc/rls-data/issues/7
file_name: self.span_utils.make_filename_string(&lo_loc.file),
num: n.as_u32(),
id: GlobalCrateId {
name: self.tcx.crate_name(n).to_string(),
2017-11-07 15:43:05 -06:00
disambiguator: self.tcx.crate_disambiguator(n).to_fingerprint().as_value(),
},
2015-09-27 15:20:49 -05:00
});
}
result
}
2015-05-05 02:27:44 -05:00
2017-03-07 23:04:30 -06:00
pub fn get_extern_item_data(&self, item: &ast::ForeignItem) -> Option<Data> {
2019-12-22 16:42:04 -06:00
let qualname = format!(
"::{}",
self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id))
);
match item.kind {
ast::ForeignItemKind::Fn(ref sig, ref generics, _) => {
filter!(self.span_utils, item.ident.span);
Some(Data::DefData(Def {
kind: DefKind::ForeignFunction,
id: id_from_node_id(item.id, self),
span: self.span_from_span(item.ident.span),
2017-03-07 23:04:30 -06:00
name: item.ident.to_string(),
qualname,
value: make_signature(&sig.decl, generics),
2017-03-07 23:04:30 -06:00
parent: None,
children: vec![],
decl_id: None,
docs: self.docs_for_attrs(&item.attrs),
sig: sig::foreign_item_signature(item, self),
attributes: lower_attributes(item.attrs.clone(), self),
2017-03-07 23:04:30 -06:00
}))
}
2020-02-14 19:23:10 -06:00
ast::ForeignItemKind::Const(ref ty, _) | ast::ForeignItemKind::Static(ref ty, _, _) => {
filter!(self.span_utils, item.ident.span);
2019-02-06 10:02:00 -06:00
let id = id_from_node_id(item.id, self);
let span = self.span_from_span(item.ident.span);
Some(Data::DefData(Def {
kind: DefKind::ForeignStatic,
id,
span,
2017-03-07 23:04:30 -06:00
name: item.ident.to_string(),
qualname,
value: ty_to_string(ty),
2017-03-07 23:04:30 -06:00
parent: None,
children: vec![],
decl_id: None,
docs: self.docs_for_attrs(&item.attrs),
sig: sig::foreign_item_signature(item, self),
attributes: lower_attributes(item.attrs.clone(), self),
2017-03-07 23:04:30 -06:00
}))
}
2017-09-03 13:53:58 -05:00
// FIXME(plietar): needs a new DefKind in rls-data
ast::ForeignItemKind::TyAlias(..) => None,
ast::ForeignItemKind::Macro(..) => None,
2017-03-07 23:04:30 -06:00
}
}
pub fn get_item_data(&self, item: &ast::Item) -> Option<Data> {
2019-09-26 11:51:36 -05:00
match item.kind {
2019-11-07 06:33:37 -06:00
ast::ItemKind::Fn(ref sig, .., ref generics, _) => {
2019-12-22 16:42:04 -06:00
let qualname = format!(
"::{}",
self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id))
);
filter!(self.span_utils, item.ident.span);
Some(Data::DefData(Def {
kind: DefKind::Function,
id: id_from_node_id(item.id, self),
span: self.span_from_span(item.ident.span),
name: item.ident.to_string(),
qualname,
2019-11-07 06:33:37 -06:00
value: make_signature(&sig.decl, generics),
parent: None,
children: vec![],
decl_id: None,
docs: self.docs_for_attrs(&item.attrs),
sig: sig::item_signature(item, self),
attributes: lower_attributes(item.attrs.clone(), self),
}))
2015-05-05 02:27:44 -05:00
}
ast::ItemKind::Static(ref typ, ..) => {
2019-12-22 16:42:04 -06:00
let qualname = format!(
"::{}",
self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id))
);
filter!(self.span_utils, item.ident.span);
let id = id_from_node_id(item.id, self);
let span = self.span_from_span(item.ident.span);
Some(Data::DefData(Def {
kind: DefKind::Static,
id,
span,
name: item.ident.to_string(),
qualname,
value: ty_to_string(&typ),
parent: None,
children: vec![],
decl_id: None,
docs: self.docs_for_attrs(&item.attrs),
sig: sig::item_signature(item, self),
attributes: lower_attributes(item.attrs.clone(), self),
}))
}
ast::ItemKind::Const(ref typ, _) => {
2019-12-22 16:42:04 -06:00
let qualname = format!(
"::{}",
self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id))
);
filter!(self.span_utils, item.ident.span);
let id = id_from_node_id(item.id, self);
let span = self.span_from_span(item.ident.span);
Some(Data::DefData(Def {
kind: DefKind::Const,
id,
span,
name: item.ident.to_string(),
qualname,
value: ty_to_string(typ),
parent: None,
children: vec![],
decl_id: None,
docs: self.docs_for_attrs(&item.attrs),
sig: sig::item_signature(item, self),
attributes: lower_attributes(item.attrs.clone(), self),
}))
}
ast::ItemKind::Mod(ref m) => {
2019-12-22 16:42:04 -06:00
let qualname = format!(
"::{}",
self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id))
);
2018-08-18 05:14:09 -05:00
let cm = self.tcx.sess.source_map();
let filename = cm.span_to_filename(m.inner);
filter!(self.span_utils, item.ident.span);
Some(Data::DefData(Def {
kind: DefKind::Mod,
id: id_from_node_id(item.id, self),
name: item.ident.to_string(),
qualname,
span: self.span_from_span(item.ident.span),
value: filename.to_string(),
parent: None,
2019-12-22 16:42:04 -06:00
children: m.items.iter().map(|i| id_from_node_id(i.id, self)).collect(),
decl_id: None,
docs: self.docs_for_attrs(&item.attrs),
sig: sig::item_signature(item, self),
attributes: lower_attributes(item.attrs.clone(), self),
}))
2015-09-01 22:37:07 -05:00
}
ast::ItemKind::Enum(ref def, _) => {
let name = item.ident.to_string();
2019-12-22 16:42:04 -06:00
let qualname = format!(
"::{}",
self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(item.id))
);
filter!(self.span_utils, item.ident.span);
2019-12-22 16:42:04 -06:00
let variants_str =
def.variants.iter().map(|v| v.ident.to_string()).collect::<Vec<_>>().join(", ");
let value = format!("{}::{{{}}}", name, variants_str);
Some(Data::DefData(Def {
kind: DefKind::Enum,
id: id_from_node_id(item.id, self),
span: self.span_from_span(item.ident.span),
name,
qualname,
value,
parent: None,
2019-12-22 16:42:04 -06:00
children: def.variants.iter().map(|v| id_from_node_id(v.id, self)).collect(),
decl_id: None,
docs: self.docs_for_attrs(&item.attrs),
sig: sig::item_signature(item, self),
attributes: lower_attributes(item.attrs.clone(), self),
}))
2015-09-01 22:37:07 -05:00
}
ast::ItemKind::Impl { ref of_trait, ref self_ty, ref items, .. } => {
if let ast::TyKind::Path(None, ref path) = self_ty.kind {
2015-06-05 00:50:04 -05:00
// Common case impl for a struct or something basic.
if generated_code(path.span) {
return None;
2015-06-05 00:50:04 -05:00
}
let sub_span = path.segments.last().unwrap().ident.span;
filter!(self.span_utils, sub_span);
2018-02-02 01:29:59 -06:00
let impl_id = self.next_impl_id();
let span = self.span_from_span(sub_span);
2018-02-02 01:29:59 -06:00
let type_data = self.lookup_def_id(self_ty.id);
2017-11-07 15:43:05 -06:00
type_data.map(|type_data| {
2019-12-22 16:42:04 -06:00
Data::RelationData(
Relation {
kind: RelationKind::Impl { id: impl_id },
span: span.clone(),
from: id_from_def_id(type_data),
to: of_trait
2019-12-22 16:42:04 -06:00
.as_ref()
.and_then(|t| self.lookup_def_id(t.ref_id))
.map(id_from_def_id)
.unwrap_or_else(|| null_id()),
2018-02-02 01:29:59 -06:00
},
2019-12-22 16:42:04 -06:00
Impl {
id: impl_id,
kind: match *of_trait {
2019-12-22 16:42:04 -06:00
Some(_) => ImplKind::Direct,
None => ImplKind::Inherent,
},
span: span,
value: String::new(),
parent: None,
children: items
2019-12-22 16:42:04 -06:00
.iter()
.map(|i| id_from_node_id(i.id, self))
.collect(),
docs: String::new(),
sig: None,
attributes: vec![],
2018-02-02 01:29:59 -06:00
},
2019-12-22 16:42:04 -06:00
)
2017-11-07 15:43:05 -06:00
})
} else {
None
2015-06-05 00:50:04 -05:00
}
}
_ => {
// FIXME
bug!();
}
}
}
2017-11-07 15:43:05 -06:00
pub fn get_field_data(&self, field: &ast::StructField, scope: NodeId) -> Option<Def> {
2016-04-06 03:19:10 -05:00
if let Some(ident) = field.ident {
2016-11-21 13:12:02 -06:00
let name = ident.to_string();
2019-12-22 16:42:04 -06:00
let qualname = format!(
"::{}::{}",
self.tcx.def_path_str(self.tcx.hir().local_def_id_from_node_id(scope)),
2019-12-22 16:42:04 -06:00
ident
);
filter!(self.span_utils, ident.span);
let def_id = self.tcx.hir().local_def_id_from_node_id(field.id);
let typ = self.tcx.type_of(def_id).to_string();
2016-11-21 13:12:02 -06:00
let id = id_from_node_id(field.id, self);
let span = self.span_from_span(ident.span);
Some(Def {
kind: DefKind::Field,
id,
span,
name,
qualname,
value: typ,
parent: Some(id_from_node_id(scope, self)),
children: vec![],
decl_id: None,
docs: self.docs_for_attrs(&field.attrs),
sig: sig::field_signature(field, self),
attributes: lower_attributes(field.attrs.clone(), self),
2016-04-02 08:47:53 -05:00
})
} else {
None
2015-06-04 23:03:48 -05:00
}
}
2015-07-08 19:23:29 -05:00
// FIXME would be nice to take a MethodItem here, but the ast provides both
// trait and impl flavours, so the caller must do the disassembly.
pub fn get_method_data(&self, id: ast::NodeId, ident: ast::Ident, span: Span) -> Option<Def> {
2015-07-08 19:23:29 -05:00
// The qualname for a method is the trait name or name of the struct in an impl in
// which the method is declared in, followed by the method's name.
2019-12-22 16:42:04 -06:00
let (qualname, parent_scope, decl_id, docs, attributes) = match self
.tcx
.impl_of_method(self.tcx.hir().local_def_id_from_node_id(id))
{
Some(impl_id) => match self.tcx.hir().get_if_local(impl_id) {
Some(Node::Item(item)) => match item.kind {
hir::ItemKind::Impl { ref self_ty, .. } => {
2019-12-22 16:42:04 -06:00
let mut qualname = String::from("<");
qualname.push_str(&self.tcx.hir().hir_to_pretty_string(self_ty.hir_id));
2019-12-22 16:42:04 -06:00
let trait_id = self.tcx.trait_id_of_impl(impl_id);
let mut docs = String::new();
let mut attrs = vec![];
2019-06-24 02:55:11 -05:00
let hir_id = self.tcx.hir().node_to_hir_id(id);
2019-12-22 16:42:04 -06:00
if let Some(Node::ImplItem(item)) = self.tcx.hir().find(hir_id) {
docs = self.docs_for_attrs(&item.attrs);
attrs = item.attrs.to_vec();
}
let mut decl_id = None;
2019-12-22 16:42:04 -06:00
if let Some(def_id) = trait_id {
// A method in a trait impl.
qualname.push_str(" as ");
qualname.push_str(&self.tcx.def_path_str(def_id));
decl_id = self
.tcx
2019-12-22 16:42:04 -06:00
.associated_items(def_id)
.filter_by_name_unhygienic(ident.name)
.next()
.map(|item| item.def_id);
2019-12-22 16:42:04 -06:00
}
qualname.push_str(">");
(qualname, trait_id, decl_id, docs, attrs)
}
2019-12-22 16:42:04 -06:00
_ => {
span_bug!(span, "Container {:?} for method {} not an impl?", impl_id, id);
2015-07-08 19:23:29 -05:00
}
2017-11-07 15:43:05 -06:00
},
2019-12-22 16:42:04 -06:00
r => {
span_bug!(
span,
"Container {:?} for method {} is not a node item {:?}",
impl_id,
id,
r
);
}
},
None => match self.tcx.trait_of_item(self.tcx.hir().local_def_id_from_node_id(id)) {
Some(def_id) => {
let mut docs = String::new();
let mut attrs = vec![];
let hir_id = self.tcx.hir().node_to_hir_id(id);
if let Some(Node::TraitItem(item)) = self.tcx.hir().find(hir_id) {
docs = self.docs_for_attrs(&item.attrs);
attrs = item.attrs.to_vec();
}
(
format!("::{}", self.tcx.def_path_str(def_id)),
Some(def_id),
None,
docs,
attrs,
)
}
None => {
debug!("could not find container for method {} at {:?}", id, span);
// This is not necessarily a bug, if there was a compilation error,
// the tables we need might not exist.
return None;
}
},
};
2015-07-08 19:23:29 -05:00
let qualname = format!("{}::{}", qualname, ident.name);
2015-07-08 19:23:29 -05:00
filter!(self.span_utils, ident.span);
Some(Def {
kind: DefKind::Method,
id: id_from_node_id(id, self),
span: self.span_from_span(ident.span),
name: ident.name.to_string(),
qualname,
// FIXME you get better data here by using the visitor.
value: String::new(),
parent: parent_scope.map(|id| id_from_def_id(id)),
children: vec![],
decl_id: decl_id.map(|id| id_from_def_id(id)),
docs,
sig: None,
attributes: lower_attributes(attributes, self),
})
2015-07-08 19:23:29 -05:00
}
2017-11-07 15:43:05 -06:00
pub fn get_trait_ref_data(&self, trait_ref: &ast::TraitRef) -> Option<Ref> {
self.lookup_def_id(trait_ref.ref_id).and_then(|def_id| {
let span = trait_ref.path.span;
if generated_code(span) {
return None;
}
let sub_span = trait_ref.path.segments.last().unwrap().ident.span;
filter!(self.span_utils, sub_span);
let span = self.span_from_span(sub_span);
2019-12-22 16:42:04 -06:00
Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id) })
2015-06-05 00:50:04 -05:00
})
}
2015-06-14 17:06:01 -05:00
pub fn get_expr_data(&self, expr: &ast::Expr) -> Option<Data> {
let expr_hir_id = self.tcx.hir().node_to_hir_id(expr.id);
let hir_node = self.tcx.hir().expect_expr(expr_hir_id);
let ty = self.tables.expr_ty_adjusted_opt(&hir_node);
2019-09-16 13:08:35 -05:00
if ty.is_none() || ty.unwrap().kind == ty::Error {
return None;
}
match expr.kind {
ast::ExprKind::Field(ref sub_ex, ident) => {
2019-06-24 02:55:11 -05:00
let sub_ex_hir_id = self.tcx.hir().node_to_hir_id(sub_ex.id);
2019-06-24 02:58:49 -05:00
let hir_node = match self.tcx.hir().find(sub_ex_hir_id) {
2018-08-25 09:56:16 -05:00
Some(Node::Expr(expr)) => expr,
_ => {
2017-11-07 15:43:05 -06:00
debug!(
"Missing or weird node for sub-expression {} in {:?}",
2019-12-22 16:42:04 -06:00
sub_ex.id, expr
2017-11-07 15:43:05 -06:00
);
return None;
}
};
2019-09-16 13:08:35 -05:00
match self.tables.expr_ty_adjusted(&hir_node).kind {
ty::Adt(def, _) if !def.is_enum() => {
let variant = &def.non_enum_variant();
let index = self.tcx.find_field_index(ident, variant).unwrap();
filter!(self.span_utils, ident.span);
let span = self.span_from_span(ident.span);
return Some(Data::RefData(Ref {
kind: RefKind::Variable,
span,
ref_id: id_from_def_id(variant.fields[index].did),
}));
}
ty::Tuple(..) => None,
2015-06-14 17:06:01 -05:00
_ => {
debug!("expected struct or union type, found {:?}", ty);
2015-06-14 17:06:01 -05:00
None
}
}
}
2016-08-26 11:23:42 -05:00
ast::ExprKind::Struct(ref path, ..) => {
2019-09-16 13:08:35 -05:00
match self.tables.expr_ty_adjusted(&hir_node).kind {
ty::Adt(def, _) if !def.is_enum() => {
let sub_span = path.segments.last().unwrap().ident.span;
filter!(self.span_utils, sub_span);
let span = self.span_from_span(sub_span);
Some(Data::RefData(Ref {
kind: RefKind::Type,
span,
ref_id: id_from_def_id(def.did),
2015-06-14 17:06:01 -05:00
}))
2015-06-08 16:51:54 -05:00
}
_ => {
// FIXME ty could legitimately be an enum, but then we will fail
2015-06-14 17:06:01 -05:00
// later if we try to look up the fields.
debug!("expected struct or union, found {:?}", ty);
2015-06-14 17:06:01 -05:00
None
2015-06-08 16:51:54 -05:00
}
}
}
ast::ExprKind::MethodCall(ref seg, ..) => {
let expr_hir_id = self.tcx.hir().definitions().node_to_hir_id(expr.id);
let method_id = match self.tables.type_dependent_def_id(expr_hir_id) {
Some(id) => id,
None => {
debug!("could not resolve method id for {:?}", expr);
return None;
}
};
let (def_id, decl_id) = match self.tcx.associated_item(method_id).container {
2015-07-06 18:42:43 -05:00
ty::ImplContainer(_) => (Some(method_id), None),
2015-09-01 22:37:07 -05:00
ty::TraitContainer(_) => (None, Some(method_id)),
2015-07-06 18:42:43 -05:00
};
2018-03-18 19:54:56 -05:00
let sub_span = seg.ident.span;
filter!(self.span_utils, sub_span);
let span = self.span_from_span(sub_span);
Some(Data::RefData(Ref {
kind: RefKind::Function,
span,
2017-11-07 15:43:05 -06:00
ref_id: def_id
.or(decl_id)
.map(|id| id_from_def_id(id))
.unwrap_or_else(|| null_id()),
2015-07-06 18:42:43 -05:00
}))
}
ast::ExprKind::Path(_, ref path) => {
self.get_path_data(expr.id, path).map(|d| Data::RefData(d))
2015-07-07 21:30:18 -05:00
}
2015-05-05 02:27:44 -05:00
_ => {
// FIXME
bug!();
2015-05-05 02:27:44 -05:00
}
}
}
pub fn get_path_res(&self, id: NodeId) -> Res {
let hir_id = self.tcx.hir().node_to_hir_id(id);
2019-06-20 03:39:19 -05:00
match self.tcx.hir().get(hir_id) {
Node::TraitRef(tr) => tr.path.res,
2019-12-22 16:42:04 -06:00
Node::Item(&hir::Item { kind: hir::ItemKind::Use(path, _), .. }) => path.res,
Node::Visibility(&Spanned {
node: hir::VisibilityKind::Restricted { ref path, .. },
2017-11-07 15:43:05 -06:00
..
2019-11-28 12:28:50 -06:00
}) => path.res,
2019-12-22 16:42:04 -06:00
Node::PathSegment(seg) => match seg.res {
Some(res) if res != Res::Err => res,
_ => {
let parent_node = self.tcx.hir().get_parent_node(hir_id);
self.get_path_res(self.tcx.hir().hir_to_node_id(parent_node))
save-analysis: Get path def from parent in case there's no def for the path itself. This fixes #57462. The relevant part from the hir type collector is: ``` DEBUG 2019-01-09T15:42:58Z: rustc::hir::map::collector: hir_map: NodeId(32) => Entry { parent: NodeId(33), dep_node: 4294967040, node: Expr(expr(32: <Foo>::new)) } DEBUG 2019-01-09T15:42:58Z: rustc::hir::map::collector: hir_map: NodeId(48) => Entry { parent: NodeId(32), dep_node: 4294967040, node: Ty(type(Foo)) } DEBUG 2019-01-09T15:42:58Z: rustc::hir::map::collector: hir_map: NodeId(30) => Entry { parent: NodeId(48), dep_node: 4294967040, node: PathSegment(PathSegment { ident: Foo#0, id: Some(NodeId(30)), def: Some(Err), args: None, infer_types: true }) } DEBUG 2019-01-09T15:42:58Z: rustc::hir::map::collector: hir_map: NodeId(31) => Entry { parent: NodeId(32), dep_node: 4294967040, node: PathSegment(PathSegment { ident: new#0, id: Some(NodeId(31)), def: Some(Err), args: None, infer_types: true }) } ``` We have the right ID when looking for NodeId(31) and try with NodeId(32) (which is the right thing to look for) from get_path_data, but not for the segments that we write from `write_sub_paths_truncated`. Basically `process_path` takes an id which is always the parent, and that we fall back to in `get_path_data()`, so we get the right result for the last path segment, but not for the other segments that get written to from `write_sub_paths_truncated`. I think we can stop passing the explicit id around to `get_path_data` now, will consider sending that as a followup.
2019-01-09 10:31:16 -06:00
}
2019-12-22 16:42:04 -06:00
},
2019-12-22 16:42:04 -06:00
Node::Expr(&hir::Expr { kind: hir::ExprKind::Struct(ref qpath, ..), .. }) => {
self.tables.qpath_res(qpath, hir_id)
}
2019-12-22 16:42:04 -06:00
Node::Expr(&hir::Expr { kind: hir::ExprKind::Path(ref qpath), .. })
| Node::Pat(&hir::Pat { kind: hir::PatKind::Path(ref qpath), .. })
| Node::Pat(&hir::Pat { kind: hir::PatKind::Struct(ref qpath, ..), .. })
| Node::Pat(&hir::Pat { kind: hir::PatKind::TupleStruct(ref qpath, ..), .. })
| Node::Ty(&hir::Ty { kind: hir::TyKind::Path(ref qpath), .. }) => {
self.tables.qpath_res(qpath, hir_id)
}
2018-08-25 09:56:16 -05:00
Node::Binding(&hir::Pat {
2019-12-22 16:42:04 -06:00
kind: hir::PatKind::Binding(_, canonical_id, ..), ..
}) => Res::Local(canonical_id),
_ => Res::Err,
}
}
pub fn get_path_data(&self, id: NodeId, path: &ast::Path) -> Option<Ref> {
2019-12-22 16:42:04 -06:00
path.segments.last().and_then(|seg| {
self.get_path_segment_data(seg).or_else(|| self.get_path_segment_data_with_id(seg, id))
})
2018-09-09 22:26:47 -05:00
}
pub fn get_path_segment_data(&self, path_seg: &ast::PathSegment) -> Option<Ref> {
self.get_path_segment_data_with_id(path_seg, path_seg.id)
}
fn get_path_segment_data_with_id(
&self,
path_seg: &ast::PathSegment,
id: NodeId,
) -> Option<Ref> {
// Returns true if the path is function type sugar, e.g., `Fn(A) -> B`.
2018-09-09 22:26:47 -05:00
fn fn_type(seg: &ast::PathSegment) -> bool {
if let Some(ref generic_args) = seg.args {
if let ast::GenericArgs::Parenthesized(_) = **generic_args {
return true;
}
}
false
}
if id == DUMMY_NODE_ID {
return None;
}
let res = self.get_path_res(id);
2018-09-09 22:26:47 -05:00
let span = path_seg.ident.span;
filter!(self.span_utils, span);
let span = self.span_from_span(span);
match res {
2019-12-22 16:42:04 -06:00
Res::Local(id) => Some(Ref {
kind: RefKind::Variable,
span,
ref_id: id_from_node_id(self.tcx.hir().hir_to_node_id(id), self),
}),
Res::Def(HirDefKind::Trait, def_id) if fn_type(path_seg) => {
2019-12-22 16:42:04 -06:00
Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id) })
}
2019-12-22 16:42:04 -06:00
Res::Def(HirDefKind::Struct, def_id)
| Res::Def(HirDefKind::Variant, def_id)
| Res::Def(HirDefKind::Union, def_id)
| Res::Def(HirDefKind::Enum, def_id)
| Res::Def(HirDefKind::TyAlias, def_id)
| Res::Def(HirDefKind::ForeignTy, def_id)
| Res::Def(HirDefKind::TraitAlias, def_id)
| Res::Def(HirDefKind::AssocOpaqueTy, def_id)
| Res::Def(HirDefKind::AssocTy, def_id)
| Res::Def(HirDefKind::Trait, def_id)
| Res::Def(HirDefKind::OpaqueTy, def_id)
| Res::Def(HirDefKind::TyParam, def_id) => {
Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(def_id) })
2015-07-07 21:30:18 -05:00
}
Res::Def(HirDefKind::ConstParam, def_id) => {
2019-12-22 16:42:04 -06:00
Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(def_id) })
}
Res::Def(HirDefKind::Ctor(CtorOf::Struct, ..), def_id) => {
// This is a reference to a tuple struct where the def_id points
// to an invisible constructor function. That is not a very useful
// def, so adjust to point to the tuple struct itself.
let parent_def_id = self.tcx.parent(def_id).unwrap();
2019-12-22 16:42:04 -06:00
Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(parent_def_id) })
}
2019-12-22 16:42:04 -06:00
Res::Def(HirDefKind::Static, _)
| Res::Def(HirDefKind::Const, _)
| Res::Def(HirDefKind::AssocConst, _)
| Res::Def(HirDefKind::Ctor(..), _) => {
Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(res.def_id()) })
}
Res::Def(HirDefKind::Method, decl_id) => {
let def_id = if decl_id.is_local() {
let ti = self.tcx.associated_item(decl_id);
2017-11-07 15:43:05 -06:00
self.tcx
.associated_items(ti.container.id())
.filter_by_name_unhygienic(ti.ident.name)
.find(|item| item.defaultness.has_value())
.map(|item| item.def_id)
2015-07-07 21:30:18 -05:00
} else {
None
};
Some(Ref {
kind: RefKind::Function,
span,
ref_id: id_from_def_id(def_id.unwrap_or(decl_id)),
})
2015-09-01 22:37:07 -05:00
}
Res::Def(HirDefKind::Fn, def_id) => {
2019-12-22 16:42:04 -06:00
Some(Ref { kind: RefKind::Function, span, ref_id: id_from_def_id(def_id) })
2015-07-07 21:30:18 -05:00
}
Res::Def(HirDefKind::Mod, def_id) => {
2019-12-22 16:42:04 -06:00
Some(Ref { kind: RefKind::Mod, span, ref_id: id_from_def_id(def_id) })
}
2019-12-22 16:42:04 -06:00
Res::PrimTy(..)
| Res::SelfTy(..)
| Res::Def(HirDefKind::Macro(..), _)
| Res::ToolMod
| Res::NonMacroAttr(..)
| Res::SelfCtor(..)
| Res::Err => None,
2015-07-07 21:30:18 -05:00
}
}
2017-11-07 15:43:05 -06:00
pub fn get_field_ref_data(
&self,
field_ref: &ast::Field,
variant: &ty::VariantDef,
) -> Option<Ref> {
filter!(self.span_utils, field_ref.ident.span);
self.tcx.find_field_index(field_ref.ident, variant).map(|index| {
let span = self.span_from_span(field_ref.ident.span);
2019-12-22 16:42:04 -06:00
Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(variant.fields[index].did) }
})
2015-06-08 16:51:54 -05:00
}
/// Attempt to return MacroRef for any AST node.
///
/// For a given piece of AST defined by the supplied Span and NodeId,
2019-02-08 07:53:55 -06:00
/// returns `None` if the node is not macro-generated or the span is malformed,
/// else uses the expansion callsite and callee to return some MacroRef.
pub fn get_macro_use_data(&self, span: Span) -> Option<MacroRef> {
if !generated_code(span) {
return None;
}
// Note we take care to use the source callsite/callee, to handle
// nested expansions and ensure we only generate data for source-visible
// macro uses.
2017-03-16 23:04:41 -05:00
let callsite = span.source_callsite();
let callsite_span = self.span_from_span(callsite);
let callee = span.source_callee()?;
let mac_name = match callee.kind {
ExpnKind::Macro(mac_kind, name) => match mac_kind {
MacroKind::Bang => name,
// Ignore attribute macros, their spans are usually mangled
// FIXME(eddyb) is this really the case anymore?
MacroKind::Attr | MacroKind::Derive => return None,
},
// These are not macros.
// FIXME(eddyb) maybe there is a way to handle them usefully?
ExpnKind::Root | ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) => return None,
};
// If the callee is an imported macro from an external crate, need to get
// the source span and name from the session, as their spans are localized
// when read in, and no longer correspond to the source.
2019-12-22 16:42:04 -06:00
if let Some(mac) = self.tcx.sess.imported_macro_spans.borrow().get(&callee.def_site) {
let &(ref mac_name, mac_span) = mac;
let mac_span = self.span_from_span(mac_span);
return Some(MacroRef {
span: callsite_span,
qualname: mac_name.clone(), // FIXME: generate the real qualname
callee_span: mac_span,
});
}
let callee_span = self.span_from_span(callee.def_site);
Some(MacroRef {
span: callsite_span,
qualname: mac_name.to_string(), // FIXME: generate the real qualname
callee_span,
})
}
fn lookup_def_id(&self, ref_id: NodeId) -> Option<DefId> {
match self.get_path_res(ref_id) {
Res::PrimTy(_) | Res::SelfTy(..) | Res::Err => None,
def => Some(def.def_id()),
2015-06-05 00:50:04 -05:00
}
}
fn docs_for_attrs(&self, attrs: &[Attribute]) -> String {
let mut result = String::new();
for attr in attrs {
2019-12-07 12:28:29 -06:00
if let Some(val) = attr.doc_str() {
if attr.is_doc_comment() {
result.push_str(&strip_doc_comment_decoration(&val.as_str()));
} else {
result.push_str(&val.as_str());
}
result.push('\n');
} else if attr.check_name(sym::doc) {
if let Some(meta_list) = attr.meta_item_list() {
2019-12-22 16:42:04 -06:00
meta_list
.into_iter()
.filter(|it| it.check_name(sym::include))
.filter_map(|it| it.meta_item_list().map(|l| l.to_owned()))
.flat_map(|it| it)
.filter(|meta| meta.check_name(sym::contents))
.filter_map(|meta| meta.value_str())
.for_each(|val| {
result.push_str(&val.as_str());
result.push('\n');
});
}
}
}
if !self.config.full_docs {
if let Some(index) = result.find("\n\n") {
result.truncate(index);
}
}
result
}
2018-02-02 01:29:59 -06:00
fn next_impl_id(&self) -> u32 {
let next = self.impl_counter.get();
self.impl_counter.set(next + 1);
next
}
}
fn make_signature(decl: &ast::FnDecl, generics: &ast::Generics) -> String {
let mut sig = "fn ".to_owned();
if !generics.params.is_empty() {
sig.push('<');
2019-12-22 16:42:04 -06:00
sig.push_str(
&generics
.params
.iter()
.map(|param| param.ident.to_string())
.collect::<Vec<_>>()
.join(", "),
);
sig.push_str("> ");
}
sig.push('(');
2019-12-22 16:42:04 -06:00
sig.push_str(&decl.inputs.iter().map(param_to_string).collect::<Vec<_>>().join(", "));
sig.push(')');
match decl.output {
2020-02-14 21:10:59 -06:00
ast::FnRetTy::Default(_) => sig.push_str(" -> ()"),
ast::FnRetTy::Ty(ref t) => sig.push_str(&format!(" -> {}", ty_to_string(t))),
}
sig
}
// An AST visitor for collecting paths (e.g., the names of structs) and formal
// variables (idents) from patterns.
struct PathCollector<'l> {
collected_paths: Vec<(NodeId, &'l ast::Path)>,
2018-03-18 19:54:56 -05:00
collected_idents: Vec<(NodeId, ast::Ident, ast::Mutability)>,
2015-05-05 05:03:20 -05:00
}
impl<'l> PathCollector<'l> {
fn new() -> PathCollector<'l> {
2019-12-22 16:42:04 -06:00
PathCollector { collected_paths: vec![], collected_idents: vec![] }
2015-05-05 05:03:20 -05:00
}
}
impl<'l> Visitor<'l> for PathCollector<'l> {
fn visit_pat(&mut self, p: &'l ast::Pat) {
2019-09-26 10:18:31 -05:00
match p.kind {
2016-08-26 11:23:42 -05:00
PatKind::Struct(ref path, ..) => {
self.collected_paths.push((p.id, path));
2015-05-05 05:03:20 -05:00
}
2017-11-07 15:43:05 -06:00
PatKind::TupleStruct(ref path, ..) | PatKind::Path(_, ref path) => {
self.collected_paths.push((p.id, path));
2015-05-05 05:03:20 -05:00
}
2018-03-18 08:47:09 -05:00
PatKind::Ident(bm, ident, _) => {
2017-11-07 15:43:05 -06:00
debug!(
"PathCollector, visit ident in pat {}: {:?} {:?}",
2019-12-22 16:42:04 -06:00
ident, p.span, ident.span
2017-11-07 15:43:05 -06:00
);
2015-05-05 05:03:20 -05:00
let immut = match bm {
// Even if the ref is mut, you can't change the ref, only
// the data pointed at, so showing the initialising expression
// is still worthwhile.
ast::BindingMode::ByRef(_) => ast::Mutability::Not,
ast::BindingMode::ByValue(mt) => mt,
2015-05-05 05:03:20 -05:00
};
2019-12-22 16:42:04 -06:00
self.collected_idents.push((p.id, ident, immut));
2015-05-05 05:03:20 -05:00
}
_ => {}
}
visit::walk_pat(self, p);
}
}
/// Defines what to do with the results of saving the analysis.
pub trait SaveHandler {
2019-12-22 16:42:04 -06:00
fn save(&mut self, save_ctxt: &SaveContext<'_, '_>, analysis: &Analysis);
}
/// Dump the save-analysis results to a file.
pub struct DumpHandler<'a> {
odir: Option<&'a Path>,
2017-11-07 15:43:05 -06:00
cratename: String,
}
impl<'a> DumpHandler<'a> {
pub fn new(odir: Option<&'a Path>, cratename: &str) -> DumpHandler<'a> {
2019-12-22 16:42:04 -06:00
DumpHandler { odir, cratename: cratename.to_owned() }
}
fn output_file(&self, ctx: &SaveContext<'_, '_>) -> (BufWriter<File>, PathBuf) {
let sess = &ctx.tcx.sess;
let file_name = match ctx.config.output_file {
Some(ref s) => PathBuf::from(s),
None => {
let mut root_path = match self.odir {
Some(val) => val.join("save-analysis"),
None => PathBuf::from("save-analysis-temp"),
};
if let Err(e) = std::fs::create_dir_all(&root_path) {
error!("Could not create directory {}: {}", root_path.display(), e);
}
2019-12-22 16:42:04 -06:00
let executable =
sess.crate_types.borrow().iter().any(|ct| *ct == CrateType::Executable);
let mut out_name = if executable { String::new() } else { "lib".to_owned() };
out_name.push_str(&self.cratename);
out_name.push_str(&sess.opts.cg.extra_filename);
out_name.push_str(".json");
root_path.push(&out_name);
root_path
}
};
info!("Writing output to {}", file_name.display());
2019-12-22 16:42:04 -06:00
let output_file = BufWriter::new(File::create(&file_name).unwrap_or_else(|e| {
sess.fatal(&format!("Could not open {}: {}", file_name.display(), e))
}));
(output_file, file_name)
}
}
impl SaveHandler for DumpHandler<'_> {
2019-12-22 16:42:04 -06:00
fn save(&mut self, save_ctxt: &SaveContext<'_, '_>, analysis: &Analysis) {
let sess = &save_ctxt.tcx.sess;
let (output, file_name) = self.output_file(&save_ctxt);
if let Err(e) = serde_json::to_writer(output, &analysis) {
2019-07-26 17:36:10 -05:00
error!("Can't serialize save-analysis: {:?}", e);
}
rustc: Stabilize options for pipelined compilation This commit stabilizes options in the compiler necessary for Cargo to enable "pipelined compilation" by default. The concept of pipelined compilation, how it's implemented, and what it means for rustc are documented in #60988. This PR is coupled with a PR against Cargo (rust-lang/cargo#7143) which updates Cargo's support for pipelined compliation to rustc, and also enables support by default in Cargo. (note that the Cargo PR cannot land until this one against rustc lands). The technical changes performed here were to stabilize the functionality proposed in #60419 and #60987, the underlying pieces to enable pipelined compilation support in Cargo. The issues have had some discussion during stabilization, but the newly stabilized surface area here is: * A new `--json` flag was added to the compiler. * The `--json` flag can be passed multiple times. * The value of the `--json` flag is a comma-separated list of directives. * The `--json` flag cannot be combined with `--color` * The `--json` flag must be combined with `--error-format=json` * The acceptable list of directives to `--json` are: * `diagnostic-short` - the `rendered` field of diagnostics will have a "short" rendering matching `--error-format=short` * `diagnostic-rendered-ansi` - the `rendered` field of diagnostics will be colorized with ansi color codes embedded in the string field * `artifacts` - JSON blobs will be emitted for artifacts being emitted by the compiler The unstable `-Z emit-artifact-notifications` and `--json-rendered` flags have also been removed during this commit as well. Closes #60419 Closes #60987 Closes #60988
2019-07-17 14:52:56 -05:00
if sess.opts.json_artifact_notifications {
2019-12-22 16:42:04 -06:00
sess.parse_sess.span_diagnostic.emit_artifact_notification(&file_name, "save-analysis");
}
}
}
/// Call a callback with the results of save-analysis.
pub struct CallbackHandler<'b> {
pub callback: &'b mut dyn FnMut(&rls_data::Analysis),
}
impl SaveHandler for CallbackHandler<'_> {
2019-12-22 16:42:04 -06:00
fn save(&mut self, _: &SaveContext<'_, '_>, analysis: &Analysis) {
(self.callback)(analysis)
}
}
2017-11-07 15:43:05 -06:00
pub fn process_crate<'l, 'tcx, H: SaveHandler>(
2019-06-13 16:48:52 -05:00
tcx: TyCtxt<'tcx>,
2017-11-07 15:43:05 -06:00
krate: &ast::Crate,
cratename: &str,
input: &'l Input,
2017-11-07 15:43:05 -06:00
config: Option<Config>,
mut handler: H,
) {
tcx.dep_graph.with_ignore(|| {
info!("Dumping crate {}", cratename);
// Privacy checking requires and is done after type checking; use a
// fallback in case the access levels couldn't have been correctly computed.
let access_levels = match tcx.sess.compile_status() {
Ok(..) => tcx.privacy_access_levels(LOCAL_CRATE),
2018-12-01 08:34:04 -06:00
Err(..) => tcx.arena.alloc(AccessLevels::default()),
};
let save_ctxt = SaveContext {
tcx,
tables: &ty::TypeckTables::empty(None),
empty_tables: &ty::TypeckTables::empty(None),
access_levels: &access_levels,
span_utils: SpanUtils::new(&tcx.sess),
config: find_config(config),
2018-02-02 01:29:59 -06:00
impl_counter: Cell::new(0),
};
let mut visitor = DumpVisitor::new(save_ctxt);
visitor.dump_crate_info(cratename, krate);
visitor.dump_compilation_options(input, cratename);
visit::walk_crate(&mut visitor, krate);
handler.save(&visitor.save_ctxt, &visitor.analysis())
})
}
fn find_config(supplied: Option<Config>) -> Config {
if let Some(config) = supplied {
return config;
}
match env::var_os("RUST_SAVE_ANALYSIS_CONFIG") {
None => Config::default(),
2019-12-22 16:42:04 -06:00
Some(config) => config
.to_str()
.ok_or(())
.map_err(|_| error!("`RUST_SAVE_ANALYSIS_CONFIG` isn't UTF-8"))
2019-12-22 16:42:04 -06:00
.and_then(|cfg| {
serde_json::from_str(cfg)
.map_err(|_| error!("Could not deserialize save-analysis config"))
})
.unwrap_or_default(),
}
}
// Utility functions for the module.
// Helper function to escape quotes in a string
fn escape(s: String) -> String {
s.replace("\"", "\"\"")
}
// Helper function to determine if a span came from a
// macro expansion or syntax extension.
2017-05-30 23:13:27 -05:00
fn generated_code(span: Span) -> bool {
span.from_expansion() || span.is_dummy()
}
2017-05-30 23:13:27 -05:00
// DefId::index is a newtype and so the JSON serialisation is ugly. Therefore
// we use our own Id which is the same, but without the newtype.
fn id_from_def_id(id: DefId) -> rls_data::Id {
2019-12-22 16:42:04 -06:00
rls_data::Id { krate: id.krate.as_u32(), index: id.index.as_u32() }
2017-05-30 23:13:27 -05:00
}
2019-02-06 10:02:00 -06:00
fn id_from_node_id(id: NodeId, scx: &SaveContext<'_, '_>) -> rls_data::Id {
2019-06-27 04:49:08 -05:00
let def_id = scx.tcx.hir().opt_local_def_id_from_node_id(id);
def_id.map(|id| id_from_def_id(id)).unwrap_or_else(|| {
// Create a *fake* `DefId` out of a `NodeId` by subtracting the `NodeId`
// out of the maximum u32 value. This will work unless you have *billions*
// of definitions in a single crate (very unlikely to actually happen).
2019-12-22 16:42:04 -06:00
rls_data::Id { krate: LOCAL_CRATE.as_u32(), index: !id.as_u32() }
})
}
fn null_id() -> rls_data::Id {
2019-12-22 16:42:04 -06:00
rls_data::Id { krate: u32::max_value(), index: u32::max_value() }
}
2019-02-06 10:02:00 -06:00
fn lower_attributes(attrs: Vec<Attribute>, scx: &SaveContext<'_, '_>) -> Vec<rls_data::Attribute> {
2019-12-22 16:42:04 -06:00
attrs
.into_iter()
// Only retain real attributes. Doc comments are lowered separately.
.filter(|attr| !attr.has_name(sym::doc))
.map(|mut attr| {
// Remove the surrounding '#[..]' or '#![..]' of the pretty printed
// attribute. First normalize all inner attribute (#![..]) to outer
// ones (#[..]), then remove the two leading and the one trailing character.
attr.style = ast::AttrStyle::Outer;
let value = pprust::attribute_to_string(&attr);
// This str slicing works correctly, because the leading and trailing characters
// are in the ASCII range and thus exactly one byte each.
let value = value[2..value.len() - 1].to_string();
rls_data::Attribute { value, span: scx.span_from_span(attr.span) }
})
.collect()
2017-05-30 23:13:27 -05:00
}