Auto merge of #132030 - matthiaskrgr:rollup-1g6quh0, r=matthiaskrgr

Rollup of 3 pull requests

Successful merges:

 - #131918 (coverage: Make counter creation handle node/edge counters more uniformly)
 - #132021 (nuttx.md: typo)
 - #132029 (Subtree update of `rust-analyzer`)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-10-22 08:34:18 +00:00
commit 916e9ced40
65 changed files with 7234 additions and 3056 deletions

View file

@ -5,7 +5,6 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::graph::DirectedGraph;
use rustc_index::IndexVec;
use rustc_index::bit_set::BitSet;
use rustc_middle::bug;
use rustc_middle::mir::coverage::{CounterId, CovTerm, Expression, ExpressionId, Op};
use tracing::{debug, debug_span, instrument};
@ -58,13 +57,13 @@ pub(super) struct CoverageCounters {
counter_increment_sites: IndexVec<CounterId, CounterIncrementSite>,
/// Coverage counters/expressions that are associated with individual BCBs.
bcb_counters: IndexVec<BasicCoverageBlock, Option<BcbCounter>>,
node_counters: IndexVec<BasicCoverageBlock, Option<BcbCounter>>,
/// Coverage counters/expressions that are associated with the control-flow
/// edge between two BCBs.
///
/// We currently don't iterate over this map, but if we do in the future,
/// switch it back to `FxIndexMap` to avoid query stability hazards.
bcb_edge_counters: FxHashMap<(BasicCoverageBlock, BasicCoverageBlock), BcbCounter>,
edge_counters: FxHashMap<(BasicCoverageBlock, BasicCoverageBlock), BcbCounter>,
/// Table of expression data, associating each expression ID with its
/// corresponding operator (+ or -) and its LHS/RHS operands.
@ -78,20 +77,20 @@ impl CoverageCounters {
/// Ensures that each BCB node needing a counter has one, by creating physical
/// counters or counter expressions for nodes and edges as required.
pub(super) fn make_bcb_counters(
basic_coverage_blocks: &CoverageGraph,
graph: &CoverageGraph,
bcb_needs_counter: &BitSet<BasicCoverageBlock>,
) -> Self {
let mut counters = MakeBcbCounters::new(basic_coverage_blocks, bcb_needs_counter);
counters.make_bcb_counters();
let mut builder = CountersBuilder::new(graph, bcb_needs_counter);
builder.make_bcb_counters();
counters.coverage_counters
builder.counters
}
fn with_num_bcbs(num_bcbs: usize) -> Self {
Self {
counter_increment_sites: IndexVec::new(),
bcb_counters: IndexVec::from_elem_n(None, num_bcbs),
bcb_edge_counters: FxHashMap::default(),
node_counters: IndexVec::from_elem_n(None, num_bcbs),
edge_counters: FxHashMap::default(),
expressions: IndexVec::new(),
expressions_memo: FxHashMap::default(),
}
@ -104,24 +103,18 @@ impl CoverageCounters {
BcbCounter::Counter { id }
}
/// Creates a new physical counter attached a BCB node.
/// The node must not already have a counter.
/// Creates a new physical counter for a BCB node.
fn make_phys_node_counter(&mut self, bcb: BasicCoverageBlock) -> BcbCounter {
let counter = self.make_counter_inner(CounterIncrementSite::Node { bcb });
debug!(?bcb, ?counter, "node gets a physical counter");
self.set_bcb_counter(bcb, counter)
self.make_counter_inner(CounterIncrementSite::Node { bcb })
}
/// Creates a new physical counter attached to a BCB edge.
/// The edge must not already have a counter.
/// Creates a new physical counter for a BCB edge.
fn make_phys_edge_counter(
&mut self,
from_bcb: BasicCoverageBlock,
to_bcb: BasicCoverageBlock,
) -> BcbCounter {
let counter = self.make_counter_inner(CounterIncrementSite::Edge { from_bcb, to_bcb });
debug!(?from_bcb, ?to_bcb, ?counter, "edge gets a physical counter");
self.set_bcb_edge_counter(from_bcb, to_bcb, counter)
self.make_counter_inner(CounterIncrementSite::Edge { from_bcb, to_bcb })
}
fn make_expression(&mut self, lhs: BcbCounter, op: Op, rhs: BcbCounter) -> BcbCounter {
@ -193,35 +186,31 @@ impl CoverageCounters {
self.counter_increment_sites.len()
}
fn set_bcb_counter(&mut self, bcb: BasicCoverageBlock, counter_kind: BcbCounter) -> BcbCounter {
if let Some(replaced) = self.bcb_counters[bcb].replace(counter_kind) {
bug!(
"attempt to set a BasicCoverageBlock coverage counter more than once; \
{bcb:?} already had counter {replaced:?}",
);
} else {
counter_kind
}
fn set_node_counter(&mut self, bcb: BasicCoverageBlock, counter: BcbCounter) -> BcbCounter {
let existing = self.node_counters[bcb].replace(counter);
assert!(
existing.is_none(),
"node {bcb:?} already has a counter: {existing:?} => {counter:?}"
);
counter
}
fn set_bcb_edge_counter(
fn set_edge_counter(
&mut self,
from_bcb: BasicCoverageBlock,
to_bcb: BasicCoverageBlock,
counter_kind: BcbCounter,
counter: BcbCounter,
) -> BcbCounter {
if let Some(replaced) = self.bcb_edge_counters.insert((from_bcb, to_bcb), counter_kind) {
bug!(
"attempt to set an edge counter more than once; from_bcb: \
{from_bcb:?} already had counter {replaced:?}",
);
} else {
counter_kind
}
let existing = self.edge_counters.insert((from_bcb, to_bcb), counter);
assert!(
existing.is_none(),
"edge ({from_bcb:?} -> {to_bcb:?}) already has a counter: {existing:?} => {counter:?}"
);
counter
}
pub(super) fn term_for_bcb(&self, bcb: BasicCoverageBlock) -> Option<CovTerm> {
self.bcb_counters[bcb].map(|counter| counter.as_term())
self.node_counters[bcb].map(|counter| counter.as_term())
}
/// Returns an iterator over all the nodes/edges in the coverage graph that
@ -238,7 +227,7 @@ impl CoverageCounters {
pub(super) fn bcb_nodes_with_coverage_expressions(
&self,
) -> impl Iterator<Item = (BasicCoverageBlock, ExpressionId)> + Captures<'_> {
self.bcb_counters.iter_enumerated().filter_map(|(bcb, &counter_kind)| match counter_kind {
self.node_counters.iter_enumerated().filter_map(|(bcb, &counter)| match counter {
// Yield the BCB along with its associated expression ID.
Some(BcbCounter::Expression { id }) => Some((bcb, id)),
// This BCB is associated with a counter or nothing, so skip it.
@ -265,22 +254,20 @@ impl CoverageCounters {
}
}
/// Helper struct that allows counter creation to inspect the BCB graph.
struct MakeBcbCounters<'a> {
coverage_counters: CoverageCounters,
basic_coverage_blocks: &'a CoverageGraph,
/// Helper struct that allows counter creation to inspect the BCB graph, and
/// the set of nodes that need counters.
struct CountersBuilder<'a> {
counters: CoverageCounters,
graph: &'a CoverageGraph,
bcb_needs_counter: &'a BitSet<BasicCoverageBlock>,
}
impl<'a> MakeBcbCounters<'a> {
fn new(
basic_coverage_blocks: &'a CoverageGraph,
bcb_needs_counter: &'a BitSet<BasicCoverageBlock>,
) -> Self {
assert_eq!(basic_coverage_blocks.num_nodes(), bcb_needs_counter.domain_size());
impl<'a> CountersBuilder<'a> {
fn new(graph: &'a CoverageGraph, bcb_needs_counter: &'a BitSet<BasicCoverageBlock>) -> Self {
assert_eq!(graph.num_nodes(), bcb_needs_counter.domain_size());
Self {
coverage_counters: CoverageCounters::with_num_bcbs(basic_coverage_blocks.num_nodes()),
basic_coverage_blocks,
counters: CoverageCounters::with_num_bcbs(graph.num_nodes()),
graph,
bcb_needs_counter,
}
}
@ -295,7 +282,7 @@ impl<'a> MakeBcbCounters<'a> {
// nodes within the loop are visited before visiting any nodes outside
// the loop. It also keeps track of which loop(s) the traversal is
// currently inside.
let mut traversal = TraverseCoverageGraphWithLoops::new(self.basic_coverage_blocks);
let mut traversal = TraverseCoverageGraphWithLoops::new(self.graph);
while let Some(bcb) = traversal.next() {
let _span = debug_span!("traversal", ?bcb).entered();
if self.bcb_needs_counter.contains(bcb) {
@ -322,25 +309,35 @@ impl<'a> MakeBcbCounters<'a> {
// We might also use that counter to compute one of the out-edge counters.
let node_counter = self.get_or_make_node_counter(from_bcb);
let successors = self.basic_coverage_blocks.successors[from_bcb].as_slice();
let successors = self.graph.successors[from_bcb].as_slice();
// If this node's out-edges won't sum to the node's counter,
// then there's no reason to create edge counters here.
if !self.basic_coverage_blocks[from_bcb].is_out_summable {
if !self.graph[from_bcb].is_out_summable {
return;
}
// Determine the set of out-edges that don't yet have edge counters.
let candidate_successors = self.basic_coverage_blocks.successors[from_bcb]
// When choosing which out-edge should be given a counter expression, ignore edges that
// already have counters, or could use the existing counter of their target node.
let out_edge_has_counter = |to_bcb| {
if self.counters.edge_counters.contains_key(&(from_bcb, to_bcb)) {
return true;
}
self.graph.sole_predecessor(to_bcb) == Some(from_bcb)
&& self.counters.node_counters[to_bcb].is_some()
};
// Determine the set of out-edges that could benefit from being given an expression.
let candidate_successors = self.graph.successors[from_bcb]
.iter()
.copied()
.filter(|&to_bcb| self.edge_has_no_counter(from_bcb, to_bcb))
.filter(|&to_bcb| !out_edge_has_counter(to_bcb))
.collect::<Vec<_>>();
debug!(?candidate_successors);
// If there are out-edges without counters, choose one to be given an expression
// (computed from this node and the other out-edges) instead of a physical counter.
let Some(expression_to_bcb) =
let Some(target_bcb) =
self.choose_out_edge_for_expression(traversal, &candidate_successors)
else {
return;
@ -353,43 +350,44 @@ impl<'a> MakeBcbCounters<'a> {
.iter()
.copied()
// Skip the chosen edge, since we'll calculate its count from this sum.
.filter(|&to_bcb| to_bcb != expression_to_bcb)
.filter(|&edge_target_bcb| edge_target_bcb != target_bcb)
.map(|to_bcb| self.get_or_make_edge_counter(from_bcb, to_bcb))
.collect::<Vec<_>>();
let Some(sum_of_all_other_out_edges) =
self.coverage_counters.make_sum(&other_out_edge_counters)
let Some(sum_of_all_other_out_edges) = self.counters.make_sum(&other_out_edge_counters)
else {
return;
};
// Now create an expression for the chosen edge, by taking the counter
// for its source node and subtracting the sum of its sibling out-edges.
let expression = self.coverage_counters.make_expression(
node_counter,
Op::Subtract,
sum_of_all_other_out_edges,
);
let expression =
self.counters.make_expression(node_counter, Op::Subtract, sum_of_all_other_out_edges);
debug!("{expression_to_bcb:?} gets an expression: {expression:?}");
if let Some(sole_pred) = self.basic_coverage_blocks.sole_predecessor(expression_to_bcb) {
// This edge normally wouldn't get its own counter, so attach the expression
// to its target node instead, so that `edge_has_no_counter` can see it.
assert_eq!(sole_pred, from_bcb);
self.coverage_counters.set_bcb_counter(expression_to_bcb, expression);
} else {
self.coverage_counters.set_bcb_edge_counter(from_bcb, expression_to_bcb, expression);
}
debug!("{target_bcb:?} gets an expression: {expression:?}");
self.counters.set_edge_counter(from_bcb, target_bcb, expression);
}
#[instrument(level = "debug", skip(self))]
fn get_or_make_node_counter(&mut self, bcb: BasicCoverageBlock) -> BcbCounter {
// If the BCB already has a counter, return it.
if let Some(counter_kind) = self.coverage_counters.bcb_counters[bcb] {
debug!("{bcb:?} already has a counter: {counter_kind:?}");
return counter_kind;
if let Some(counter) = self.counters.node_counters[bcb] {
debug!("{bcb:?} already has a counter: {counter:?}");
return counter;
}
let predecessors = self.basic_coverage_blocks.predecessors[bcb].as_slice();
let counter = self.make_node_counter_inner(bcb);
self.counters.set_node_counter(bcb, counter)
}
fn make_node_counter_inner(&mut self, bcb: BasicCoverageBlock) -> BcbCounter {
// If the node's sole in-edge already has a counter, use that.
if let Some(sole_pred) = self.graph.sole_predecessor(bcb)
&& let Some(&edge_counter) = self.counters.edge_counters.get(&(sole_pred, bcb))
{
return edge_counter;
}
let predecessors = self.graph.predecessors[bcb].as_slice();
// Handle cases where we can't compute a node's count from its in-edges:
// - START_BCB has no in-edges, so taking the sum would panic (or be wrong).
@ -398,7 +396,9 @@ impl<'a> MakeBcbCounters<'a> {
// leading to infinite recursion.
if predecessors.len() <= 1 || predecessors.contains(&bcb) {
debug!(?bcb, ?predecessors, "node has <=1 predecessors or is its own predecessor");
return self.coverage_counters.make_phys_node_counter(bcb);
let counter = self.counters.make_phys_node_counter(bcb);
debug!(?bcb, ?counter, "node gets a physical counter");
return counter;
}
// A BCB with multiple incoming edges can compute its count by ensuring that counters
@ -408,13 +408,11 @@ impl<'a> MakeBcbCounters<'a> {
.copied()
.map(|from_bcb| self.get_or_make_edge_counter(from_bcb, bcb))
.collect::<Vec<_>>();
let sum_of_in_edges: BcbCounter = self
.coverage_counters
.make_sum(&in_edge_counters)
.expect("there must be at least one in-edge");
let sum_of_in_edges: BcbCounter =
self.counters.make_sum(&in_edge_counters).expect("there must be at least one in-edge");
debug!("{bcb:?} gets a new counter (sum of predecessor counters): {sum_of_in_edges:?}");
self.coverage_counters.set_bcb_counter(bcb, sum_of_in_edges)
sum_of_in_edges
}
#[instrument(level = "debug", skip(self))]
@ -422,10 +420,25 @@ impl<'a> MakeBcbCounters<'a> {
&mut self,
from_bcb: BasicCoverageBlock,
to_bcb: BasicCoverageBlock,
) -> BcbCounter {
// If the edge already has a counter, return it.
if let Some(&counter) = self.counters.edge_counters.get(&(from_bcb, to_bcb)) {
debug!("Edge {from_bcb:?}->{to_bcb:?} already has a counter: {counter:?}");
return counter;
}
let counter = self.make_edge_counter_inner(from_bcb, to_bcb);
self.counters.set_edge_counter(from_bcb, to_bcb, counter)
}
fn make_edge_counter_inner(
&mut self,
from_bcb: BasicCoverageBlock,
to_bcb: BasicCoverageBlock,
) -> BcbCounter {
// If the target node has exactly one in-edge (i.e. this one), then just
// use the node's counter, since it will have the same value.
if let Some(sole_pred) = self.basic_coverage_blocks.sole_predecessor(to_bcb) {
if let Some(sole_pred) = self.graph.sole_predecessor(to_bcb) {
assert_eq!(sole_pred, from_bcb);
// This call must take care not to invoke `get_or_make_edge` for
// this edge, since that would result in infinite recursion!
@ -434,21 +447,15 @@ impl<'a> MakeBcbCounters<'a> {
// If the source node has exactly one out-edge (i.e. this one) and would have
// the same execution count as that edge, then just use the node's counter.
if let Some(simple_succ) = self.basic_coverage_blocks.simple_successor(from_bcb) {
if let Some(simple_succ) = self.graph.simple_successor(from_bcb) {
assert_eq!(simple_succ, to_bcb);
return self.get_or_make_node_counter(from_bcb);
}
// If the edge already has a counter, return it.
if let Some(&counter_kind) =
self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb))
{
debug!("Edge {from_bcb:?}->{to_bcb:?} already has a counter: {counter_kind:?}");
return counter_kind;
}
// Make a new counter to count this edge.
self.coverage_counters.make_phys_edge_counter(from_bcb, to_bcb)
let counter = self.counters.make_phys_edge_counter(from_bcb, to_bcb);
debug!(?from_bcb, ?to_bcb, ?counter, "edge gets a physical counter");
counter
}
/// Given a set of candidate out-edges (represented by their successor node),
@ -493,9 +500,9 @@ impl<'a> MakeBcbCounters<'a> {
for &target_bcb in candidate_successors {
// An edge is a reloop edge if its target dominates any BCB that has
// an edge back to the loop header. (Otherwise it's an exit edge.)
let is_reloop_edge = reloop_bcbs.iter().any(|&reloop_bcb| {
self.basic_coverage_blocks.dominates(target_bcb, reloop_bcb)
});
let is_reloop_edge = reloop_bcbs
.iter()
.any(|&reloop_bcb| self.graph.dominates(target_bcb, reloop_bcb));
if is_reloop_edge {
// We found a good out-edge to be given an expression.
return Some(target_bcb);
@ -508,21 +515,4 @@ impl<'a> MakeBcbCounters<'a> {
None
}
#[inline]
fn edge_has_no_counter(
&self,
from_bcb: BasicCoverageBlock,
to_bcb: BasicCoverageBlock,
) -> bool {
let edge_counter =
if let Some(sole_pred) = self.basic_coverage_blocks.sole_predecessor(to_bcb) {
assert_eq!(sole_pred, from_bcb);
self.coverage_counters.bcb_counters[to_bcb]
} else {
self.coverage_counters.bcb_edge_counters.get(&(from_bcb, to_bcb)).copied()
};
edge_counter.is_none()
}
}

View file

@ -35,7 +35,7 @@ The following target names are defined:
## Building the target
The target can be built by enabled in the `rustc` build:
The target can be built by enabling it in the `rustc` build:
```toml
[build]

View file

@ -13,5 +13,5 @@ max_line_length = 100
[*.md]
indent_size = 2
[*.{yml, yaml}]
[*.{yml,yaml}]
indent_size = 2

View file

@ -104,11 +104,11 @@ jobs:
if: matrix.os == 'ubuntu-latest'
run: target/${{ matrix.target }}/debug/rust-analyzer analysis-stats .
- name: Run analysis-stats on rust std library
- name: Run analysis-stats on the rust standard libraries
if: matrix.os == 'ubuntu-latest'
env:
RUSTC_BOOTSTRAP: 1
run: target/${{ matrix.target }}/debug/rust-analyzer analysis-stats --with-deps $(rustc --print sysroot)/lib/rustlib/src/rust/library/std
RUSTC_BOOTSTRAP: 1
run: target/${{ matrix.target }}/debug/rust-analyzer analysis-stats --with-deps --no-sysroot --no-test $(rustc --print sysroot)/lib/rustlib/src/rust/library/
- name: clippy
if: matrix.os == 'windows-latest'

View file

@ -73,7 +73,7 @@ dependencies = [
"intern",
"la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"lz4_flex",
"rustc-hash",
"rustc-hash 2.0.0",
"salsa",
"semver",
"span",
@ -161,7 +161,7 @@ dependencies = [
"expect-test",
"intern",
"oorandom",
"rustc-hash",
"rustc-hash 2.0.0",
"syntax",
"syntax-bridge",
"tt",
@ -216,7 +216,7 @@ dependencies = [
"chalk-derive",
"chalk-ir",
"chalk-solve",
"rustc-hash",
"rustc-hash 1.1.0",
"tracing",
]
@ -232,7 +232,7 @@ dependencies = [
"indexmap",
"itertools",
"petgraph",
"rustc-hash",
"rustc-hash 1.1.0",
"tracing",
]
@ -513,7 +513,7 @@ dependencies = [
"hir-ty",
"intern",
"itertools",
"rustc-hash",
"rustc-hash 2.0.0",
"smallvec",
"span",
"stdx",
@ -547,7 +547,7 @@ dependencies = [
"mbe",
"ra-ap-rustc_abi",
"ra-ap-rustc_parse_format",
"rustc-hash",
"rustc-hash 2.0.0",
"rustc_apfloat",
"smallvec",
"span",
@ -577,7 +577,7 @@ dependencies = [
"limit",
"mbe",
"parser",
"rustc-hash",
"rustc-hash 2.0.0",
"smallvec",
"span",
"stdx",
@ -616,7 +616,7 @@ dependencies = [
"ra-ap-rustc_abi",
"ra-ap-rustc_index",
"ra-ap-rustc_pattern_analysis",
"rustc-hash",
"rustc-hash 2.0.0",
"rustc_apfloat",
"scoped-tls",
"smallvec",
@ -731,13 +731,13 @@ dependencies = [
"indexmap",
"itertools",
"limit",
"line-index 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"line-index 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"memchr",
"nohash-hasher",
"parser",
"profile",
"rayon",
"rustc-hash",
"rustc-hash 2.0.0",
"span",
"stdx",
"syntax",
@ -834,7 +834,7 @@ version = "0.0.0"
dependencies = [
"dashmap",
"hashbrown",
"rustc-hash",
"rustc-hash 2.0.0",
"sptr",
"triomphe",
]
@ -939,7 +939,7 @@ version = "0.0.0"
[[package]]
name = "line-index"
version = "0.1.1"
version = "0.1.2"
dependencies = [
"nohash-hasher",
"oorandom",
@ -948,9 +948,9 @@ dependencies = [
[[package]]
name = "line-index"
version = "0.1.1"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67d61795376ae2683928c218fda7d7d7db136fd38c06b7552904667f0d55580a"
checksum = "3e27e0ed5a392a7f5ba0b3808a2afccff16c64933312c84b57618b49d1209bd2"
dependencies = [
"nohash-hasher",
"text-size",
@ -1051,7 +1051,7 @@ dependencies = [
"intern",
"parser",
"ra-ap-rustc_lexer",
"rustc-hash",
"rustc-hash 2.0.0",
"smallvec",
"span",
"stdx",
@ -1345,7 +1345,7 @@ dependencies = [
"indexmap",
"intern",
"paths",
"rustc-hash",
"rustc-hash 2.0.0",
"serde",
"serde_json",
"span",
@ -1435,7 +1435,7 @@ dependencies = [
"itertools",
"la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"paths",
"rustc-hash",
"rustc-hash 2.0.0",
"semver",
"serde",
"serde_json",
@ -1497,9 +1497,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_abi"
version = "0.71.0"
version = "0.73.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6999d098000b98415939f13158dac78cb3eeeb7b0c073847f3e4b623866e27c"
checksum = "879ece0781e3c1cb670b9f29775c81a43a16db789d1296fad6bc5c74065b2fac"
dependencies = [
"bitflags 2.6.0",
"ra-ap-rustc_index",
@ -1508,9 +1508,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_index"
version = "0.71.0"
version = "0.73.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae9fb312d942817dab10790881f555928c1f6a11a85186e8e573ad4a86c7d3be"
checksum = "6910087ff89bb9f3db114bfcd86b5139042731fe7278d3ff4ceaa69a140154a7"
dependencies = [
"arrayvec",
"ra-ap-rustc_index_macros",
@ -1519,9 +1519,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_index_macros"
version = "0.71.0"
version = "0.73.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "766e3990eb1066a06deefc561b5a01b32ca5c9211feea31cbf4ed50611519872"
checksum = "3b6f7bd12b678fbb37444ba77f3b0cfc13b7394a6dc7b0c799491fc9df0a9997"
dependencies = [
"proc-macro2",
"quote",
@ -1530,9 +1530,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_lexer"
version = "0.71.0"
version = "0.73.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4afa98eb7889c137d5a3f1cd189089e16da04d1e4837d358a67aa3dab10ffbe"
checksum = "119bc05b5b6bc3e7f5b67ce8b8080e185da94bd83c447f91b6b3f3ecf60cbab1"
dependencies = [
"unicode-properties",
"unicode-xid",
@ -1540,9 +1540,9 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_parse_format"
version = "0.71.0"
version = "0.73.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9234c96ffb0565286790407fb7eb7f55ebf69267de4db382fdec0a17f14b0e2"
checksum = "70ed6150ae71d905c064dc88d7824ebb0fa81083f45d7477cba7b57176f2f635"
dependencies = [
"ra-ap-rustc_index",
"ra-ap-rustc_lexer",
@ -1550,12 +1550,12 @@ dependencies = [
[[package]]
name = "ra-ap-rustc_pattern_analysis"
version = "0.71.0"
version = "0.73.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "273d5f72926a58c7eea27aebc898d1d5b32d23d2342f692a94a2cf8746aa4a2f"
checksum = "6e830862a0ec85fce211d34735315686bb8d6a12d418d6d735fb534aa1cd3293"
dependencies = [
"ra-ap-rustc_index",
"rustc-hash",
"rustc-hash 2.0.0",
"rustc_apfloat",
"smallvec",
"tracing",
@ -1640,7 +1640,7 @@ dependencies = [
"countme",
"hashbrown",
"memoffset",
"rustc-hash",
"rustc-hash 1.1.0",
"text-size",
]
@ -1680,7 +1680,7 @@ dependencies = [
"profile",
"project-model",
"rayon",
"rustc-hash",
"rustc-hash 2.0.0",
"scip",
"semver",
"serde",
@ -1717,6 +1717,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc-hash"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152"
[[package]]
name = "rustc_apfloat"
version = "0.2.1+llvm-462a31f5a5ab"
@ -1746,7 +1752,7 @@ dependencies = [
"oorandom",
"parking_lot",
"rand",
"rustc-hash",
"rustc-hash 2.0.0",
"salsa-macros",
"smallvec",
"tracing",
@ -1898,7 +1904,7 @@ version = "0.0.0"
dependencies = [
"hashbrown",
"la-arena 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-hash",
"rustc-hash 2.0.0",
"salsa",
"stdx",
"syntax",
@ -1967,7 +1973,7 @@ dependencies = [
"ra-ap-rustc_lexer",
"rayon",
"rowan",
"rustc-hash",
"rustc-hash 2.0.0",
"rustc_apfloat",
"smol_str",
"stdx",
@ -1983,7 +1989,7 @@ version = "0.0.0"
dependencies = [
"intern",
"parser",
"rustc-hash",
"rustc-hash 2.0.0",
"span",
"stdx",
"syntax",
@ -2000,7 +2006,7 @@ dependencies = [
"cfg",
"hir-expand",
"intern",
"rustc-hash",
"rustc-hash 2.0.0",
"span",
"stdx",
"test-utils",
@ -2014,7 +2020,7 @@ dependencies = [
"dissimilar",
"paths",
"profile",
"rustc-hash",
"rustc-hash 2.0.0",
"stdx",
"text-size",
"tracing",
@ -2361,7 +2367,7 @@ dependencies = [
"indexmap",
"nohash-hasher",
"paths",
"rustc-hash",
"rustc-hash 2.0.0",
"stdx",
"tracing",
]
@ -2374,7 +2380,7 @@ dependencies = [
"notify",
"paths",
"rayon",
"rustc-hash",
"rustc-hash 2.0.0",
"stdx",
"tracing",
"vfs",

View file

@ -85,18 +85,18 @@ tt = { path = "./crates/tt", version = "0.0.0" }
vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
vfs = { path = "./crates/vfs", version = "0.0.0" }
ra-ap-rustc_lexer = { version = "0.71.0", default-features = false }
ra-ap-rustc_parse_format = { version = "0.71.0", default-features = false }
ra-ap-rustc_index = { version = "0.71.0", default-features = false }
ra-ap-rustc_abi = { version = "0.71.0", default-features = false }
ra-ap-rustc_pattern_analysis = { version = "0.71.0", default-features = false }
ra-ap-rustc_lexer = { version = "0.73", default-features = false }
ra-ap-rustc_parse_format = { version = "0.73", default-features = false }
ra-ap-rustc_index = { version = "0.73", default-features = false }
ra-ap-rustc_abi = { version = "0.73", default-features = false }
ra-ap-rustc_pattern_analysis = { version = "0.73", default-features = false }
# local crates that aren't published to crates.io. These should not have versions.
test-fixture = { path = "./crates/test-fixture" }
test-utils = { path = "./crates/test-utils" }
# In-tree crates that are published separately and follow semver. See lib/README.md
line-index = { version = "0.1.1" }
line-index = { version = "0.1.2" }
la-arena = { version = "0.3.1" }
lsp-server = { version = "0.7.6" }
@ -136,7 +136,7 @@ process-wrap = { version = "8.0.2", features = ["std"] }
pulldown-cmark-to-cmark = "10.0.4"
pulldown-cmark = { version = "0.9.0", default-features = false }
rayon = "1.8.0"
rustc-hash = "1.1.0"
rustc-hash = "2.0.0"
semver = "1.0.14"
serde = { version = "1.0.192", features = ["derive"] }
serde_json = "1.0.108"

View file

@ -148,6 +148,10 @@ impl FunctionData {
self.flags.contains(FnFlags::HAS_UNSAFE_KW)
}
pub fn is_safe(&self) -> bool {
self.flags.contains(FnFlags::HAS_SAFE_KW)
}
pub fn is_varargs(&self) -> bool {
self.flags.contains(FnFlags::IS_VARARGS)
}
@ -567,6 +571,8 @@ pub struct StaticData {
pub visibility: RawVisibility,
pub mutable: bool,
pub is_extern: bool,
pub has_safe_kw: bool,
pub has_unsafe_kw: bool,
}
impl StaticData {
@ -581,6 +587,8 @@ impl StaticData {
visibility: item_tree[statik.visibility].clone(),
mutable: statik.mutable,
is_extern: matches!(loc.container, ItemContainerId::ExternBlockId(_)),
has_safe_kw: statik.has_safe_kw,
has_unsafe_kw: statik.has_unsafe_kw,
})
}
}

View file

@ -754,6 +754,7 @@ bitflags::bitflags! {
const HAS_ASYNC_KW = 1 << 4;
const HAS_UNSAFE_KW = 1 << 5;
const IS_VARARGS = 1 << 6;
const HAS_SAFE_KW = 1 << 7;
}
}
@ -822,7 +823,10 @@ pub struct Const {
pub struct Static {
pub name: Name,
pub visibility: RawVisibilityId,
// TODO: use bitflags when we have more flags
pub mutable: bool,
pub has_safe_kw: bool,
pub has_unsafe_kw: bool,
pub type_ref: Interned<TypeRef>,
pub ast_id: FileAstId<ast::Static>,
}

View file

@ -440,6 +440,9 @@ impl<'a> Ctx<'a> {
if func.unsafe_token().is_some() {
flags |= FnFlags::HAS_UNSAFE_KW;
}
if func.safe_token().is_some() {
flags |= FnFlags::HAS_SAFE_KW;
}
if has_var_args {
flags |= FnFlags::IS_VARARGS;
}
@ -484,8 +487,11 @@ impl<'a> Ctx<'a> {
let type_ref = self.lower_type_ref_opt(static_.ty());
let visibility = self.lower_visibility(static_);
let mutable = static_.mut_token().is_some();
let has_safe_kw = static_.safe_token().is_some();
let has_unsafe_kw = static_.unsafe_token().is_some();
let ast_id = self.source_ast_id_map.ast_id(static_);
let res = Static { name, visibility, mutable, type_ref, ast_id };
let res =
Static { name, visibility, mutable, type_ref, ast_id, has_safe_kw, has_unsafe_kw };
Some(id(self.data().statics.alloc(res)))
}

View file

@ -278,6 +278,9 @@ impl Printer<'_> {
if flags.contains(FnFlags::HAS_UNSAFE_KW) {
w!(self, "unsafe ");
}
if flags.contains(FnFlags::HAS_SAFE_KW) {
w!(self, "safe ");
}
if let Some(abi) = abi {
w!(self, "extern \"{}\" ", abi);
}
@ -379,9 +382,23 @@ impl Printer<'_> {
wln!(self, " = _;");
}
ModItem::Static(it) => {
let Static { name, visibility, mutable, type_ref, ast_id } = &self.tree[it];
let Static {
name,
visibility,
mutable,
type_ref,
ast_id,
has_safe_kw,
has_unsafe_kw,
} = &self.tree[it];
self.print_ast_id(ast_id.erase());
self.print_visibility(*visibility);
if *has_safe_kw {
w!(self, "safe ");
}
if *has_unsafe_kw {
w!(self, "unsafe ");
}
w!(self, "static ");
if *mutable {
w!(self, "mut ");

View file

@ -139,13 +139,11 @@ impl Visibility {
let def_map_block = def_map.block_id();
loop {
match (to_module.block, def_map_block) {
// to_module is not a block, so there is no parent def map to use
// `to_module` is not a block, so there is no parent def map to use.
(None, _) => (),
// `to_module` is at `def_map`'s block, no need to move further.
(Some(a), Some(b)) if a == b => {
cov_mark::hit!(is_visible_from_same_block_def_map);
if let Some(parent) = def_map.parent() {
to_module = parent;
}
}
_ => {
if let Some(parent) = to_module.def_map(db).parent() {

View file

@ -5,19 +5,20 @@ use cfg::CfgExpr;
use either::Either;
use intern::{sym, Symbol};
use mbe::{expect_fragment, DelimiterKind};
use span::{Edition, EditionedFileId, Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
use span::{Edition, EditionedFileId, Span};
use stdx::format_to;
use syntax::{
format_smolstr,
unescape::{unescape_byte, unescape_char, unescape_unicode, Mode},
};
use syntax_bridge::parse_to_token_tree;
use syntax_bridge::syntax_node_to_token_tree;
use crate::{
builtin::quote::{dollar_crate, quote},
db::ExpandDatabase,
hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt},
name,
span_map::SpanMap,
tt::{self, DelimSpan},
ExpandError, ExpandResult, HirFileIdExt, Lookup as _, MacroCallId,
};
@ -739,18 +740,14 @@ fn include_expand(
return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e)
}
};
match parse_to_token_tree(
file_id.edition(),
SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID },
SyntaxContextId::ROOT,
&db.file_text(file_id.file_id()),
) {
Some(it) => ExpandResult::ok(it),
None => ExpandResult::new(
tt::Subtree::empty(DelimSpan { open: span, close: span }),
ExpandError::other(span, "failed to parse included file"),
),
}
let span_map = db.real_span_map(file_id);
// FIXME: Parse errors
ExpandResult::ok(syntax_node_to_token_tree(
&db.parse(file_id).syntax_node(),
SpanMap::RealSpanMap(span_map),
span,
syntax_bridge::DocCommentDesugarMode::ProcMacro,
))
}
pub fn include_input_to_file_id(

View file

@ -35,7 +35,7 @@ type MacroArgResult = (Arc<tt::Subtree>, SyntaxFixupUndoInfo, Span);
/// an error will be emitted.
///
/// Actual max for `analysis-stats .` at some point: 30672.
static TOKEN_LIMIT: Limit = Limit::new(1_048_576);
static TOKEN_LIMIT: Limit = Limit::new(2_097_152);
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum TokenExpander {

View file

@ -95,7 +95,8 @@ fn check_answer(ra_fixture: &str, check: impl FnOnce(&[u8], &MemoryMap)) {
fn pretty_print_err(e: ConstEvalError, db: TestDB) -> String {
let mut err = String::new();
let span_formatter = |file, range| format!("{file:?} {range:?}");
let edition = db.crate_graph()[db.test_crate()].edition;
let edition =
db.crate_graph()[*db.crate_graph().crates_in_topological_order().last().unwrap()].edition;
match e {
ConstEvalError::MirLowerError(e) => e.pretty_print(&mut err, &db, span_formatter, edition),
ConstEvalError::MirEvalError(e) => e.pretty_print(&mut err, &db, span_formatter, edition),
@ -2896,7 +2897,7 @@ fn recursive_adt() {
{
const VARIANT_TAG_TREE: TagTree = TagTree::Choice(
&[
TagTree::Leaf,
TAG_TREE,
],
);
VARIANT_TAG_TREE
@ -2905,6 +2906,6 @@ fn recursive_adt() {
TAG_TREE
};
"#,
|e| matches!(e, ConstEvalError::MirEvalError(MirEvalError::StackOverflow)),
|e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::Loop)),
);
}

View file

@ -89,7 +89,7 @@ fn walk_unsafe(
let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path);
if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial {
let static_data = db.static_data(id);
if static_data.mutable || static_data.is_extern {
if static_data.mutable || (static_data.is_extern && !static_data.has_safe_kw) {
unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block });
}
}

View file

@ -917,9 +917,19 @@ impl<'a> InferenceTable<'a> {
/// Check if given type is `Sized` or not
pub(crate) fn is_sized(&mut self, ty: &Ty) -> bool {
// Early return for some obvious types
if matches!(ty.kind(Interner), TyKind::Scalar(..) | TyKind::Ref(..) | TyKind::Raw(..)) {
if matches!(
ty.kind(Interner),
TyKind::Scalar(..)
| TyKind::Ref(..)
| TyKind::Raw(..)
| TyKind::Never
| TyKind::FnDef(..)
| TyKind::Array(..)
| TyKind::Function(_)
) {
return true;
}
if let Some((AdtId::StructId(id), subst)) = ty.as_adt() {
let struct_data = self.db.struct_data(id);
if let Some((last_field, _)) = struct_data.variant_data.fields().iter().last() {

View file

@ -72,18 +72,15 @@ pub type Variants = hir_def::layout::Variants<RustcFieldIdx, RustcEnumVariantIdx
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum LayoutError {
// FIXME: Remove variants that duplicate LayoutCalculatorError's variants after sync
// FIXME: Remove more variants once they get added to LayoutCalculatorError
BadCalc(LayoutCalculatorError<()>),
EmptyUnion,
HasErrorConst,
HasErrorType,
HasPlaceholder,
InvalidSimdType,
NotImplemented,
RecursiveTypeWithoutIndirection,
SizeOverflow,
TargetLayoutNotAvailable,
UnexpectedUnsized,
Unknown,
UserReprTooSmall,
}
@ -93,7 +90,6 @@ impl fmt::Display for LayoutError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
LayoutError::BadCalc(err) => err.fallback_fmt(f),
LayoutError::EmptyUnion => write!(f, "type is an union with no fields"),
LayoutError::HasErrorConst => write!(f, "type contains an unevaluatable const"),
LayoutError::HasErrorType => write!(f, "type contains an error"),
LayoutError::HasPlaceholder => write!(f, "type contains placeholders"),
@ -102,11 +98,7 @@ impl fmt::Display for LayoutError {
LayoutError::RecursiveTypeWithoutIndirection => {
write!(f, "recursive type without indirection")
}
LayoutError::SizeOverflow => write!(f, "size overflow"),
LayoutError::TargetLayoutNotAvailable => write!(f, "target layout not available"),
LayoutError::UnexpectedUnsized => {
write!(f, "an unsized type was found where a sized type was expected")
}
LayoutError::Unknown => write!(f, "unknown"),
LayoutError::UserReprTooSmall => {
write!(f, "the `#[repr]` hint is too small to hold the discriminants of the enum")
@ -181,7 +173,10 @@ fn layout_of_simd_ty(
};
// Compute the size and alignment of the vector:
let size = e_ly.size.checked_mul(e_len, dl).ok_or(LayoutError::SizeOverflow)?;
let size = e_ly
.size
.checked_mul(e_len, dl)
.ok_or(LayoutError::BadCalc(LayoutCalculatorError::SizeOverflow))?;
let align = dl.vector_align(size);
let size = size.align_to(align.abi);
@ -294,7 +289,10 @@ pub fn layout_of_ty_query(
TyKind::Array(element, count) => {
let count = try_const_usize(db, count).ok_or(LayoutError::HasErrorConst)? as u64;
let element = db.layout_of_ty(element.clone(), trait_env)?;
let size = element.size.checked_mul(count, dl).ok_or(LayoutError::SizeOverflow)?;
let size = element
.size
.checked_mul(count, dl)
.ok_or(LayoutError::BadCalc(LayoutCalculatorError::SizeOverflow))?;
let abi = if count != 0 && matches!(element.abi, Abi::Uninhabited) {
Abi::Uninhabited

View file

@ -51,10 +51,7 @@ mod test_db;
#[cfg(test)]
mod tests;
use std::{
collections::hash_map::Entry,
hash::{BuildHasherDefault, Hash},
};
use std::hash::Hash;
use base_db::ra_salsa::InternValueTrivial;
use chalk_ir::{
@ -65,10 +62,11 @@ use chalk_ir::{
use either::Either;
use hir_def::{hir::ExprId, type_ref::Rawness, CallableDefId, GeneralConstId, TypeOrConstParamId};
use hir_expand::name::Name;
use indexmap::{map::Entry, IndexMap};
use intern::{sym, Symbol};
use la_arena::{Arena, Idx};
use mir::{MirEvalError, VTableMap};
use rustc_hash::{FxHashMap, FxHashSet};
use rustc_hash::{FxBuildHasher, FxHashMap, FxHashSet};
use span::Edition;
use syntax::ast::{make, ConstArg};
use traits::FnTrait;
@ -199,7 +197,7 @@ pub enum MemoryMap {
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct ComplexMemoryMap {
memory: FxHashMap<usize, Box<[u8]>>,
memory: IndexMap<usize, Box<[u8]>, FxBuildHasher>,
vtable: VTableMap,
}
@ -245,7 +243,7 @@ impl MemoryMap {
match self {
MemoryMap::Empty => Ok(Default::default()),
MemoryMap::Simple(m) => transform((&0, m)).map(|(addr, val)| {
let mut map = FxHashMap::with_capacity_and_hasher(1, BuildHasherDefault::default());
let mut map = FxHashMap::with_capacity_and_hasher(1, rustc_hash::FxBuildHasher);
map.insert(addr, val);
map
}),

View file

@ -257,10 +257,12 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool {
return true;
}
match func.lookup(db.upcast()).container {
let loc = func.lookup(db.upcast());
match loc.container {
hir_def::ItemContainerId::ExternBlockId(block) => {
// Function in an `extern` block are always unsafe to call, except when it has
// `"rust-intrinsic"` ABI there are a few exceptions.
// Function in an `extern` block are always unsafe to call, except when
// it is marked as `safe` or it has `"rust-intrinsic"` ABI there are a
// few exceptions.
let id = block.lookup(db.upcast()).id;
let is_intrinsic =
@ -270,8 +272,8 @@ pub fn is_fn_unsafe_to_call(db: &dyn HirDatabase, func: FunctionId) -> bool {
// Intrinsics are unsafe unless they have the rustc_safe_intrinsic attribute
!data.attrs.by_key(&sym::rustc_safe_intrinsic).exists()
} else {
// Extern items are always unsafe
true
// Extern items without `safe` modifier are always unsafe
!data.is_safe()
}
}
_ => false,

View file

@ -892,29 +892,8 @@ impl<'db> SemanticsImpl<'db> {
f: &mut dyn FnMut(InFile<SyntaxToken>, SyntaxContextId) -> ControlFlow<T>,
) -> Option<T> {
let _p = tracing::info_span!("descend_into_macros_impl").entered();
let (sa, span, file_id) = token
.parent()
.and_then(|parent| {
self.analyze_impl(InRealFile::new(file_id, &parent).into(), None, false)
})
.and_then(|sa| {
let file_id = sa.file_id.file_id()?;
Some((
sa,
self.db.real_span_map(file_id).span_for_range(token.text_range()),
HirFileId::from(file_id),
))
})?;
let mut m_cache = self.macro_call_cache.borrow_mut();
let def_map = sa.resolver.def_map();
// A stack of tokens to process, along with the file they came from
// These are tracked to know which macro calls we still have to look into
// the tokens themselves aren't that interesting as the span that is being used to map
// things down never changes.
let mut stack: Vec<(_, SmallVec<[_; 2]>)> =
vec![(file_id, smallvec![(token, SyntaxContextId::ROOT)])];
let span = self.db.real_span_map(file_id).span_for_range(token.text_range());
// Process the expansion of a call, pushing all tokens with our span in the expansion back onto our stack
let process_expansion_for_token = |stack: &mut Vec<_>, macro_file| {
@ -926,7 +905,6 @@ impl<'db> SemanticsImpl<'db> {
.map(SmallVec::<[_; 2]>::from_iter),
)
})?;
// we have found a mapping for the token if the vec is non-empty
let res = mapped_tokens.is_empty().not().then_some(());
// requeue the tokens we got from mapping our current token down
@ -934,6 +912,33 @@ impl<'db> SemanticsImpl<'db> {
res
};
// A stack of tokens to process, along with the file they came from
// These are tracked to know which macro calls we still have to look into
// the tokens themselves aren't that interesting as the span that is being used to map
// things down never changes.
let mut stack: Vec<(_, SmallVec<[_; 2]>)> = vec![];
let include = self.s2d_cache.borrow_mut().get_or_insert_include_for(self.db, file_id);
match include {
Some(include) => {
// include! inputs are always from real files, so they only need to be handled once upfront
process_expansion_for_token(&mut stack, include)?;
}
None => {
stack.push((file_id.into(), smallvec![(token, SyntaxContextId::ROOT)]));
}
}
let (file_id, tokens) = stack.first()?;
// make sure we pick the token in the expanded include if we encountered an include,
// otherwise we'll get the wrong semantics
let sa =
tokens.first()?.0.parent().and_then(|parent| {
self.analyze_impl(InFile::new(*file_id, &parent), None, false)
})?;
let mut m_cache = self.macro_call_cache.borrow_mut();
let def_map = sa.resolver.def_map();
// Filters out all tokens that contain the given range (usually the macro call), any such
// token is redundant as the corresponding macro call has already been processed
let filter_duplicates = |tokens: &mut SmallVec<_>, range: TextRange| {
@ -1011,6 +1016,7 @@ impl<'db> SemanticsImpl<'db> {
) {
call.as_macro_file()
} else {
// FIXME: This is wrong, the SourceAnalyzer might be invalid here
sa.expand(self.db, mcall.as_ref())?
};
m_cache.insert(mcall, it);

View file

@ -104,7 +104,7 @@ use hir_expand::{
};
use rustc_hash::FxHashMap;
use smallvec::SmallVec;
use span::{FileId, MacroFileId};
use span::{EditionedFileId, FileId, MacroFileId};
use stdx::impl_from;
use syntax::{
ast::{self, HasName},
@ -118,9 +118,27 @@ pub(super) struct SourceToDefCache {
pub(super) dynmap_cache: FxHashMap<(ChildContainer, HirFileId), DynMap>,
expansion_info_cache: FxHashMap<MacroFileId, ExpansionInfo>,
pub(super) file_to_def_cache: FxHashMap<FileId, SmallVec<[ModuleId; 1]>>,
pub(super) included_file_cache: FxHashMap<EditionedFileId, Option<MacroFileId>>,
}
impl SourceToDefCache {
pub(super) fn get_or_insert_include_for(
&mut self,
db: &dyn HirDatabase,
file: EditionedFileId,
) -> Option<MacroFileId> {
if let Some(&m) = self.included_file_cache.get(&file) {
return m;
}
self.included_file_cache.insert(file, None);
for &crate_id in db.relevant_crates(file.into()).iter() {
db.include_macro_invoc(crate_id).iter().for_each(|&(macro_call_id, file_id)| {
self.included_file_cache.insert(file_id, Some(MacroFileId { macro_call_id }));
});
}
self.included_file_cache.get(&file).copied().flatten()
}
pub(super) fn get_or_insert_expansion(
&mut self,
sema: &SemanticsImpl<'_>,
@ -163,9 +181,13 @@ impl SourceToDefCtx<'_, '_> {
.include_macro_invoc(crate_id)
.iter()
.filter(|&&(_, file_id)| file_id == file)
.flat_map(|(call, _)| {
.flat_map(|&(macro_call_id, file_id)| {
self.cache
.included_file_cache
.insert(file_id, Some(MacroFileId { macro_call_id }));
modules(
call.lookup(self.db.upcast())
macro_call_id
.lookup(self.db.upcast())
.kind
.file_id()
.original_file(self.db.upcast())

View file

@ -1095,6 +1095,7 @@ fn main() {
#[test]
fn field_enum_cross_file() {
// FIXME: The import is missing
check_assist(
bool_to_enum,
r#"
@ -1132,7 +1133,7 @@ fn foo() {
}
//- /main.rs
use foo::{Bool, Foo};
use foo::Foo;
mod foo;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -223,9 +223,9 @@ mod handlers {
mod unnecessary_async;
mod unqualify_method_call;
mod unwrap_block;
mod unwrap_result_return_type;
mod unwrap_return_type;
mod unwrap_tuple;
mod wrap_return_type_in_result;
mod wrap_return_type;
mod wrap_unwrap_cfg_attr;
pub(crate) fn all() -> &'static [Handler] {
@ -355,10 +355,10 @@ mod handlers {
unmerge_use::unmerge_use,
unnecessary_async::unnecessary_async,
unwrap_block::unwrap_block,
unwrap_result_return_type::unwrap_result_return_type,
unwrap_return_type::unwrap_return_type,
unwrap_tuple::unwrap_tuple,
unqualify_method_call::unqualify_method_call,
wrap_return_type_in_result::wrap_return_type_in_result,
wrap_return_type::wrap_return_type,
wrap_unwrap_cfg_attr::wrap_unwrap_cfg_attr,
// These are manually sorted for better priorities. By default,

View file

@ -3264,6 +3264,20 @@ fn foo() {
)
}
#[test]
fn doctest_unwrap_option_return_type() {
check_doc_test(
"unwrap_option_return_type",
r#####"
//- minicore: option
fn foo() -> Option<i32>$0 { Some(42i32) }
"#####,
r#####"
fn foo() -> i32 { 42i32 }
"#####,
)
}
#[test]
fn doctest_unwrap_result_return_type() {
check_doc_test(
@ -3297,6 +3311,20 @@ fn main() {
)
}
#[test]
fn doctest_wrap_return_type_in_option() {
check_doc_test(
"wrap_return_type_in_option",
r#####"
//- minicore: option
fn foo() -> i32$0 { 42i32 }
"#####,
r#####"
fn foo() -> Option<i32> { Some(42i32) }
"#####,
)
}
#[test]
fn doctest_wrap_return_type_in_result() {
check_doc_test(

View file

@ -29,7 +29,9 @@ pub(crate) fn complete_item_list(
kind: &ItemListKind,
) {
let _p = tracing::info_span!("complete_item_list").entered();
if path_ctx.is_trivial_path() {
// We handle completions for trait-impls in [`item_list::trait_impl`]
if path_ctx.is_trivial_path() && !matches!(kind, ItemListKind::TraitImpl(_)) {
add_keywords(acc, ctx, Some(kind));
}
@ -75,73 +77,95 @@ fn add_keywords(acc: &mut Completions, ctx: &CompletionContext<'_>, kind: Option
let in_item_list = matches!(kind, Some(ItemListKind::SourceFile | ItemListKind::Module) | None);
let in_assoc_non_trait_impl = matches!(kind, Some(ItemListKind::Impl | ItemListKind::Trait));
let in_extern_block = matches!(kind, Some(ItemListKind::ExternBlock));
let in_extern_block = matches!(kind, Some(ItemListKind::ExternBlock { .. }));
let in_unsafe_extern_block =
matches!(kind, Some(ItemListKind::ExternBlock { is_unsafe: true }));
let in_trait = matches!(kind, Some(ItemListKind::Trait));
let in_trait_impl = matches!(kind, Some(ItemListKind::TraitImpl(_)));
let in_inherent_impl = matches!(kind, Some(ItemListKind::Impl));
let no_vis_qualifiers = ctx.qualifier_ctx.vis_node.is_none();
let in_block = kind.is_none();
let missing_qualifiers = [
ctx.qualifier_ctx.unsafe_tok.is_none().then_some(("unsafe", "unsafe $0")),
ctx.qualifier_ctx.async_tok.is_none().then_some(("async", "async $0")),
];
let no_vis_qualifiers = ctx.qualifier_ctx.vis_node.is_none();
let has_unsafe_kw = ctx.qualifier_ctx.unsafe_tok.is_some();
let has_async_kw = ctx.qualifier_ctx.async_tok.is_some();
let has_safe_kw = ctx.qualifier_ctx.safe_tok.is_some();
if !in_trait_impl {
// handle qualifier tokens
if missing_qualifiers.iter().any(Option::is_none) {
// only complete missing qualifiers
missing_qualifiers.iter().filter_map(|x| *x).for_each(|(kw, snippet)| {
add_keyword(kw, snippet);
});
// Some keywords are invalid after non-vis qualifiers, so we handle them first.
if (has_unsafe_kw || has_safe_kw) && in_extern_block {
add_keyword("fn", "fn $1($2);");
add_keyword("static", "static $1: $2;");
return;
}
if in_item_list || in_assoc_non_trait_impl {
add_keyword("fn", "fn $1($2) {\n $0\n}");
}
if ctx.qualifier_ctx.unsafe_tok.is_some() && in_item_list {
add_keyword("trait", "trait $1 {\n $0\n}");
if no_vis_qualifiers {
add_keyword("impl", "impl $1 {\n $0\n}");
}
}
return;
if has_unsafe_kw || has_async_kw {
if !has_unsafe_kw {
add_keyword("unsafe", "unsafe $0");
}
if !has_async_kw {
add_keyword("async", "async $0");
}
if in_item_list {
add_keyword("enum", "enum $1 {\n $0\n}");
add_keyword("mod", "mod $0");
add_keyword("static", "static $0");
add_keyword("struct", "struct $0");
if in_item_list || in_assoc_non_trait_impl {
add_keyword("fn", "fn $1($2) {\n $0\n}");
}
if has_unsafe_kw && in_item_list {
add_keyword("trait", "trait $1 {\n $0\n}");
add_keyword("union", "union $1 {\n $0\n}");
add_keyword("use", "use $0");
if no_vis_qualifiers {
add_keyword("impl", "impl $1 {\n $0\n}");
}
}
if !in_trait && !in_block && no_vis_qualifiers {
add_keyword("pub(crate)", "pub(crate) $0");
add_keyword("pub(super)", "pub(super) $0");
add_keyword("pub", "pub $0");
if !has_async_kw && no_vis_qualifiers && in_item_list {
add_keyword("extern", "extern $0");
}
if in_extern_block {
add_keyword("fn", "fn $1($2);");
} else {
if !in_inherent_impl {
if !in_trait {
add_keyword("extern", "extern $0");
}
add_keyword("type", "type $0");
}
return;
}
add_keyword("fn", "fn $1($2) {\n $0\n}");
add_keyword("unsafe", "unsafe $0");
add_keyword("const", "const $0");
add_keyword("async", "async $0");
// ...and the rest deals with cases without any non-vis qualifiers.
// Visibility qualifiers
if !in_trait && !in_block && no_vis_qualifiers {
add_keyword("pub(crate)", "pub(crate) $0");
add_keyword("pub(super)", "pub(super) $0");
add_keyword("pub", "pub $0");
}
// Keywords that are valid in `item_list`
if in_item_list {
add_keyword("enum", "enum $1 {\n $0\n}");
add_keyword("mod", "mod $0");
add_keyword("static", "static $0");
add_keyword("struct", "struct $0");
add_keyword("trait", "trait $1 {\n $0\n}");
add_keyword("union", "union $1 {\n $0\n}");
add_keyword("use", "use $0");
if no_vis_qualifiers {
add_keyword("impl", "impl $1 {\n $0\n}");
}
}
if in_extern_block {
add_keyword("unsafe", "unsafe $0");
if in_unsafe_extern_block {
add_keyword("safe", "safe $0");
}
add_keyword("fn", "fn $1($2);");
add_keyword("static", "static $1: $2;");
} else {
if !in_inherent_impl {
if !in_trait {
add_keyword("extern", "extern $0");
}
add_keyword("type", "type $0");
}
add_keyword("fn", "fn $1($2) {\n $0\n}");
add_keyword("unsafe", "unsafe $0");
add_keyword("const", "const $0");
add_keyword("async", "async $0");
}
}

View file

@ -58,6 +58,7 @@ mod tests {
r"fn my_fn() { unsafe $0 }",
expect![[r#"
kw async
kw extern
kw fn
kw impl
kw trait

View file

@ -48,12 +48,16 @@ pub(crate) struct QualifierCtx {
// TODO: Add try_tok and default_tok
pub(crate) async_tok: Option<SyntaxToken>,
pub(crate) unsafe_tok: Option<SyntaxToken>,
pub(crate) safe_tok: Option<SyntaxToken>,
pub(crate) vis_node: Option<ast::Visibility>,
}
impl QualifierCtx {
pub(crate) fn none(&self) -> bool {
self.async_tok.is_none() && self.unsafe_tok.is_none() && self.vis_node.is_none()
self.async_tok.is_none()
&& self.unsafe_tok.is_none()
&& self.safe_tok.is_none()
&& self.vis_node.is_none()
}
}
@ -229,7 +233,7 @@ pub(crate) enum ItemListKind {
Impl,
TraitImpl(Option<ast::Impl>),
Trait,
ExternBlock,
ExternBlock { is_unsafe: bool },
}
#[derive(Debug)]

View file

@ -1108,7 +1108,14 @@ fn classify_name_ref(
},
None => return None,
} },
ast::ExternItemList(_) => PathKind::Item { kind: ItemListKind::ExternBlock },
ast::ExternItemList(it) => {
let exn_blk = it.syntax().parent().and_then(ast::ExternBlock::cast);
PathKind::Item {
kind: ItemListKind::ExternBlock {
is_unsafe: exn_blk.and_then(|it| it.unsafe_token()).is_some(),
}
}
},
ast::SourceFile(_) => PathKind::Item { kind: ItemListKind::SourceFile },
_ => return None,
}
@ -1310,6 +1317,7 @@ fn classify_name_ref(
match token.kind() {
SyntaxKind::UNSAFE_KW => qualifier_ctx.unsafe_tok = Some(token),
SyntaxKind::ASYNC_KW => qualifier_ctx.async_tok = Some(token),
SyntaxKind::SAFE_KW => qualifier_ctx.safe_tok = Some(token),
_ => {}
}
}

View file

@ -1853,8 +1853,8 @@ fn f() { A { bar: b$0 }; }
expect![[r#"
fn bar() [type+name]
fn baz() [type]
ex baz() [type]
ex bar() [type]
ex baz() [type]
st A []
fn f() []
"#]],

View file

@ -124,6 +124,7 @@ fn after_unsafe_token() {
r#"unsafe $0"#,
expect![[r#"
kw async
kw extern
kw fn
kw impl
kw trait
@ -495,3 +496,57 @@ type O = $0;
",
)
}
#[test]
fn inside_extern_blocks() {
// Should suggest `fn`, `static`, `unsafe`
check(
r#"extern { $0 }"#,
expect![[r#"
ma makro!() macro_rules! makro
md module
kw crate::
kw fn
kw pub
kw pub(crate)
kw pub(super)
kw self::
kw static
kw unsafe
"#]],
);
// Should suggest `fn`, `static`, `safe`, `unsafe`
check(
r#"unsafe extern { $0 }"#,
expect![[r#"
ma makro!() macro_rules! makro
md module
kw crate::
kw fn
kw pub
kw pub(crate)
kw pub(super)
kw safe
kw self::
kw static
kw unsafe
"#]],
);
check(
r#"unsafe extern { pub safe $0 }"#,
expect![[r#"
kw fn
kw static
"#]],
);
check(
r#"unsafe extern { pub unsafe $0 }"#,
expect![[r#"
kw fn
kw static
"#]],
)
}

View file

@ -923,3 +923,21 @@ fn foo() {
"#]],
);
}
#[test]
fn private_item_in_module_in_function_body() {
check_empty(
r#"
fn main() {
mod foo {
struct Private;
pub struct Public;
}
foo::$0
}
"#,
expect![[r#"
st Public Public
"#]],
);
}

View file

@ -595,4 +595,39 @@ unsafe fn foo(p: *mut i32) {
"#,
)
}
#[test]
fn no_unsafe_diagnostic_with_safe_kw() {
check_diagnostics(
r#"
unsafe extern {
pub safe fn f();
pub unsafe fn g();
pub fn h();
pub safe static S1: i32;
pub unsafe static S2: i32;
pub static S3: i32;
}
fn main() {
f();
g();
//^^^💡 error: this operation is unsafe and requires an unsafe function or block
h();
//^^^💡 error: this operation is unsafe and requires an unsafe function or block
let _ = S1;
let _ = S2;
//^^💡 error: this operation is unsafe and requires an unsafe function or block
let _ = S3;
//^^💡 error: this operation is unsafe and requires an unsafe function or block
}
"#,
);
}
}

View file

@ -286,6 +286,20 @@ fn main() {
),
},
},
Annotation {
range: 53..57,
kind: HasReferences {
pos: FilePositionWrapper {
file_id: FileId(
0,
),
offset: 53,
},
data: Some(
[],
),
},
},
Annotation {
range: 53..57,
kind: Runnable(
@ -305,20 +319,6 @@ fn main() {
},
),
},
Annotation {
range: 53..57,
kind: HasReferences {
pos: FilePositionWrapper {
file_id: FileId(
0,
),
offset: 53,
},
data: Some(
[],
),
},
},
]
"#]],
);
@ -336,6 +336,20 @@ fn main() {
"#,
expect![[r#"
[
Annotation {
range: 7..11,
kind: HasImpls {
pos: FilePositionWrapper {
file_id: FileId(
0,
),
offset: 7,
},
data: Some(
[],
),
},
},
Annotation {
range: 7..11,
kind: HasReferences {
@ -358,13 +372,13 @@ fn main() {
},
},
Annotation {
range: 7..11,
kind: HasImpls {
range: 17..21,
kind: HasReferences {
pos: FilePositionWrapper {
file_id: FileId(
0,
),
offset: 7,
offset: 17,
},
data: Some(
[],
@ -390,20 +404,6 @@ fn main() {
},
),
},
Annotation {
range: 17..21,
kind: HasReferences {
pos: FilePositionWrapper {
file_id: FileId(
0,
),
offset: 17,
},
data: Some(
[],
),
},
},
]
"#]],
);
@ -425,6 +425,30 @@ fn main() {
"#,
expect![[r#"
[
Annotation {
range: 7..11,
kind: HasImpls {
pos: FilePositionWrapper {
file_id: FileId(
0,
),
offset: 7,
},
data: Some(
[
NavigationTarget {
file_id: FileId(
0,
),
full_range: 36..64,
focus_range: 57..61,
name: "impl",
kind: Impl,
},
],
),
},
},
Annotation {
range: 7..11,
kind: HasReferences {
@ -452,30 +476,6 @@ fn main() {
),
},
},
Annotation {
range: 7..11,
kind: HasImpls {
pos: FilePositionWrapper {
file_id: FileId(
0,
),
offset: 7,
},
data: Some(
[
NavigationTarget {
file_id: FileId(
0,
),
full_range: 36..64,
focus_range: 57..61,
name: "impl",
kind: Impl,
},
],
),
},
},
Annotation {
range: 20..31,
kind: HasImpls {
@ -521,20 +521,6 @@ fn main() {
),
},
},
Annotation {
range: 69..73,
kind: HasReferences {
pos: FilePositionWrapper {
file_id: FileId(
0,
),
offset: 69,
},
data: Some(
[],
),
},
},
Annotation {
range: 69..73,
kind: Runnable(
@ -554,6 +540,20 @@ fn main() {
},
),
},
Annotation {
range: 69..73,
kind: HasReferences {
pos: FilePositionWrapper {
file_id: FileId(
0,
),
offset: 69,
},
data: Some(
[],
),
},
},
]
"#]],
);
@ -567,6 +567,20 @@ fn main() {}
"#,
expect![[r#"
[
Annotation {
range: 3..7,
kind: HasReferences {
pos: FilePositionWrapper {
file_id: FileId(
0,
),
offset: 3,
},
data: Some(
[],
),
},
},
Annotation {
range: 3..7,
kind: Runnable(
@ -586,20 +600,6 @@ fn main() {}
},
),
},
Annotation {
range: 3..7,
kind: HasReferences {
pos: FilePositionWrapper {
file_id: FileId(
0,
),
offset: 3,
},
data: Some(
[],
),
},
},
]
"#]],
);
@ -621,6 +621,30 @@ fn main() {
"#,
expect![[r#"
[
Annotation {
range: 7..11,
kind: HasImpls {
pos: FilePositionWrapper {
file_id: FileId(
0,
),
offset: 7,
},
data: Some(
[
NavigationTarget {
file_id: FileId(
0,
),
full_range: 14..56,
focus_range: 19..23,
name: "impl",
kind: Impl,
},
],
),
},
},
Annotation {
range: 7..11,
kind: HasReferences {
@ -648,30 +672,6 @@ fn main() {
),
},
},
Annotation {
range: 7..11,
kind: HasImpls {
pos: FilePositionWrapper {
file_id: FileId(
0,
),
offset: 7,
},
data: Some(
[
NavigationTarget {
file_id: FileId(
0,
),
full_range: 14..56,
focus_range: 19..23,
name: "impl",
kind: Impl,
},
],
),
},
},
Annotation {
range: 33..44,
kind: HasReferences {
@ -693,20 +693,6 @@ fn main() {
),
},
},
Annotation {
range: 61..65,
kind: HasReferences {
pos: FilePositionWrapper {
file_id: FileId(
0,
),
offset: 61,
},
data: Some(
[],
),
},
},
Annotation {
range: 61..65,
kind: Runnable(
@ -726,6 +712,20 @@ fn main() {
},
),
},
Annotation {
range: 61..65,
kind: HasReferences {
pos: FilePositionWrapper {
file_id: FileId(
0,
),
offset: 61,
},
data: Some(
[],
),
},
},
]
"#]],
);
@ -744,20 +744,6 @@ mod tests {
"#,
expect![[r#"
[
Annotation {
range: 3..7,
kind: HasReferences {
pos: FilePositionWrapper {
file_id: FileId(
0,
),
offset: 3,
},
data: Some(
[],
),
},
},
Annotation {
range: 3..7,
kind: Runnable(
@ -777,6 +763,20 @@ mod tests {
},
),
},
Annotation {
range: 3..7,
kind: HasReferences {
pos: FilePositionWrapper {
file_id: FileId(
0,
),
offset: 3,
},
data: Some(
[],
),
},
},
Annotation {
range: 18..23,
kind: Runnable(
@ -876,7 +876,7 @@ struct Foo;
[
Annotation {
range: 0..71,
kind: HasReferences {
kind: HasImpls {
pos: FilePositionWrapper {
file_id: FileId(
0,
@ -890,7 +890,7 @@ struct Foo;
},
Annotation {
range: 0..71,
kind: HasImpls {
kind: HasReferences {
pos: FilePositionWrapper {
file_id: FileId(
0,

View file

@ -497,6 +497,44 @@ fn func_in_include() {
//^^^^^^^^^^^^^^^
}
fn foo() {
func_in_include$0();
}
"#,
);
}
#[test]
fn goto_def_in_included_file_inside_mod() {
check(
r#"
//- minicore:include
//- /main.rs
mod a {
include!("b.rs");
}
//- /b.rs
fn func_in_include() {
//^^^^^^^^^^^^^^^
}
fn foo() {
func_in_include$0();
}
"#,
);
check(
r#"
//- minicore:include
//- /main.rs
mod a {
include!("a.rs");
}
//- /a.rs
fn func_in_include() {
//^^^^^^^^^^^^^^^
}
fn foo() {
func_in_include$0();
}

View file

@ -2750,4 +2750,25 @@ impl Foo {
"#]],
);
}
#[test]
fn goto_ref_on_included_file() {
check(
r#"
//- minicore:include
//- /lib.rs
include!("foo.rs");
fn howdy() {
let _ = FOO;
}
//- /foo.rs
const FOO$0: i32 = 0;
"#,
expect![[r#"
FOO Const FileId(1) 0..19 6..9
FileId(0) 45..48
"#]],
);
}
}

View file

@ -1350,18 +1350,18 @@ mod tests {
file_id: FileId(
0,
),
full_range: 121..185,
focus_range: 136..145,
name: "foo2_test",
full_range: 52..115,
focus_range: 67..75,
name: "foo_test",
kind: Function,
},
NavigationTarget {
file_id: FileId(
0,
),
full_range: 52..115,
focus_range: 67..75,
name: "foo_test",
full_range: 121..185,
focus_range: 136..145,
name: "foo2_test",
kind: Function,
},
]

View file

@ -46,14 +46,14 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
.unresolved_reference { color: #FC5555; text-decoration: wavy underline; }
</style>
<pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
<span class="keyword">let</span> <span class="variable declaration reference" data-binding-hash="8384512769119783714" style="color: hsl(59,93%,58%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration" data-binding-hash="17360984456076382725" style="color: hsl(95,79%,86%);">x</span> <span class="operator">=</span> <span class="variable reference" data-binding-hash="8384512769119783714" style="color: hsl(59,93%,58%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration" data-binding-hash="17186414787327620935" style="color: hsl(196,64%,89%);">y</span> <span class="operator">=</span> <span class="variable reference" data-binding-hash="8384512769119783714" style="color: hsl(59,93%,58%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration reference" data-binding-hash="16711699953829236520" style="color: hsl(345,54%,46%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration" data-binding-hash="10753541418856619067" style="color: hsl(51,52%,47%);">x</span> <span class="operator">=</span> <span class="variable reference" data-binding-hash="16711699953829236520" style="color: hsl(345,54%,46%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration" data-binding-hash="9865812862466303869" style="color: hsl(329,86%,55%);">y</span> <span class="operator">=</span> <span class="variable reference" data-binding-hash="16711699953829236520" style="color: hsl(345,54%,46%);">hello</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration reference" data-binding-hash="4786021388930833562" style="color: hsl(137,61%,87%);">x</span> <span class="operator">=</span> <span class="string_literal">"other color please!"</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration" data-binding-hash="18017815841345165192" style="color: hsl(39,76%,89%);">y</span> <span class="operator">=</span> <span class="variable reference" data-binding-hash="4786021388930833562" style="color: hsl(137,61%,87%);">x</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration reference" data-binding-hash="4890670724659097491" style="color: hsl(330,46%,45%);">x</span> <span class="operator">=</span> <span class="string_literal">"other color please!"</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="variable declaration" data-binding-hash="4002942168268782293" style="color: hsl(114,87%,67%);">y</span> <span class="operator">=</span> <span class="variable reference" data-binding-hash="4890670724659097491" style="color: hsl(330,46%,45%);">x</span><span class="operator">.</span><span class="unresolved_reference">to_string</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span>
<span class="brace">}</span>
<span class="keyword">fn</span> <span class="function declaration">bar</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
<span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable reference" data-binding-hash="8384512769119783714" style="color: hsl(59,93%,58%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="semicolon">;</span>
<span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable reference" data-binding-hash="16711699953829236520" style="color: hsl(345,54%,46%);">hello</span> <span class="operator">=</span> <span class="string_literal">"hello"</span><span class="semicolon">;</span>
<span class="brace">}</span></code></pre>

View file

@ -3,6 +3,7 @@
use intern::Symbol;
use rustc_hash::FxHashMap;
use span::{Edition, Span};
use stdx::itertools::Itertools;
use syntax::{
ast::{self, HasName},
AstNode,
@ -27,9 +28,10 @@ fn benchmark_parse_macro_rules() {
let hash: usize = {
let _pt = bench("mbe parse macro rules");
rules
.values()
.map(|it| {
DeclarativeMacro::parse_macro_rules(it, |_| span::Edition::CURRENT).rules.len()
.into_iter()
.sorted_by_key(|(id, _)| id.clone())
.map(|(_, it)| {
DeclarativeMacro::parse_macro_rules(&it, |_| span::Edition::CURRENT).rules.len()
})
.sum()
};
@ -55,12 +57,13 @@ fn benchmark_expand_macro_rules() {
})
.sum()
};
assert_eq!(hash, 69413);
assert_eq!(hash, 65720);
}
fn macro_rules_fixtures() -> FxHashMap<String, DeclarativeMacro> {
macro_rules_fixtures_tt()
.into_iter()
.sorted_by_key(|(id, _)| id.clone())
.map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt, |_| span::Edition::CURRENT)))
.collect()
}
@ -93,7 +96,7 @@ fn invocation_fixtures(
let mut seed = 123456789;
let mut res = Vec::new();
for (name, it) in rules {
for (name, it) in rules.iter().sorted_by_key(|&(id, _)| id) {
for rule in it.rules.iter() {
// Generate twice
for _ in 0..2 {

View file

@ -135,6 +135,11 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> {
has_mods = true;
}
if p.at_contextual_kw(T![safe]) {
p.eat_contextual_kw(T![safe]);
has_mods = true;
}
if p.at(T![extern]) {
has_extern = true;
has_mods = true;
@ -189,6 +194,7 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> {
T![fn] => fn_(p, m),
T![const] if p.nth(1) != T!['{'] => consts::konst(p, m),
T![static] if matches!(p.nth(1), IDENT | T![_] | T![mut]) => consts::static_(p, m),
T![trait] => traits::trait_(p, m),
T![impl] => traits::impl_(p, m),

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,208 @@
SOURCE_FILE
EXTERN_BLOCK
UNSAFE_KW "unsafe"
WHITESPACE " "
ABI
EXTERN_KW "extern"
WHITESPACE " "
EXTERN_ITEM_LIST
L_CURLY "{"
WHITESPACE "\n "
FN
COMMENT "// sqrt (from libm) may be called with any `f64`"
WHITESPACE "\n "
VISIBILITY
PUB_KW "pub"
WHITESPACE " "
SAFE_KW "safe"
WHITESPACE " "
FN_KW "fn"
WHITESPACE " "
NAME
IDENT "sqrt"
PARAM_LIST
L_PAREN "("
PARAM
IDENT_PAT
NAME
IDENT "x"
COLON ":"
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "f64"
R_PAREN ")"
WHITESPACE " "
RET_TYPE
THIN_ARROW "->"
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "f64"
SEMICOLON ";"
WHITESPACE "\n\n "
FN
COMMENT "// strlen (from libc) requires a valid pointer,"
WHITESPACE "\n "
COMMENT "// so we mark it as being an unsafe fn"
WHITESPACE "\n "
VISIBILITY
PUB_KW "pub"
WHITESPACE " "
UNSAFE_KW "unsafe"
WHITESPACE " "
FN_KW "fn"
WHITESPACE " "
NAME
IDENT "strlen"
PARAM_LIST
L_PAREN "("
PARAM
IDENT_PAT
NAME
IDENT "p"
COLON ":"
WHITESPACE " "
PTR_TYPE
STAR "*"
CONST_KW "const"
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "c_char"
R_PAREN ")"
WHITESPACE " "
RET_TYPE
THIN_ARROW "->"
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "usize"
SEMICOLON ";"
WHITESPACE "\n\n "
FN
COMMENT "// this function doesn't say safe or unsafe, so it defaults to unsafe"
WHITESPACE "\n "
VISIBILITY
PUB_KW "pub"
WHITESPACE " "
FN_KW "fn"
WHITESPACE " "
NAME
IDENT "free"
PARAM_LIST
L_PAREN "("
PARAM
IDENT_PAT
NAME
IDENT "p"
COLON ":"
WHITESPACE " "
PTR_TYPE
STAR "*"
MUT_KW "mut"
WHITESPACE " "
PATH_TYPE
PATH
PATH
PATH
PATH_SEGMENT
NAME_REF
IDENT "core"
COLON2 "::"
PATH_SEGMENT
NAME_REF
IDENT "ffi"
COLON2 "::"
PATH_SEGMENT
NAME_REF
IDENT "c_void"
R_PAREN ")"
SEMICOLON ";"
WHITESPACE "\n\n "
STATIC
VISIBILITY
PUB_KW "pub"
WHITESPACE " "
SAFE_KW "safe"
WHITESPACE " "
STATIC_KW "static"
WHITESPACE " "
MUT_KW "mut"
WHITESPACE " "
NAME
IDENT "COUNTER"
COLON ":"
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "i32"
SEMICOLON ";"
WHITESPACE "\n\n "
STATIC
VISIBILITY
PUB_KW "pub"
WHITESPACE " "
UNSAFE_KW "unsafe"
WHITESPACE " "
STATIC_KW "static"
WHITESPACE " "
NAME
IDENT "IMPORTANT_BYTES"
COLON ":"
WHITESPACE " "
ARRAY_TYPE
L_BRACK "["
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "u8"
SEMICOLON ";"
WHITESPACE " "
CONST_ARG
LITERAL
INT_NUMBER "256"
R_BRACK "]"
SEMICOLON ";"
WHITESPACE "\n\n "
STATIC
VISIBILITY
PUB_KW "pub"
WHITESPACE " "
SAFE_KW "safe"
WHITESPACE " "
STATIC_KW "static"
WHITESPACE " "
NAME
IDENT "LINES"
COLON ":"
WHITESPACE " "
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "SyncUnsafeCell"
GENERIC_ARG_LIST
L_ANGLE "<"
TYPE_ARG
PATH_TYPE
PATH
PATH_SEGMENT
NAME_REF
IDENT "i32"
R_ANGLE ">"
SEMICOLON ";"
WHITESPACE "\n"
R_CURLY "}"
WHITESPACE "\n"

View file

@ -0,0 +1,17 @@
unsafe extern {
// sqrt (from libm) may be called with any `f64`
pub safe fn sqrt(x: f64) -> f64;
// strlen (from libc) requires a valid pointer,
// so we mark it as being an unsafe fn
pub unsafe fn strlen(p: *const c_char) -> usize;
// this function doesn't say safe or unsafe, so it defaults to unsafe
pub fn free(p: *mut core::ffi::c_void);
pub safe static mut COUNTER: i32;
pub unsafe static IMPORTANT_BYTES: [u8; 256];
pub safe static LINES: SyncUnsafeCell<i32>;
}

View file

@ -222,8 +222,6 @@ fn rust_project_is_proc_macro_has_proc_macro_dep() {
}
#[test]
// FIXME Remove the ignore
#[ignore = "requires nightly until the sysroot ships a cargo workspace for library on stable"]
fn smoke_test_real_sysroot_cargo() {
let file_map = &mut FxHashMap::<AbsPathBuf, FileId>::default();
let meta: Metadata = get_test_json_file("hello-world-metadata.json");
@ -235,7 +233,6 @@ fn smoke_test_real_sysroot_cargo() {
&Default::default(),
);
assert!(matches!(sysroot.mode(), SysrootMode::Workspace(_)));
let project_workspace = ProjectWorkspace {
kind: ProjectWorkspaceKind::Cargo {
cargo: cargo_workspace,

View file

@ -17,7 +17,7 @@ indexmap = "2.1.0"
lock_api = "0.4"
tracing = "0.1"
parking_lot = "0.12.1"
rustc-hash = "1.0"
rustc-hash = "2.0.0"
smallvec = "1.0.0"
oorandom = "11"
triomphe = "0.1.11"

View file

@ -6,6 +6,7 @@ use std::{
time::{SystemTime, UNIX_EPOCH},
};
use cfg::{CfgAtom, CfgDiff};
use hir::{
db::{DefDatabase, ExpandDatabase, HirDatabase},
Adt, AssocItem, Crate, DefWithBody, HasSource, HirDisplay, HirFileIdExt, ImportPathConfig,
@ -31,7 +32,7 @@ use itertools::Itertools;
use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice};
use oorandom::Rand32;
use profile::{Bytes, StopWatch};
use project_model::{CargoConfig, ProjectManifest, ProjectWorkspace, RustLibSource};
use project_model::{CargoConfig, CfgOverrides, ProjectManifest, ProjectWorkspace, RustLibSource};
use rayon::prelude::*;
use rustc_hash::{FxHashMap, FxHashSet};
use syntax::{AstNode, SyntaxNode};
@ -65,7 +66,11 @@ impl flags::AnalysisStats {
false => Some(RustLibSource::Discover),
},
all_targets: true,
set_test: true,
set_test: !self.no_test,
cfg_overrides: CfgOverrides {
global: CfgDiff::new(vec![CfgAtom::Flag(hir::sym::miri.clone())], vec![]).unwrap(),
selective: Default::default(),
},
..Default::default()
};
let no_progress = &|_| ();

View file

@ -71,6 +71,8 @@ xflags::xflags! {
optional --with-deps
/// Don't load sysroot crates (`std`, `core` & friends).
optional --no-sysroot
/// Don't set #[cfg(test)].
optional --no-test
/// Don't run build scripts or load `OUT_DIR` values by running `cargo check` before analysis.
optional --disable-build-scripts
@ -233,6 +235,7 @@ pub struct AnalysisStats {
pub only: Option<String>,
pub with_deps: bool,
pub no_sysroot: bool,
pub no_test: bool,
pub disable_build_scripts: bool,
pub disable_proc_macros: bool,
pub proc_macro_srv: Option<PathBuf>,

View file

@ -35,10 +35,18 @@ pub(crate) fn offset(
.ok_or_else(|| format_err!("Invalid wide col offset"))?
}
};
let text_size = line_index.index.offset(line_col).ok_or_else(|| {
let line_range = line_index.index.line(line_col.line).ok_or_else(|| {
format_err!("Invalid offset {line_col:?} (line index length: {:?})", line_index.index.len())
})?;
Ok(text_size)
let col = TextSize::from(line_col.col);
let clamped_len = col.min(line_range.len());
if clamped_len < col {
tracing::error!(
"Position {line_col:?} column exceeds line length {}, clamping it",
u32::from(line_range.len()),
);
}
Ok(line_range.start() + clamped_len)
}
pub(crate) fn text_range(

View file

@ -190,7 +190,7 @@ UseTreeList =
Fn =
Attr* Visibility?
'default'? 'const'? 'async'? 'gen'? 'unsafe'? Abi?
'default'? 'const'? 'async'? 'gen'? 'unsafe'? 'safe'? Abi?
'fn' Name GenericParamList? ParamList RetType? WhereClause?
(body:BlockExpr | ';')
@ -284,6 +284,7 @@ Const =
Static =
Attr* Visibility?
'unsafe'? 'safe'?
'static' 'mut'? Name ':' Type
('=' body:Expr)? ';'

View file

@ -668,6 +668,8 @@ impl Fn {
#[inline]
pub fn gen_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![gen]) }
#[inline]
pub fn safe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![safe]) }
#[inline]
pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
}
@ -1761,7 +1763,11 @@ impl Static {
#[inline]
pub fn mut_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![mut]) }
#[inline]
pub fn safe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![safe]) }
#[inline]
pub fn static_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![static]) }
#[inline]
pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]

View file

@ -27,7 +27,7 @@ pub struct AstPtr<N: AstNode> {
_ty: PhantomData<fn() -> N>,
}
impl<N: AstNode + std::fmt::Debug> std::fmt::Debug for AstPtr<N> {
impl<N: AstNode> std::fmt::Debug for AstPtr<N> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("AstPtr").field(&self.raw).finish()
}

View file

@ -95,8 +95,8 @@ avoid checking unnecessary things.
Default:
----
{
"debug_assertions": null,
"miri": null
"miri": null,
"debug_assertions": null
}
----
List of cfg options to enable with the given values.
@ -321,18 +321,10 @@ Enables completions of private items and fields that are defined in the current
Default:
----
{
"Arc::new": {
"postfix": "arc",
"body": "Arc::new(${receiver})",
"requires": "std::sync::Arc",
"description": "Put the expression into an `Arc`",
"scope": "expr"
},
"Rc::new": {
"postfix": "rc",
"body": "Rc::new(${receiver})",
"requires": "std::rc::Rc",
"description": "Put the expression into an `Rc`",
"Ok": {
"postfix": "ok",
"body": "Ok(${receiver})",
"description": "Wrap the expression in a `Result::Ok`",
"scope": "expr"
},
"Box::pin": {
@ -342,10 +334,11 @@ Default:
"description": "Put the expression into a pinned `Box`",
"scope": "expr"
},
"Err": {
"postfix": "err",
"body": "Err(${receiver})",
"description": "Wrap the expression in a `Result::Err`",
"Arc::new": {
"postfix": "arc",
"body": "Arc::new(${receiver})",
"requires": "std::sync::Arc",
"description": "Put the expression into an `Arc`",
"scope": "expr"
},
"Some": {
@ -354,10 +347,17 @@ Default:
"description": "Wrap the expression in an `Option::Some`",
"scope": "expr"
},
"Ok": {
"postfix": "ok",
"body": "Ok(${receiver})",
"description": "Wrap the expression in a `Result::Ok`",
"Err": {
"postfix": "err",
"body": "Err(${receiver})",
"description": "Wrap the expression in a `Result::Err`",
"scope": "expr"
},
"Rc::new": {
"postfix": "rc",
"body": "Rc::new(${receiver})",
"requires": "std::rc::Rc",
"description": "Put the expression into an `Rc`",
"scope": "expr"
}
}

File diff suppressed because it is too large Load diff

View file

@ -58,14 +58,14 @@
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"@vscode/test-electron": "^2.3.8",
"@vscode/vsce": "^2.19.0",
"@vscode/vsce": "^3.0.0",
"esbuild": "^0.18.12",
"eslint": "^8.44.0",
"eslint-config-prettier": "^8.8.0",
"ovsx": "^0.8.2",
"prettier": "^3.0.0",
"tslib": "^2.6.0",
"typescript": "^5.1.6"
"typescript": "^5.6.0"
},
"activationEvents": [
"workspaceContains:Cargo.toml",
@ -349,6 +349,11 @@
"markdownDescription": "Whether to show the test explorer.",
"default": false,
"type": "boolean"
},
"rust-analyzer.initializeStopped": {
"markdownDescription": "Do not start rust-analyzer server when the extension is activated.",
"default": false,
"type": "boolean"
}
}
},
@ -728,8 +733,8 @@
"rust-analyzer.cargo.cfgs": {
"markdownDescription": "List of cfg options to enable with the given values.",
"default": {
"debug_assertions": null,
"miri": null
"miri": null,
"debug_assertions": null
},
"type": "object"
}
@ -1152,18 +1157,10 @@
"rust-analyzer.completion.snippets.custom": {
"markdownDescription": "Custom completion snippets.",
"default": {
"Arc::new": {
"postfix": "arc",
"body": "Arc::new(${receiver})",
"requires": "std::sync::Arc",
"description": "Put the expression into an `Arc`",
"scope": "expr"
},
"Rc::new": {
"postfix": "rc",
"body": "Rc::new(${receiver})",
"requires": "std::rc::Rc",
"description": "Put the expression into an `Rc`",
"Ok": {
"postfix": "ok",
"body": "Ok(${receiver})",
"description": "Wrap the expression in a `Result::Ok`",
"scope": "expr"
},
"Box::pin": {
@ -1173,10 +1170,11 @@
"description": "Put the expression into a pinned `Box`",
"scope": "expr"
},
"Err": {
"postfix": "err",
"body": "Err(${receiver})",
"description": "Wrap the expression in a `Result::Err`",
"Arc::new": {
"postfix": "arc",
"body": "Arc::new(${receiver})",
"requires": "std::sync::Arc",
"description": "Put the expression into an `Arc`",
"scope": "expr"
},
"Some": {
@ -1185,10 +1183,17 @@
"description": "Wrap the expression in an `Option::Some`",
"scope": "expr"
},
"Ok": {
"postfix": "ok",
"body": "Ok(${receiver})",
"description": "Wrap the expression in a `Result::Ok`",
"Err": {
"postfix": "err",
"body": "Err(${receiver})",
"description": "Wrap the expression in a `Result::Err`",
"scope": "expr"
},
"Rc::new": {
"postfix": "rc",
"body": "Rc::new(${receiver})",
"requires": "std::rc::Rc",
"description": "Put the expression into an `Rc`",
"scope": "expr"
}
},

View file

@ -330,6 +330,10 @@ export class Config {
get statusBarClickAction() {
return this.get<string>("statusBar.clickAction");
}
get initializeStopped() {
return this.get<boolean>("initializeStopped");
}
}
export function prepareVSCodeConfig<T>(resp: T): T {

View file

@ -6,7 +6,6 @@ import type * as ra from "./lsp_ext";
import { Cargo } from "./toolchain";
import type { Ctx } from "./ctx";
import { createTaskFromRunnable, prepareEnv } from "./run";
import { execSync } from "node:child_process";
import { execute, isCargoRunnableArgs, unwrapUndefinable } from "./util";
import type { Config } from "./config";
@ -152,9 +151,24 @@ async function getDebugConfiguration(
const env = prepareEnv(inheritEnv, runnable.label, runnableArgs, config.runnablesExtraEnv);
const executable = await getDebugExecutable(runnableArgs, env);
let sourceFileMap = debugOptions.sourceFileMap;
if (sourceFileMap === "auto") {
sourceFileMap = {};
await discoverSourceFileMap(sourceFileMap, env, wsFolder);
const computedSourceFileMap = await discoverSourceFileMap(env, wsFolder);
if (computedSourceFileMap) {
// lldb-dap requires passing the source map as an array of two element arrays.
// the two element array contains a source and destination pathname.
// TODO: remove lldb-dap-specific post-processing once
// https://github.com/llvm/llvm-project/pull/106919/ is released in the extension.
if (provider.type === "lldb-dap") {
provider.additional["sourceMap"] = [
[computedSourceFileMap?.source, computedSourceFileMap?.destination],
];
} else {
sourceFileMap[computedSourceFileMap.source] = computedSourceFileMap.destination;
}
}
}
const debugConfig = getDebugConfig(
@ -189,11 +203,15 @@ async function getDebugConfiguration(
return debugConfig;
}
type SourceFileMap = {
source: string;
destination: string;
};
async function discoverSourceFileMap(
sourceFileMap: Record<string, string>,
env: Record<string, string>,
cwd: string,
) {
): Promise<SourceFileMap | undefined> {
const sysroot = env["RUSTC_TOOLCHAIN"];
if (sysroot) {
// let's try to use the default toolchain
@ -203,9 +221,11 @@ async function discoverSourceFileMap(
const commitHash = rx.exec(data)?.[1];
if (commitHash) {
const rustlib = path.normalize(sysroot + "/lib/rustlib/src/rust");
sourceFileMap[`/rustc/${commitHash}/`] = rustlib;
return { source: rustlib, destination: rustlib };
}
}
return;
}
type PropertyFetcher<Config, Input, Key extends keyof Config> = (
@ -218,7 +238,7 @@ type DebugConfigProvider<Type extends string, DebugConfig extends BaseDebugConfi
runnableArgsProperty: PropertyFetcher<DebugConfig, ra.CargoRunnableArgs, keyof DebugConfig>;
sourceFileMapProperty?: keyof DebugConfig;
type: Type;
additional?: Record<string, unknown>;
additional: Record<string, unknown>;
};
type KnownEnginesType = (typeof knownEngines)[keyof typeof knownEngines];
@ -236,16 +256,7 @@ const knownEngines: {
"args",
runnableArgs.executableArgs,
],
additional: {
sourceMap: [
[
`/rustc/${/commit-hash:\s(.*)$/m.exec(
execSync("rustc -V -v", {}).toString(),
)?.[1]}/library`,
"${config:rust-analyzer.cargo.sysroot}/lib/rustlib/src/rust/library",
],
],
},
additional: {},
},
"vadimcn.vscode-lldb": {
type: "lldb",

View file

@ -107,7 +107,14 @@ async function activateServer(ctx: Ctx): Promise<RustAnalyzerExtensionApi> {
initializeDebugSessionTrackingAndRebuild(ctx);
}
await ctx.start();
if (ctx.config.initializeStopped) {
ctx.setServerStatus({
health: "stopped",
});
} else {
await ctx.start();
}
return ctx;
}

View file

@ -1,6 +1,6 @@
[package]
name = "line-index"
version = "0.1.1"
version = "0.1.2"
description = "Maps flat `TextSize` offsets to/from `(line, column)` representation."
license = "MIT OR Apache-2.0"
repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/line-index"

View file

@ -177,6 +177,14 @@ impl LineIndex {
Some(LineCol { line: line_col.line, col })
}
/// Returns the given line's range.
pub fn line(&self, line: u32) -> Option<TextRange> {
let start = self.start_offset(line as usize)?;
let next_newline = self.newlines.get(line as usize).copied().unwrap_or(self.len);
let line_length = next_newline - start;
Some(TextRange::new(start, start + line_length))
}
/// Given a range [start, end), returns a sorted iterator of non-empty ranges [start, x1), [x1,
/// x2), ..., [xn, end) where all the xi, which are positions of newlines, are inside the range
/// [start, end).

View file

@ -195,3 +195,26 @@ fn test_every_chars() {
}
}
}
#[test]
fn test_line() {
use text_size::TextRange;
macro_rules! validate {
($text:expr, $line:expr, $expected_start:literal .. $expected_end:literal) => {
let line_index = LineIndex::new($text);
assert_eq!(
line_index.line($line),
Some(TextRange::new(
TextSize::from($expected_start),
TextSize::from($expected_end)
))
);
};
}
validate!("", 0, 0..0);
validate!("\n", 1, 1..1);
validate!("\nabc", 1, 1..4);
validate!("\nabc\ndef", 1, 1..5);
}

View file

@ -1 +1 @@
dd5127615ad626741a1116d022cf784637ac05df
1de57a5ce952c722f7053aeacfc6c90bc139b678

View file

@ -111,7 +111,7 @@ const RESERVED: &[&str] = &[
// keywords that are keywords only in specific parse contexts
#[doc(alias = "WEAK_KEYWORDS")]
const CONTEXTUAL_KEYWORDS: &[&str] =
&["macro_rules", "union", "default", "raw", "dyn", "auto", "yeet"];
&["macro_rules", "union", "default", "raw", "dyn", "auto", "yeet", "safe"];
// keywords we use for special macro expansions
const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[
"asm",