Skip to content

Commit 5b57826

Browse files
authored
Merge pull request #17659 from github/aibaars/rust-macros
Rust: add macro expansion to the extractor
2 parents 2af60f1 + fc298b2 commit 5b57826

File tree

79 files changed

+1851
-338
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+1851
-338
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/ast-generator/src/main.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -440,16 +440,17 @@ fn write_extractor(grammar: &AstSrc) -> std::io::Result<String> {
440440
"//! Generated by `ast-generator`, do not edit by hand.\n
441441
#![cfg_attr(any(), rustfmt::skip)]
442442
443-
use crate::generated;
444443
use super::base::{{TextValue, Translator}};
444+
use crate::emit_detached;
445+
use crate::generated;
445446
use crate::trap::{{Label, TrapId}};
446447
use ra_ap_syntax::ast::{{
447448
HasArgList, HasAttrs, HasGenericArgs, HasGenericParams, HasLoopBody, HasModuleItem, HasName,
448449
HasTypeBounds, HasVisibility, RangeItem,
449450
}};
450451
use ra_ap_syntax::{{ast, AstNode}};
451452
452-
impl Translator {{
453+
impl Translator<'_> {{
453454
fn emit_else_branch(&mut self, node: ast::ElseBranch) -> Label<generated::Expr> {{
454455
match node {{
455456
ast::ElseBranch::IfExpr(inner) => self.emit_if_expr(inner).into(),
@@ -550,7 +551,12 @@ impl Translator {{
550551
writeln!(buf, " self.emit_location(label, &node);")?;
551552
writeln!(
552553
buf,
553-
" self.emit_tokens(label.into(), node.syntax().children_with_tokens());"
554+
" emit_detached!({}, self, node, label);",
555+
class_name
556+
)?;
557+
writeln!(
558+
buf,
559+
" self.emit_tokens(&node, label.into(), node.syntax().children_with_tokens());"
554560
)?;
555561
writeln!(buf, " label")?;
556562

@@ -566,9 +572,6 @@ fn main() -> std::io::Result<()> {
566572
.parse()
567573
.unwrap();
568574
let mut grammar = codegen::grammar::lower(&grammar);
569-
grammar
570-
.nodes
571-
.retain(|x| x.name != "MacroStmts" && x.name != "MacroItems");
572575

573576
grammar.enums.retain(|x| x.name != "Adt");
574577

rust/extractor/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@ ra_ap_base_db = "0.0.232"
1313
ra_ap_hir = "0.0.232"
1414
ra_ap_hir_def = "0.0.232"
1515
ra_ap_ide_db = "0.0.232"
16+
ra_ap_hir_expand = "0.0.232"
1617
ra_ap_load-cargo = "0.0.232"
1718
ra_ap_paths = "0.0.232"
1819
ra_ap_project_model = "0.0.232"
1920
ra_ap_syntax = "0.0.232"
2021
ra_ap_vfs = "0.0.232"
2122
ra_ap_parser = "0.0.232"
23+
ra_ap_span = "0.0.232"
2224
serde = "1.0.209"
2325
serde_with = "3.9.0"
2426
stderrlog = "0.6.0"

rust/extractor/src/generated/.generated.list

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/extractor/src/generated/top.rs

Lines changed: 110 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/extractor/src/main.rs

Lines changed: 94 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,119 @@
1+
use std::{
2+
collections::HashMap,
3+
path::{Path, PathBuf},
4+
};
5+
16
use anyhow::Context;
2-
use ra_ap_ide_db::line_index::LineIndex;
3-
use ra_ap_parser::Edition;
4-
use std::borrow::Cow;
7+
use archive::Archiver;
8+
use ra_ap_ide_db::line_index::{LineCol, LineIndex};
9+
use ra_ap_project_model::ProjectManifest;
10+
use rust_analyzer::{ParseResult, RustAnalyzer};
511
mod archive;
612
mod config;
713
pub mod generated;
14+
mod rust_analyzer;
815
mod translate;
916
pub mod trap;
10-
use ra_ap_syntax::ast::SourceFile;
11-
use ra_ap_syntax::{AstNode, SyntaxError, TextRange, TextSize};
12-
13-
fn from_utf8_lossy(v: &[u8]) -> (Cow<'_, str>, Option<SyntaxError>) {
14-
let mut iter = v.utf8_chunks();
15-
let (first_valid, first_invalid) = if let Some(chunk) = iter.next() {
16-
let valid = chunk.valid();
17-
let invalid = chunk.invalid();
18-
if invalid.is_empty() {
19-
debug_assert_eq!(valid.len(), v.len());
20-
return (Cow::Borrowed(valid), None);
21-
}
22-
(valid, invalid)
23-
} else {
24-
return (Cow::Borrowed(""), None);
25-
};
26-
27-
const REPLACEMENT: &str = "\u{FFFD}";
28-
let error_start = first_valid.len() as u32;
29-
let error_end = error_start + first_invalid.len() as u32;
30-
let error_range = TextRange::new(TextSize::new(error_start), TextSize::new(error_end));
31-
let error = SyntaxError::new("invalid utf-8 sequence".to_owned(), error_range);
32-
let mut res = String::with_capacity(v.len());
33-
res.push_str(first_valid);
34-
35-
res.push_str(REPLACEMENT);
36-
37-
for chunk in iter {
38-
res.push_str(chunk.valid());
39-
if !chunk.invalid().is_empty() {
40-
res.push_str(REPLACEMENT);
41-
}
42-
}
43-
44-
(Cow::Owned(res), Some(error))
45-
}
4617

4718
fn extract(
48-
archiver: &archive::Archiver,
19+
rust_analyzer: &mut rust_analyzer::RustAnalyzer,
20+
archiver: &Archiver,
4921
traps: &trap::TrapFileProvider,
50-
file: std::path::PathBuf,
51-
) -> anyhow::Result<()> {
52-
let file = std::path::absolute(&file).unwrap_or(file);
53-
let file = std::fs::canonicalize(&file).unwrap_or(file);
54-
archiver.archive(&file);
55-
let input = std::fs::read(&file)?;
56-
let (input, err) = from_utf8_lossy(&input);
57-
let line_index = LineIndex::new(&input);
22+
file: &std::path::Path,
23+
) {
24+
archiver.archive(file);
25+
26+
let ParseResult {
27+
ast,
28+
text,
29+
errors,
30+
file_id,
31+
semantics,
32+
} = rust_analyzer.parse(file);
33+
let line_index = LineIndex::new(text.as_ref());
5834
let display_path = file.to_string_lossy();
59-
let mut trap = traps.create("source", &file);
60-
let label = trap.emit_file(&file);
61-
let mut translator = translate::Translator::new(trap, label, line_index);
62-
if let Some(err) = err {
63-
translator.emit_parse_error(display_path.as_ref(), err);
64-
}
65-
let parse = ra_ap_syntax::ast::SourceFile::parse(&input, Edition::CURRENT);
66-
for err in parse.errors() {
67-
translator.emit_parse_error(display_path.as_ref(), err);
35+
let mut trap = traps.create("source", file);
36+
let label = trap.emit_file(file);
37+
let mut translator = translate::Translator::new(
38+
trap,
39+
display_path.as_ref(),
40+
label,
41+
line_index,
42+
file_id,
43+
semantics,
44+
);
45+
46+
for err in errors {
47+
translator.emit_parse_error(&ast, &err);
6848
}
69-
if let Some(ast) = SourceFile::cast(parse.syntax_node()) {
70-
translator.emit_source_file(ast);
71-
} else {
72-
log::warn!("Skipped {}", display_path);
49+
let no_location = (LineCol { line: 0, col: 0 }, LineCol { line: 0, col: 0 });
50+
if translator.semantics.is_none() {
51+
translator.emit_diagnostic(
52+
trap::DiagnosticSeverity::Warning,
53+
"semantics".to_owned(),
54+
"semantic analyzer unavailable".to_owned(),
55+
"semantic analyzer unavailable: macro expansion, call graph, and type inference will be skipped.".to_owned(),
56+
no_location,
57+
);
7358
}
74-
translator.trap.commit()?;
75-
Ok(())
59+
translator.emit_source_file(ast);
60+
translator.trap.commit().unwrap_or_else(|err| {
61+
log::error!(
62+
"Failed to write trap file for: {}: {}",
63+
display_path,
64+
err.to_string()
65+
)
66+
});
7667
}
7768
fn main() -> anyhow::Result<()> {
7869
let cfg = config::Config::extract().context("failed to load configuration")?;
7970
stderrlog::new()
8071
.module(module_path!())
81-
.verbosity(2 + cfg.verbose as usize)
72+
.verbosity(1 + cfg.verbose as usize)
8273
.init()?;
83-
log::info!("{cfg:?}");
74+
8475
let traps = trap::TrapFileProvider::new(&cfg).context("failed to set up trap files")?;
8576
let archiver = archive::Archiver {
8677
root: cfg.source_archive_dir,
8778
};
88-
for file in cfg.inputs {
89-
extract(&archiver, &traps, file)?;
79+
let files: Vec<PathBuf> = cfg
80+
.inputs
81+
.iter()
82+
.map(|file| {
83+
let file = std::path::absolute(file).unwrap_or(file.to_path_buf());
84+
std::fs::canonicalize(&file).unwrap_or(file)
85+
})
86+
.collect();
87+
let manifests = rust_analyzer::find_project_manifests(&files)?;
88+
let mut map: HashMap<&Path, (&ProjectManifest, Vec<&Path>)> = manifests
89+
.iter()
90+
.map(|x| (x.manifest_path().parent().as_ref(), (x, Vec::new())))
91+
.collect();
92+
let mut other_files = Vec::new();
93+
94+
'outer: for file in &files {
95+
let mut p = file.as_path();
96+
while let Some(parent) = p.parent() {
97+
p = parent;
98+
if let Some((_, files)) = map.get_mut(parent) {
99+
files.push(file);
100+
continue 'outer;
101+
}
102+
}
103+
other_files.push(file);
104+
}
105+
for (manifest, files) in map.values() {
106+
if files.is_empty() {
107+
break;
108+
}
109+
let mut rust_analyzer = RustAnalyzer::new(manifest, &cfg.scratch_dir);
110+
for file in files {
111+
extract(&mut rust_analyzer, &archiver, &traps, file);
112+
}
113+
}
114+
let mut rust_analyzer = RustAnalyzer::WithoutDatabase();
115+
for file in other_files {
116+
extract(&mut rust_analyzer, &archiver, &traps, file);
90117
}
91118

92119
Ok(())

0 commit comments

Comments
 (0)