Skip to content

Commit

Permalink
feat(rust): Add support for Rust cross-references (#4641)
Browse files Browse the repository at this point in the history
This PR adds support for emitting cross references with the Rust indexer and adds a verifier test to check functionality
  • Loading branch information
wcalandro committed Aug 11, 2020
1 parent 8750956 commit 8555988
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 1 deletion.
5 changes: 5 additions & 0 deletions kythe/rust/indexer/BUILD
Expand Up @@ -122,3 +122,8 @@ rust_indexer_test(
name = "variable_test",
srcs = ["testdata/variable.rs"],
)

rust_indexer_test(
name = "xrefs_test",
srcs = ["testdata/xrefs.rs"],
)
66 changes: 65 additions & 1 deletion kythe/rust/indexer/src/indexer/analyzers.rs
Expand Up @@ -149,7 +149,7 @@ impl<'a> UnitAnalyzer<'a> {
crate_analyzer.emit_tbuiltin_nodes()?;
crate_analyzer.process_implementations()?;
crate_analyzer.emit_definitions()?;
// TODO(Arm1stice): Emit references and implementations
crate_analyzer.emit_xrefs()?;
Ok(())
}

Expand Down Expand Up @@ -660,6 +660,70 @@ impl<'a, 'b> CrateAnalyzer<'a, 'b> {

Ok(())
}

/// Emit the Kythe edges for cross references in this crate
pub fn emit_xrefs(&mut self) -> Result<(), KytheError> {
// We must clone to avoid double borrowing "self"
let analysis = self.krate.analysis.clone();

let mut template_vname = self.krate_vname.clone();
template_vname.set_language("rust".to_string());
let krate_signature = template_vname.get_signature();

for reference in &analysis.refs {
let mut reference_vname = template_vname.clone();
let span = &reference.span;
reference_vname.set_path(span.file_name.to_str().unwrap().to_string());

// Get byte span
let start_byte_option = self.offset_index.get_byte_offset(
span.file_name.to_str().unwrap(),
span.line_start.0,
span.column_start.0,
);

// If the start byte is none, then the save_analysis is giving information about
// standard library files and we should skip
if start_byte_option.is_none() {
continue;
}

let start_byte = start_byte_option.unwrap();
let end_byte = self
.offset_index
.get_byte_offset(
span.file_name.to_str().unwrap(),
span.line_end.0,
span.column_end.0,
)
.ok_or_else(|| {
KytheError::IndexerError(format!(
"Failed to get ending offset for reference {:?}",
reference
))
})?;

// Create signature based on span
reference_vname
.set_signature(format!("{}_ref_{}_{}", krate_signature, start_byte, end_byte));

// Create VName being referenced
let disambiguators = self.krate_ids.get(&reference.ref_id.krate).ok_or_else(|| {
KytheError::IndexerError(format!(
"Failed to get krate disambiguator for reference {:?}",
reference
))
})?;

let mut target_vname = reference_vname.clone();
target_vname
.set_signature(format!("{}_def_{}", disambiguators, reference.ref_id.index));
target_vname.set_language("rust".to_string());

self.emitter.emit_reference(&reference_vname, &target_vname, start_byte, end_byte)?;
}
Ok(())
}
}

/// Convert a VName from analysis_rust_proto to a VName from storage_rust_proto
Expand Down
22 changes: 22 additions & 0 deletions kythe/rust/indexer/src/indexer/entries.rs
Expand Up @@ -86,4 +86,26 @@ impl<'a> EntryEmitter<'a> {
self.emit_node(anchor_vname, "/kythe/loc/end", byte_end.to_string().into_bytes().to_vec())?;
self.emit_edge(anchor_vname, target_vname, "/kythe/edge/defines/binding")
}

/// Creates an anchor node with ref edge to the target and emits
/// it.
///
/// # Errors
/// If an error occurs while writing the entry, an error is returned.
pub fn emit_reference(
&mut self,
anchor_vname: &VName,
target_vname: &VName,
byte_start: u32,
byte_end: u32,
) -> Result<(), KytheError> {
self.emit_node(anchor_vname, "/kythe/node/kind", b"anchor".to_vec())?;
self.emit_node(
anchor_vname,
"/kythe/loc/start",
byte_start.to_string().into_bytes().to_vec(),
)?;
self.emit_node(anchor_vname, "/kythe/loc/end", byte_end.to_string().into_bytes().to_vec())?;
self.emit_edge(anchor_vname, target_vname, "/kythe/edge/ref")
}
}
80 changes: 80 additions & 0 deletions kythe/rust/indexer/testdata/xrefs.rs
@@ -0,0 +1,80 @@
// Verifies that cross-references work properly

//- @NUM defines/binding NumConst
const NUM: u32 = 0;

//- @KYTHE_STR defines/binding Static
static KYTHE_STR: &str = "Kythe";

//- @CustomType defines/binding Type
type CustomType = u32;

//- @arg1 defines/binding Arg1
//- @arg2 defines/binding Arg2
//- @add defines/binding AddFn
fn add(arg1: u32, arg2: u32) -> u32 {
//- @arg1 ref Arg1
//- @arg2 ref Arg2
arg1 + arg2
}

//- @TestTrait defines/binding Trait
trait TestTrait {
//- @hello defines/binding TraitHelloFn
fn hello(){}
}

//- @TestStruct defines/binding Struct
struct TestStruct {
//- @test_field defines/binding Field
//- @CustomType ref Type
pub test_field: CustomType,
}

//- @TestTrait ref Trait
//- @TestStruct ref Struct
impl TestTrait for TestStruct {
//- @hello defines/binding HelloFn
//- HelloFn.node/kind function
fn hello() {
println!("Hello, Rust xrefs!");
}
}

fn main() {
//- @var1 defines/binding Var1
let var1 = 1;
//- @var2 defines/binding Var2
let var2 = 2;
//- @var3 defines/binding Var3
//- @var1 ref Var1
//- @var2 ref Var2
//- @add ref AddFn
let var3 = add(var1, var2);

//- @var3 ref Var3
println!("{}", var3);

//- @var3 ref Var3
//- @NUM ref NumConst
let var4 = var3 + NUM;

//- @KYTHE_STR ref Static
println!("{}", KYTHE_STR);

//- @TestStruct ref Struct
//- @var5 defines/binding Var5
let var5 = TestStruct {
//- @test_field ref Field
test_field: var4,
};

//- @var5 ref Var5
//- @test_field ref Field
println!("{}", var5.test_field);

// TODO: Make this reference the implementation on the method,
// not the trait definition.
//- @"hello" ref TraitHelloFn
TestStruct::hello();
}

0 comments on commit 8555988

Please sign in to comment.