Skip to content

panicked at 'body references two different self params' #10056

@matklad

Description

@matklad
Panic context:
> 
version: ce4670f29 2021-08-25 dev
request: textDocument/codeAction CodeActionParams {
    text_document: TextDocumentIdentifier {
        uri: Url {
            scheme: "file",
            cannot_be_a_base: false,
            username: "",
            password: None,
            host: None,
            port: None,
            path: "/home/matklad/projects/tracing-span-tree/src/lib.rs",
            query: None,
            fragment: None,
        },
    },
    range: Range {
        start: Position {
            line: 147,
            character: 0,
        },
        end: Position {
            line: 154,
            character: 0,
        },
    },
    context: CodeActionContext {
        diagnostics: [],
        only: None,
    },
    work_done_progress_params: WorkDoneProgressParams {
        work_done_token: None,
    },
    partial_result_params: PartialResultParams {
        partial_result_token: None,
    },
}

thread '<unnamed>' panicked at 'body references two different self params', crates/ide_assists/src/handlers/extract_function.rs:604:33
stack backtrace:
   0: std::panicking::begin_panic
   1: ide_assists::handlers::extract_function::FunctionBody::analyze::{{closure}}
   2: syntax::ast::expr_ext::<impl syntax::ast::generated::nodes::Expr>::walk::{{closure}}
   3: syntax::ast::expr_ext::<impl syntax::ast::generated::nodes::Expr>::preorder
   4: syntax::ast::expr_ext::<impl syntax::ast::generated::nodes::Expr>::walk
   5: ide_assists::handlers::extract_function::extract_function
   6: ide_assists::assists
   7: ide::Analysis::assists_with_fixes::{{closure}}
   8: std::panicking::try
   9: ide::Analysis::assists_with_fixes
  10: rust_analyzer::handlers::handle_code_action
  11: rust_analyzer::dispatch::RequestDispatcher::on::{{closure}}::{{closure}}
  12: <F as threadpool::FnBox>::call_box

Code:

//! Consumer of `tracing` data, which prints a hierarchical profile.
//!
//! Based on https://github.com/davidbarsky/tracing-tree, but does less, while
//! actually printing timings for spans.

use std::collections::hash_map::Entry;
use std::collections::HashMap;
use std::time::{Duration, Instant};
use std::{fmt, mem};

use tracing::debug;
use tracing::field::{Field, Visit};
use tracing::span::Attributes;
use tracing::{Event, Id, Subscriber};
use tracing_subscriber::layer::Context;
use tracing_subscriber::prelude::*;
use tracing_subscriber::registry::LookupSpan;
use tracing_subscriber::reload::Handle;
use tracing_subscriber::Layer;

pub fn enable() {
    let subscriber = tracing_subscriber::Registry::default().with(SpanTree);
    tracing::subscriber::set_global_default(subscriber)
        .unwrap_or_else(|_| debug!("Global subscriber is already set"));
}

#[derive(Default)]
pub struct SpanTree {
    aggregate: bool,
}

impl SpanTree {
    pub fn aggregate(self, yes: bool) -> SpanTree {
        SpanTree {
            aggregate: yes,
            ..self
        }
    }
}

struct Data {
    start: Instant,
    children: Vec<Node>,
}

impl Data {
    fn new(attrs: &Attributes<'_>) -> Self {
        let mut span = Self {
            start: Instant::now(),
            children: Vec::new(),
        };
        attrs.record(&mut span);
        span
    }
    fn into_node(self, name: &'static str) -> Node {
        Node {
            name,
            count: 1,
            duration: self.start.elapsed(),
            children: self.children,
        }
    }
}

impl Visit for Data {
    fn record_debug(&mut self, _field: &Field, _value: &dyn fmt::Debug) {}
}

impl<S> Layer<S> for SpanTree
where
    S: Subscriber + for<'span> LookupSpan<'span> + fmt::Debug,
{
    fn new_span(&self, attrs: &Attributes, id: &Id, ctx: Context<S>) {
        let span = ctx.span(id).unwrap();

        let data = Data::new(attrs);
        span.extensions_mut().insert(data);
    }

    fn on_event(&self, _event: &Event<'_>, _ctx: Context<S>) {}

    fn on_close(&self, id: Id, ctx: Context<S>) {
        let span = ctx.span(&id).unwrap();
        let data: Data = span.extensions_mut().remove().unwrap();
        let node = data.into_node(span.name());

        let ext = span.extensions();
        let data = ext.get::<Data>().unwrap();
        let duration = data.start.elapsed();

        match span.parent_id() {
            Some(parent_id) => {
                let parent_span = ctx.span(parent_id).unwrap();
                let mut parent_data: Data = parent_span.extensions_mut().get_mut().unwrap();
                parent_data.children.push(Node);
            }
            None => print(&node),
        }
        let level = ctx.scope().count();
    }
}

struct Node {
    name: &'static str,
    count: u32,
    duration: Duration,
    children: Vec<Node>,
}

impl Node {
    fn print(&self) {
        self.go(0)
    }
    fn go(&self, level: u32) {
        let bold = "\u{001b}[1m";
        let reset = "\u{001b}[0m";
        eprintln!(
            "{:width$}  {:3.2?} {bold}{}{reset}",
            "",
            self.duration,
            self.name,
            bold = bold,
            reset = reset,
            width = level * 2
        );
        for child in &self.children {
            self.go(child, level + 1)
        }
        if level == 0 {
            eprintln!()
        }
    }

    fn aggregate(&mut self) {
        self.children.sort_by_key(|it| it.name);
        let mut idx = 0;
        for i in 1..self.children.len() {
            if self.children[idx].name == self.children[i].name {
                let child = mem::take(&mut self.children[i]);
                self.children[idx].merge(child)
            } else {
                idx += 1;
                assert!(idx <= i);
                self.children.swap(idx, i);
            }
            self.children[i].aggregate();
            match visited.entry(&self.children[i].name as *const str) {
                Entry::Occupied(entry) => {}
                Entry::Vacant(entry) => {
                    entry.insert(idx);
                    self.children.swap(i, idx);
                    idx += 1
                }
            }
        }
    }
    fn merge(&mut self, other: Node) {
        debug_assert!(self.name == other.name);
        self.duration += other.duration;
        self.count += other.count;
    }
}

Selection on failure:

image

Metadata

Metadata

Assignees

Labels

Broken WindowBugs / technical debt to be addressed immediately

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions