profile
viewpoint
If you are wondering where the data of this site comes from, please visit https://api.github.com/users/lilyball/events. GitMemory does not store any data, but only uses NGINX to cache data for a period of time. The idea behind GitMemory is simply to give users a better reading experience.
Lily Ballard lilyball San Leandro, CA ๐Ÿณ๏ธโ€โšง๏ธ๐Ÿณ๏ธโ€๐ŸŒˆ Rustacean ๐Ÿฆ€. Open source contributor. Programming language aficionado. She/her.

apple/swift 57604

The Swift Programming Language

apple/swift-evolution 12797

This maintains proposals for changes and user-visible enhancements to the Swift Programming Language.

apple/swift-package-manager 8531

The Package Manager for the Swift Programming Language

apple/swift-corelibs-foundation 4429

The Foundation Project, providing core utilities, internationalization, and OS independence

apple/swift-corelibs-libdispatch 2121

The libdispatch Project, (a.k.a. Grand Central Dispatch), for concurrency on multicore hardware

apple/swift-corelibs-xctest 938

The XCTest Project, A Swift core library for providing unit test support

apple/swift-llbuild 896

A low-level build system, used by Xcode and the Swift Package Manager

apple/swift-lldb 656

This is the version of LLDB that supports the Swift programming language & REPL.

issue commentrust-lang/rust-clippy

Suggest using into_iter() instead of drain(..) on Vec

Also .drain(0..). And preferably variants that set the end (to the collection's length) as well.

linkmauve

comment created time in 5 hours

issue openedtokio-rs/tracing

`LevelFilter` documents its comparison logic as the opposite of reality

Bug Report

Version

Current published tracing-core and tracing, as well as the version currently documented on tracing.rs.

Crates

tracing-core

Description

The documentation for LevelFilter states

If a Level is considered less than a LevelFilter, it should be considered disabled; if greater than or equal to the LevelFilter, that level is enabled.

This is the exact opposite of how it works. LevelFilter compares the same way Level does, where ERROR is the lowest level and TRACE is the highest, with the addition of OFF as comparing less than ERROR. So the intended logic here is "if a Level is less than or equal to a LevelFilter it should be considered enabled; if greater than the LevelFilter, that level is disabled". Thankfully the LevelFilter::current() documentation is correct.

It would also be lovely to have a code sample added that demonstrates comparing a level against LevelFilter::current(), to help demonstrate how such comparisons work.

Side note: the LevelFilter::current() docs have a stray ` at the end.

created time in a day

issue openedtokio-rs/tracing

`tracing::enabled!()` macro for testing if events might be enabled given a static subset of event data

Feature Request

Crates

tracing-core tracing tracing-subscriber and other crates that implement subscribers

Motivation

I want to be able to test if an event is known to be disabled before doing an expensive computation required just for the event. In some cases I can do the computation as an argument of the event, but in others I cannot, such as when I'm emitting multiple events (e.g. iterating over a collection and emitting an event for each item).

Proposal

Add a new tracing::enabled!() macro. Standard usage would look like if tracing::enabled!(Level::INFO) { โ€ฆ }. This macro would allow false positives, but guarantee no false negatives (assuming correctly-implemented subscribers). The macro is testing for events specifically, not spans, as with spans you can create the span and check if it's disabled prior to using it (or filling in field values).

tracing::enabled!() would require a level argument, an optional target: argument, and then an optional set of fields. If the fields are not provided, they are considered unknown. In the interests of matching the event!() macro as closely as possible, this might look like tracing::enabled!(target: "foo", Level::INFO, message, request) if we know the fields, or tracing::enabled!(target: "foo", Level::INFO) if we don't.

From an implementation perspective, this would construct a Callsite and Metadata just like event!(), register the callsite, test the Interest, and possibly call Subscriber::enabled() as well. The metadata::Kind field would have a new variant HINT that indicates this is an enabled!() call. Additionally, the line field would have the value None (as the enabled call is unlikely to be the same line as the event). Subscribers must be aware of HINT if they care about fields or line, such that they treat line: None as representing any potential line, and they treat an empty FieldSet as meaning the fields are unknown. register_callsite can also use HINT to determine that no events will be logged against this callsite and thus avoid doing things like preallocating storage for it; the call here is only to get an Interest, not to prepare for emitting events.

We should also note that enabled!() should be called from within the same file and module as the event!() call(s) it guards, as that info is in the metadata and therefore available to filters.

Because this imposes additional requirements on subscribers, this likely needs to be considered a breaking change, as existing subscribers that care about line or field names may produce false negatives from enabled!(). We could also put this in as a minor change where it just tests against LevelFilter::current(), and adds the more precise support after a breaking change; if we do this we should still accept target: and field names and just document that they are ignored for now but will be used in a future update.

Alternatives

The name tracing::enabled!() doesn't necessarily convey that false positives are possible. Of course, even delaying your computation to an event argument doesn't guarantee it's logged if the subscriber filters on field values. In any case, we could call this tracing::disabled!() since we do guarantee that part but users then have to write if !tracing::disabled(โ€ฆ) { which is awkward. tracing::maybe_enabled!(), tracing::possibly_enabled(), tracing::not_disabled!(), none of these alternatives I can think of roll off the tongue. The imprecision of tracing::enabled!() is probably worth tolerating for the convenient and searchable name.

It would be conceptually cool if we could do something like tracing::if_enabled { body containing events } that parses the body, identifies all the events, hoists the callsite and metadata generation for the events to the top (but retaining the correct lines), registers the callsites, and tests the interests, before entering the body. The Subscriber::enabled() check itself would still be at the point of the event, to allow temporal filtering to work as normal. I don't know if that's even possible to do (e.g. reliably identifying a macro invocation as being a tracing event), but even if so it sounds very impractical to implement and might be confusing to users.

Or we could just continue to not support this. But it seems a shame to do all this performant tracing but not have a good way for users to avoid unnecessary expensive computations when the tracing is dynamically disabled.

If we don't do this, we could at least have tracing::static_is_enabled!(level) that's basically just level < tracing::level_filters::STATIC_MAX_LEVEL, to provide an easily-discoverable way to do that check. Or maybe doing level < tracing::level_filters::LevelFilter::current(), though I don't know what to call that as it's not static anymore.

created time in a day

issue commenttokio-rs/tracing

tracing-subscriber: `tracing_subscriber::fmt::try_init()` should default to INFO or TRACE, not ERROR

Random thought: #[tracing::instrument] defaults to INFO spans. In the case where I enable span events, I might actually want the global default to be ERROR, so I don't get flooded with instrument spans. I'm sure there's at least one ticket already on easily configuring the default global behavior of EnvFilter (or rather, configuring the fallback when no directive aplies). Given this, having EnvFilter::from_default_env() itself switch to allowing INFO through in the default case might be very surprising to people who have enabled span events. This doesn't affect tracing_subscriber::fmt::init() of course, as that doesn't enable span events, but it's something to consider when making more broad changes to EnvFilter itself.

I'm really kind of getting off track here, but in the context of span events, I wish EnvFilter could conditionally enable span events for specific directives. I don't know what this syntax would look like, and it would require the filter to have hooks into the span event generation (or having some way to identify an event as a span event), but it would be neat.

davidbarsky

comment created time in 2 days

issue commenttokio-rs/tracing

tracing-subscriber: `tracing_subscriber::fmt::try_init()` should default to INFO or TRACE, not ERROR

This continues to surprise and confuse me. tracing_subscriber::fmt().init() defaults to INFO, and if I want RUST_LOG support then I use tracing_subscriber::fmt::init() instead and now I'm defaulting to ERROR instead.

I'm actually surprised that EnvFilter changes behavior at all when RUST_LOG is not present (or is empty). What I would have expected is for it to only apply changes requested by RUST_LOG and not infer anything from its absence.

Not having used env_logger, I don't have any preconceived notions here, but my original expectation was that EnvFilter would not even adjust global behavior in the presence of a module-specific RUST_LOG filter. By that I mean my expectation was RUST_LOG="hello=debug" would just adjust behavior for the hello target and would not affect other targets, and that I should say e.g. RUST_LOG="error,hello=debug" if I want to override global behavior. I don't know if this is too much of a divergence from env_logger's behavior, but it seems like the least surprising behavior to me.

davidbarsky

comment created time in 2 days

issue commenttokio-rs/tracing

set_default from within get_default panics with `already borrowed: BorrowMutError`

Having said that, I'm not sure if it's actually a good thing for let _guard = get_default(|_| set_default(foo)); info!(โ€ฆ) to not be able to log using the set_default. Which is to say, that looks like it should behave like let _guard = set_default(get_default(|_| foo)); info!(โ€ฆ). And this style could be used if I want to wrap the current dispatch in something temporarily.

lilyball

comment created time in 2 days

issue commenttokio-rs/tracing

set_default from within get_default panics with `already borrowed: BorrowMutError`

We already disallow get_default from within get_default. What if we removed the can_enter flag and just made the default a Cell<Option<Dispatch>>? We can then just call default.take() and if it's None that means we're already in a get_default scope. And then we stuff our default back into the cell when we're done.

This would enforce that any nested set_default would get reverted when the get_default ends, even if the set_default guard hasn't been dropped. And we'd need a way to detect this in the case where the DefaultGuard is subsequently dropped from outside of the get_default scope. In the case of drop(get_default(|d| set_default(foo))) the DefaultGuard would hold None and can trivially avoid resetting the default, but if I do e.g. drop(get_default(|d| { let _guard = set_default(foo); set_default(bar))) then the second DropGuard would be expecting to restore foo. Though we already have weird behavior with dropping DropGuards out of order.

lilyball

comment created time in 2 days

issue commenttokio-rs/tracing

set_default from within get_default panics with `already borrowed: BorrowMutError`

Oh right, Rc requires a heap allocation. I honestly have no idea what the expected performance difference of an additional pointer dereference versus atomic operations is. The cost of allocation itself can be ignored, that's just once per set_default, not once per log.

lilyball

comment created time in 2 days

issue commentlwouis/alt-tab-macos

Per-shortcut secondary shortcuts

@lwouis It could be listed as a "While open" shortcut in the section above. That said, given existing platform behavior with โ‡งโŒ˜โ‡ฅ and โ‡งโŒ˜`, it certainly makes sense for โ‡งโŒฅโ‡ฅ to also be a trigger. But that's orthogonal to making it a per-shortcut customization and should not be considered a blocker.

Also, in the very last comment of #510, you yourself suggested setting "Select previous window" to โ‡งโ‡ฅ, which causes the issue described in this ticket.

Can we please get this ticket reopened? Regardless of how it's resolved, this is definitely an issue.

alt-tab-macos-bot

comment created time in 2 days

issue commentLnL7/nix-darwin

Replace `literalExample` with `literalExpression`/`literalDocBook`

options-to-docbook.xsl will have to be updated as well

lilyball

comment created time in 2 days

issue openedLnL7/nix-darwin

Replace `literalExample` with `literalExpression`/`literalDocBook`

Current nixpkgs master now issues warnings on literalExample. https://github.com/NixOS/nixpkgs/pull/136909 introduced this along with the replacements literalExpression and literalDocBook. This means that every darwin-rebuild build now warns me about the use of literalExample, which I can't fix myself as it's in nix-darwin.

home-manager switched over to literalExpression/literalDocBook already and added a compatibility shim. nix-darwin could adopt the same approach.

created time in 2 days

issue commentkilbd/nova-rust

[SYNTAX] Doc comments in impls aren't being highlighted

Intra-doc links are handled in VSCode via rust-analyzer's semantic token modifiers. I don't know the exact details, but this is part of LSP. It's not 100% accurate, right now it handles structs and free functions but not methods (or external links), but I think that's a limitation of rust-analyzer that will be fixed later.

As an example, here's a paragraph from tracing-subscriber:

//! In addition, the [`Filter`] trait defines an interface for filtering what
//! spans and events are recorded by a particular layer. This allows different
//! [`Layer`]s to handle separate subsets of the trace data emitted by a
//! program. See the [documentation on per-layer filtering][plf] for more
//! information on using [`Filter`]s.

Here's how it shows up in Nova:

screenshot of the comment as seen in Nova. There is no special highlighting for intra-doc links.

And here's what I see in VSCode:

screenshot of the comment as seen in Visual Studio Code. Intra-doc links are highlighted

And here's VSCode's token info for one of the intra-doc links:

screenshot. semantic token type: interface. modifiers: documentation injected intraDocLink

Notice the semantic token info which overrides the textmate scope info. Semantic token types are provided everywhere, e.g. the non-link parts of the comment also use a semantic token modifiers to identify it as a documentation comment (the textmate scope just identifies it as a comment.line.double-slash.rust).

lilyball

comment created time in 2 days

issue openedtokio-rs/tracing

Dropping `DefaultGuard`s in wrong order can leave wrong dispatch active

Bug Report

Version

asdf-tracing v0.1.0 (/Users/lily/Dev/Scratch/asdf-tracing)
โ”œโ”€โ”€ tracing v0.1.29
โ”‚   โ”œโ”€โ”€ tracing-attributes v0.1.18 (proc-macro)
โ”‚   โ””โ”€โ”€ tracing-core v0.1.21
โ””โ”€โ”€ tracing-subscriber v0.2.25
    โ”œโ”€โ”€ tracing v0.1.29 (*)
    โ”œโ”€โ”€ tracing-core v0.1.21 (*)
    โ”œโ”€โ”€ tracing-log v0.1.2
    โ”‚   โ””โ”€โ”€ tracing-core v0.1.21 (*)
    โ””โ”€โ”€ tracing-serde v0.1.2
        โ””โ”€โ”€ tracing-core v0.1.21 (*)

Platform

Darwin DeerBook 20.6.0 Darwin Kernel Version 20.6.0: Mon Aug 30 06:12:21 PDT 2021; root:xnu-7195.141.6~3/RELEASE_X86_64 x86_64

Crates

tracing-core

Description

Dropping DefaultGuards in a different order than they were created will leave the wrong dispatch active. If I make two set_default calls and drop their guards in the opposite order, the dispatch/subscriber given to the first set_default call will remain active after both guards have been dropped.

Reproduction

use std::mem;
use tracing::{info, info_span, subscriber::set_default};

fn main() {
    tracing_subscriber::fmt().with_target(false).pretty().init();
    let _compact = set_default(
        tracing_subscriber::fmt()
            .with_target(false)
            .compact()
            .finish(),
    );
    let _span = info_span!("compact").entered();
    info!("compact event");
    let _full = set_default(tracing_subscriber::fmt().with_target(false).finish());
    let _span2 = info_span!("full").entered();
    info!("full event");
    drop(_compact); // this clears the dispatch back to root
    info!("pretty event?!");
    drop(_full); // this sets it to compact
    info!("oops back to compact even though _compact was dropped");

    mem::forget((_span, _span2)); // see https://github.com/tokio-rs/tracing/issues/1656
}
Oct 19 22:43:54.441  INFO compact: compact event
Oct 19 22:43:54.442  INFO full: full event
  Oct 19 22:43:54.442  INFO pretty event?!
    at src/main.rs:18

Oct 19 22:43:54.442  INFO compact: oops back to compact even though _compact was dropped

Proposal

The default dispatch should be treated as a stack, where each DefaultGuard removes its associated dispatch from the stack. If it was not the topmost dispatch, the current default won't change, and removing the second dispatch before the topmost dispatch will properly restore the third dispatch upon removing the topmost.

One way to implement this is to make the default dispatch into a doubly-linked list, such that the guard holds a reference to the node it inserts. This way each guard can easily remove its corresponding node from the list.

created time in 3 days

issue openedtokio-rs/tracing

tracing_subscriber::Registry misbehaves when exiting span while some other subscriber is default

Bug Report

Version

asdf-tracing v0.1.0 (/Users/lily/Dev/Scratch/asdf-tracing)
โ”œโ”€โ”€ tracing v0.1.29
โ”‚   โ”œโ”€โ”€ tracing-attributes v0.1.18 (proc-macro)
โ”‚   โ””โ”€โ”€ tracing-core v0.1.21
โ””โ”€โ”€ tracing-subscriber v0.2.25
    โ”œโ”€โ”€ tracing v0.1.29 (*)
    โ”œโ”€โ”€ tracing-core v0.1.21 (*)
    โ”œโ”€โ”€ tracing-log v0.1.2
    โ”‚   โ””โ”€โ”€ tracing-core v0.1.21 (*)
    โ””โ”€โ”€ tracing-serde v0.1.2
        โ””โ”€โ”€ tracing-core v0.1.21 (*)

Platform

Darwin DeerBook 20.6.0 Darwin Kernel Version 20.6.0: Mon Aug 30 06:12:21 PDT 2021; root:xnu-7195.141.6~3/RELEASE_X86_64 x86_64

Crates

tracing-subscriber

Description

If I exit a span created from something using Registry (such as FmtSubscriber) while its associated dispatch is not the default dispatch, Registry will ask the default dispatch to try_close() the span id. This means it's asking the wrong dispatch to close it, which can lead to panics.

Reproduction

let outer = tracing_subscriber::fmt().set_default();
let one = info_span!("one").entered();
let inner = tracing_subscriber::fmt().set_default();
let two = info_span!("two").entered();
drop(one); // this closes span two
drop(two); // this panics
thread 'main' panicked at 'tried to drop a ref to Id(1), but no such span exists!', /Users/lily/.cargo/registry/src/github.com-1ecc6299db9ec823/tracing-subscriber-0.2.25/src/registry/sharded.rs:346:21

created time in 3 days

issue openedtokio-rs/tracing

set_default from within get_default panics with `already borrowed: BorrowMutError`

Bug Report

Version

asdf-tracing v0.1.0 (/Users/lily/Dev/Scratch/asdf-tracing)
โ”œโ”€โ”€ tracing v0.1.29
โ”‚   โ”œโ”€โ”€ tracing-attributes v0.1.18 (proc-macro)
โ”‚   โ””โ”€โ”€ tracing-core v0.1.21
โ””โ”€โ”€ tracing-subscriber v0.2.25
    โ”œโ”€โ”€ tracing v0.1.29 (*)
    โ”œโ”€โ”€ tracing-core v0.1.21 (*)
    โ”œโ”€โ”€ tracing-log v0.1.2
    โ”‚   โ””โ”€โ”€ tracing-core v0.1.21 (*)
    โ””โ”€โ”€ tracing-serde v0.1.2
        โ””โ”€โ”€ tracing-core v0.1.21 (*)

Platform

Darwin DeerBook 20.6.0 Darwin Kernel Version 20.6.0: Mon Aug 30 06:12:21 PDT 2021; root:xnu-7195.141.6~3/RELEASE_X86_64 x86_64

Crates

tracing-core

Description

tracing_core::dispatcher::get_default() documents that calling it from within itself will get Dispatch::none. It does not document any interaction with set_default(). And tracing_core::dispatcher::set_default() documents no interaction with get_default().

Despite this, calling set_default() from within get_default() panics with already borrowed: BorrowMutError, which appears to be entirely unintentional. It actually does have logic in set_default() to declare "the state can be entered", which suggests the desire for set_default() to allow recursive dispatch. And this makes sense too, if I want to log debug events from within my subscriber I may want to be able to temporarily swap to a debug dispatch, log the events, and back out again. Though even without the panic, that wouldn't work right today as the DefaultGuard wouldn't reset the can_enter state (and also, what happens if you set the already-entered dispatch as default and tried to re-enter it?)

At a minimum, this incompatibility should be documented, and ideally the panic message replaced with a more meaningful one that identifies the problem. Ideally this would be fixed at a more general level though.

Proposal

The panic here is because the default is stored as a RefCell. get_default() borrows this (actually, borrows it mutably, but the can_enter state avoids nested get_default calls from panicking), and set_default() calls replace() on it, which will panic if it's already borrowed. More generally, the problem is we're calling external code (the closure argument) with the borrow held.

One possible solution here is to store the dispatch as a RefCell<Rc<Dispatch>>. This way we can borrow it, clone the Rc, and drop the borrow, all prior to calling the user function. set_default would just wrap the Dispatch in an Rc before replacing it. Because this is all thread-local, the use of the Rc is fine and quite cheap.

This would allow code like

impl Subscriber for MySubscriber {
    fn enter(&self, span: &Id) {
        let _guard = set_default(self.debug_subscriber);
        let _span = trace_span!("enter", self, span).entered();
        // do work here that potentially involves trace events
    }
}

and more generally would make it safe to use set_default() and with_default() from library code without worrying about the call stack.

Alternatives

We could make set_default() do nothing at all if called from within get_default(), but that's rather surprising, especially if the DefaultGuard is returned up the stack past the get_default().

We could change the code to just clone() the Dispatch, allowing us to release the borrow immediately before calling the closure. This feels like an unnecessary expense to do on every log call though.

Additional considerations

Once this panic is fixed, we can write code like

get_default(|dispatch| {
    let _guard = set_default(dispatch);
    get_default(|dispatch| {
        // hey this gets us the working dispatch!
    });
});

This is a bit weird, and could be abused to recursively log events. This is probably okay though, as it's rather hard to do by accident. It looks like the ban on recursive get_dispatch is just to prevent accidental infinite recursion if the various Subscriber methods try to log trace events.

created time in 3 days

issue openedtokio-rs/tracing

`tracing_core::dispatcher::get_default()` takes a `FnMut` when it should take a `FnOnce`

Bug Report

Version

tracing-core v0.1.21 Also 0.2.0 as documented on tracing.rs

Crates

tracing-core

Description

tracing_core::dispatcher::get_default() (and consequently tracing::dispatcher::get_default()) takes a FnMut argument, which it executes exactly once. The use of FnMut here is unnecessarily restrictive.

Proposal

Change it to take an FnOnce. This is a backwards-compatible change (as every FnMut is also an FnOnce). The implementation can just wrap it in an Option and use take() to pull out the function; this is safe as the function will be called exactly once for every possible code path.

created time in 3 days

issue commenttokio-rs/tracing

tracing_subscriber: Expose .with_ansi() to custom event/field formatters

Sounds like based on the intended release time of 0.3 that there's not enough time for me to even find out if I can do a PR.

lilyball

comment created time in 3 days

issue commenttokio-rs/tracing

tracing_subscriber: Expose .with_ansi() to custom event/field formatters

@hawkw Perhaps. I'm not sure yet. I'll see if it's something I can do.

lilyball

comment created time in 3 days

issue openedtokio-rs/tracing

tracing_subscriber: Expose .with_ansi() to custom event/field formatters

Feature Request

Crates

tracing-subscriber

Motivation

The SubscriberBuilder::with_ansi() method only affects the default event formatter (tracing_subscriber::fmt::format::Format). This means tracing_subscriber::fmt().with_ansi(true).event_format(fmt_event).init() silently throws away the with_ansi flag. It also means that this flag isn't available to custom event formatters. And this flag isn't exposed to field formatters (not even the default field formatter, though DefaultFields doesn't currently care about this).

It would be great if instead of being a property of the default event formatter, it was a flag that was exposed to arbitrary FormatEvent and FormatFields implementations. This way if I do have a custom field formatter that cares about ANSI, there's a single source of truth for this, as well as having .event_format() not silently overwrite the .with_ansi() flag. This also makes it easier to integrate logic for only outputting colors to terminals as anything provided by tracing_subscriber directly would then propagate to custom event/field formatters.

Proposal

The simplest solution is probably to change FormatEvent and FormatFields to take a custom wrapper type instead of a fork &mut dyn Write. This wrapper type would still implement Write but would also expose the ansi flag. This way the event and field formatters will get the same flag, controlled by the .with_ansi() method, and custom field formatters can now be provided by crates as drop-in replacements easily.

Alternatives

The proposed change is a breaking one, but is the cleanest solution that comes to mind. A couple of alternatives come to mind:

Change Write to a trait with more semantic meaning

Right now, changing things like terminal colors requires replacing the event and field formatters, potentially discarding any custom logic there (e.g. if I have a field formatter that handles certain fields specially, and another one that adds color to fields, I can't combine the two). We could consider replacing Write with a trait that requires you to specify the nature of the data you're writing, such as write_key(_: &fmt::Arguments<'_>), write_value(_: &fmt::Arguments<'_>), write_separator(_: &fmt::Arguments<'_>), write_delimiter(_: &fmt::Arguments<'_>), etc. Then we can have a separate configurable "format decorator" that provides these implementations and picks appropriate escapes to use. See slog_term::RecordDecorator for an example of this approach. The complexity here is we'd need to figure out all possible semantic meanings that a format decorator would want to distinguish, but it would also allow a format decorator to e.g. emit XML instead (although such a decorator would have to handle e.g. a value with no key, and there's a bit of confusion there over who controls delimiter/separator strings, as an XML decorator would likely want to just use tags there but terminal output wants strings).

This is of course also a breaking change, and a much more significant one. It's unclear to me if it's worth the complexity in order to separate colors from text, though at the moment I would actually love to be able to do this as I'd like to change colors without losing any default behavior around custom handling of fields (such as DefaultFields skipping log.* fields and stripping r# prefixes and including error sources).

Expose the ansi flag via additional defaulted trait methods

We could extend RecordFields and FormatEvent with an additional defaulted fn set_ansi(enabled: Bool) (default impl doing nothing). This is classified as a "possibly breaking" change, in that code that has the trait in scope might throw ambiguity errors when calling the method if another in-scope trait defines the same method, and I suspect this is unlikely to affect downstream code. It's also not a particularly great API as it requires the implementation to maintain state.

Switch traits with a compatibility shim

We could do the original proposal but only expose this in new traits that replace FormatEvents and FormatFields. The new traits could have blanket impls for anything that implements the old traits, thus allowing existing code to keep working, and anything that cares about the ANSI flag would implement the new trait instead.

This should be backwards-compatible but is potentially confusing and requires coming up with new trait names.

created time in 3 days

issue openedtokio-rs/tracing

`tracing_subscriber::FmtSubscriber` treats info!(parent: None, โ€ฆ) as a contextual event

Bug Report

Version

asdf-tracing v0.1.0 (/Users/lily/Dev/Scratch/asdf-tracing)
โ”œโ”€โ”€ tracing v0.1.29
โ”‚   โ”œโ”€โ”€ tracing-attributes v0.1.18 (proc-macro)
โ”‚   โ””โ”€โ”€ tracing-core v0.1.21
โ””โ”€โ”€ tracing-subscriber v0.2.25
    โ”œโ”€โ”€ tracing v0.1.29 (*)
    โ”œโ”€โ”€ tracing-core v0.1.21 (*)
    โ”œโ”€โ”€ tracing-log v0.1.2
    โ”‚   โ””โ”€โ”€ tracing-core v0.1.21 (*)
    โ””โ”€โ”€ tracing-serde v0.1.2
        โ””โ”€โ”€ tracing-core v0.1.21 (*)

Platform

Darwin DeerBook 20.6.0 Darwin Kernel Version 20.6.0: Mon Aug 30 06:12:21 PDT 2021; root:xnu-7195.141.6~3/RELEASE_X86_64 x86_64

Crates

tracing-subscriber

Description

tracing_subscriber::FmtSubscriber treats events with parent: None as though no parent had been specified, logging them in the current span. It respects parent: None for spans but not for events.

[dependencies]
tracing = "0.1.29"
tracing-subscriber = "0.2.25"
use tracing::{info, span, Level};

fn main() {
    tracing_subscriber::fmt().init();

    span!(Level::INFO, "foo").in_scope(|| {
        info!("with parent");
        info!(parent: None, "without parent");
    });
}
Oct 19 11:03:25.071  INFO foo: asdf_tracing: with parent
Oct 19 11:03:25.072  INFO foo: asdf_tracing: without parent

It looks like the root cause here is code that looks at event.parent() and treats None as "contextual", without checking event.is_root() or event.is_contextual(). Having event.parent() be an Option instead of a tri-variant enum has always struck me as a footgun.

created time in 3 days

issue commenttokio-rs/tracing

`Dispatch` should require the `Subscriber` to be `RefUnwindSafe`

We might consider documenting this in tracing-core 0.1, saying that Subscriber implementations should be RefUnwindSafe and that this will be enforced in 0.2.

lilyball

comment created time in 4 days

issue openedtokio-rs/tracing

`Dispatch` should require the `Subscriber` to be `RefUnwindSafe`

Bug Report

Version

tracing v0.1.29 tracing-core v0.1.21

Crates

tracing-core

Description

tracing_core::Dispatch holds an Arc<dyn Subscriber + Send + Sync>. Because of the trait bounds involved here, the Arc is not UnwindSafe or RefUnwindSafe as this requires its contained type to be RefUnwindSafe.

This is an annoying limitation as it means I cannot pass a Dispatch across a catch_unwind boundary. Or, in my case, I can't hold it in an adaptor to another logging system that requires RefUnwindSafe.

What's more, I believe that Dispatch actually explicitly should require its Subscriber to be RefUnwindSafe. The reason being, the fact that Dispatchs are stuffed into thread-local and global storage means they already cross catch_unwind boundaries. This means if I write a Subscriber that is not RefUnwindSafe, and I panic in a way that breaks its invariants but catch that with catch_unwind, the Subscriber will still be active on the outside despite the invariants broken.

To demonstrate this, I wrote a sample Subscriber implementation that is Send and Sync, but is not RefUnwindSafe. It maintains a (rather trivial) invariant, that is temporarily broken during event() while a lock is held, and restored at the end. But it rather carelessly has a panic in the middle if the event doesn't have the message field. And the result of this is logging a message-free event during a catch_unwind breaks the subscriber permanently. I've included it below.

Also worth noting is that because Subscriber has to be Sync, it already has to handle the case of being accessed from multiple threads. This strongly suggests that it should be RefUnwindSafe as well, because you can spawn a thread and panic in that thread and then observe the broken invariant from your original thread without ever calling catch_unwind.

On a related note, Span also ends up being !RefUnwindSafe, but it has the additional problem where being RefUnwindSafe would require Metadata to also be RefUnwindSafe, which it currently isn't due to FieldSet, which in turn is due to callsite::Identifier. I don't believe callsite::Identifier ever actually invokes anything on its embedded dyn Callsite, so it should be able to simply declare itself to be UnwindSafe/RefUnwindSafe.

I also checked the latest tracing.rs docs and the problem still exists there, where Dispatch has a dyn Collect + Send + Sync with no mention of RefUnwindSafe.

<details><summary>Example code (<a href="https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b25df4686208aff479a44f640e5539bf">Playground</a>)</summary>

use parking_lot::Mutex;
use std::fmt::Debug;
use std::panic;
use tracing::{info, field, span, Event, Metadata, Subscriber};

struct BadSubscriber {
    // this simulates a logical invariant. It should only be `true` while a
    // method is executing, and should revert back to `false` after.
    invariant: Mutex<bool>,
}

impl BadSubscriber {
    fn new() -> Self {
        Self {
            invariant: Mutex::new(false),
        }
    }
}

impl Subscriber for BadSubscriber {
    fn enabled(&self, _: &Metadata<'_>) -> bool {
        true
    }

    // ignore spans
    fn enter(&self, _: &span::Id) {}
    fn exit(&self, _: &span::Id) {}
    fn new_span(&self, _: &span::Attributes<'_>) -> span::Id {
        span::Id::from_u64(0)
    }
    fn record(&self, _: &span::Id, _: &span::Record<'_>) {}
    fn record_follows_from(&self, _: &span::Id, _: &span::Id) {}

    fn event(&self, event: &Event<'_>) {
        let mut guard = self.invariant.lock();
        if *guard { panic!("invariant broken"); }
        *guard = true;
        // find the message field
        let mut message = None;
        event.record(&mut |field: &field::Field, value: &dyn Debug| {
            if field.name() == "message" {
                message = Some(format!("{:?}", value));
            }
        });
        // oops we did something stupid here, this will panic if there's no message
        println!("event: {}", message.unwrap());
        // restore the invariant
        *guard = false;
    }
}

fn main() {
    let _guard = tracing::dispatcher::set_default(&BadSubscriber::new().into());

    info!("first message");
    let _ = panic::catch_unwind(|| {
        info!(foo = 42); // this panics, but is caught
    });
    info!("try again"); // this panics with "invariant broken"
    // we never get to this point
}

Output:

โฏ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `target/debug/asdf`
event: first message
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/main.rs:46:39
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'main' panicked at 'invariant broken', src/main.rs:36:21

</details>

created time in 8 days

issue openedrust-lang/nomicon

PhantomData patterns table should include interactions with `Send`/`Sync`

According to the PhantomData patterns table, there seems to be no difference between using e.g. *const T and fn() -> T. Both are covariant on T and have no lifetime. But there actually is a difference, which is that PhantomData<*const T> is not Send or Sync, whereas PhantomData<fn() -> T> is Send and Sync.

This is something that users can figure out by thinking about how PhantomData implements auto traits according to its T parameter, but this isn't explicitly called out in the nomicon (or in the std::marker::PhantomData docs) and so it's easy to forget. For example, I changed some code from PhantomData<T> to PhantomData<fn() -> T> specifically to avoid the drop check (my type doesn't own a T, but instead it can produce Ts). It just so happens that this makes my type Send and Sync too, which it should have been in the first place but nobody realized that until well after the change.

This could be improved by updating the Nomicon to call out the fact that PhantomData will implement auto traits according to its type parameter, and to give an example of this by including Send/Sync columns in the table that lists whether the given pattern is Send/Sync. This would look something like

Phantom type 'a T Send Sync
PhantomData<T> - covariant (with drop check) T: Send T: Sync
PhantomData<&'a T> covariant covariant T: Sync T: Sync
PhantomData<&'a mut T> covariant invariant T: Send T: Sync
PhantomData<*const T> - covariant - -
PhantomData<*mut T> - invariant - -
PhantomData<fn(T)> - contravariant Send Sync
PhantomData<fn() -> T> - covariant Send Sync

...

created time in 8 days

issue openedRobzz/cargo-docset

`dashIndexFilePath` is garbage for virtual manifests

Describe the bug When building with current master (3e2c9b3b6e9af132bd6f261da6c4eb67f6f8604d), if I'm documenting a workspace with a virtual manifest, the dashIndexFilePath key in the docset's Info.plist is garbage. The resulting value appears to be the first entry in the members list with /index.html tacked on.

In my case, my manifest has

members = [
    "crates/*", "crates-alt/*", "examples/*",
]

And the dashIndexFilePath of the resulting docset is crates/*/index.html.

To Reproduce

  1. Run cargo-docset in a workspace that has a virtual manifest with multiple entries in members.

Expected behavior By default, it shouldn't have a dashIndexFilePath at all, since there's no index file for docs built from a virtual manifest. If I passed RUSTDOCFLAGS='-Z unstable-options --enable-index-page' (or --index-page PATH) then it would ideally be set to the resulting index.html page instead.

created time in 9 days

issue openedrust-lang/rust

RwLock is `UnwindSafe` despite not poisoning on `read()`, can cause broken invariants

RwLock only poisons on panic with write(), it explicitly does not poison on panic with read(). This makes sense for most types, but for types with interior mutability read() can expose that interior mutability and allow broken invariants to be witnessed without using AssertUnwindSafe or spawning threads.

If I understand this correctly, due to the lack of read poisoning, it really should only be UnwindSafe where T: UnwindSafe (and I believe RefUnwindSafe where T: RefUnwindSafe).

On a similar note, RwLockReadGuard is UnwindSafe and RefUnwindSafe without any conditions. So is RwLockWriteGuard, but that's the one that does poisoning so it's fine there. RwLockReadGuard should at least require T: RefUnwindSafe for it to be UnwindSafe and RefUnwindSafe. I believe that if RwLock is adjusted then RwLockReadGuard will pick it up automatically due to the auto trait rules, though in this case RwLockWriteGuard will need the manual implementations as it does poisoning.


I'm not sure if there's any quick go-to for testing unwind safety, but I wrote a simple type that maintains a logical invariant with interior mutability. Without RwLock I get the expected compiler error trying to pass a reference to it across a catch_unwind barrier. Wrapping it in RwLock gets rid of the compiler error without introducing poisoning, which then allows for observing a broken invariant. Playground link

use std::cell::RefCell;

struct Foo {
    // Invariant: inner is always Some, it's only teporarily None during transformation.
    inner: RefCell<Option<String>>,
}

impl Foo {
    fn new() -> Self {
        Foo {
            inner: RefCell::new(Some("initial".to_owned())),
        }
    }

    fn transform(&self, f: impl FnOnce(String) -> String) {
        let inner = self.inner.borrow_mut().take().unwrap();
        *self.inner.borrow_mut() = Some(f(inner));
    }

    fn inner(&self) -> std::cell::Ref<str> {
        std::cell::Ref::map(self.inner.borrow(), |inner| {
            inner.as_deref().expect("broken invariant")
        })
    }
}

fn main() {
    let foo = Foo::new();
    
    // Comment out the next two lines to get an unwind safety error
    let foo = std::sync::RwLock::new(foo);
    let foo = foo.read().unwrap();
    
    dbg!(foo.inner());
    let result = std::panic::catch_unwind(|| {
        foo.transform(|_| panic!());
    });
    let _ = dbg!(result);
    dbg!(foo.inner());
}

In this code I'm passing the RwLockReadGuard across the catch_unwind barrier, but passing the &RwLock across and calling .read().unwrap() on each access produces the same results (meaning this can't just be fixed by changing RwLockReadGuard).

Meta

Playground rust version: Stable channel, 1.55.0

Additional context

I discovered this when I was trying to figure out how to make a type with interior mutability safe to pass to something that requires RefUnwindSafe. I thought "it has interior mutability, maybe I should just use a RwLock and only take reads on it", at which point I discovered that this doesn't add poisoning despite being RefUnwindSafe. In retrospect, reading can't add poisoning (given that there can be concurrent reads, and you can't poison an extant guard) so I really do need a mutex, but the fact that RwLock makes the compiler happy here is a problem.

created time in 10 days

IssuesEvent

issue commentrust-lang/rust

std::sync::Once: Should Once be UnwindSafe or RefUnwindSafe?

Once::call_once() does not require &'static self anymore (tested with rustc 1.55.0). The original issue is still valid. Since Once poisons itself on panic, it should be considered unwind safe.

joshlf

comment created time in 10 days

issue commentkilbd/nova-rust

[BUG] Language Server Crash on trivial edits to new project

It looks like the "index out of bounds" index is in fact the last line in the file.

Similarly, I deleted that blank line, at which point mere cursor interactions on what was now the last line caused panics.

So it looks like the core problem here is any editor interaction involving the last line of the file is panicking.

lilyball

comment created time in 10 days

issue commentkilbd/nova-rust

[BUG] Language Server Crash on trivial edits to new project

Playing some more, after a save and more of the canceled by client lines, waiting long enough will cause it to stop logging those. At this point I could make a trivial edit inside of my fn main() (inserting a space), or in between my struct Foo { โ€ฆ } and fn main() items, but once again, inserting a single space at the end of the file crashed the language server.

lilyball

comment created time in 10 days

issue openedkilbd/nova-rust

[BUG] Language Server Crash on trivial edits to new project

Describe the bug I created a new project with cargo new foo, opened it in Nova, and began editing the main.rs file. And the language server keeps crashing on every edit. I restart it, it looks fine, I edit the file, crash.

<details><summary>Crash log</summary>

Rust Analyzer[11:13:31.012000] waiting for cargo metadata or cargo check
Rust Analyzer[11:13:31.705000] waiting for cargo metadata or cargo check
Rust Analyzer[11:13:31.754000] waiting for cargo metadata or cargo check
Rust Analyzer[11:13:32.545000] content modified
Rust Analyzer[11:13:33.574000] canceled by client
Rust Analyzer[11:13:36.553000] canceled by client
Rust Analyzer[11:13:36.670000] canceled by client
Rust Analyzer[11:13:45.842000] Panic context:
Rust Analyzer[11:13:45.842000] > 
Rust Analyzer[11:13:45.842000] version: ed4b312fa 2021-10-11 stable
Rust Analyzer[11:13:45.842000] request: textDocument/codeAction CodeActionParams {
Rust Analyzer[11:13:45.842000]     text_document: TextDocumentIdentifier {
Rust Analyzer[11:13:45.842000]         uri: Url {
Rust Analyzer[11:13:45.842000]             scheme: "file",
Rust Analyzer[11:13:45.842000]             cannot_be_a_base: false,
Rust Analyzer[11:13:45.842000]             username: "",
Rust Analyzer[11:13:45.842000]             password: None,
Rust Analyzer[11:13:45.842000]             host: None,
Rust Analyzer[11:13:45.842000]             port: None,
Rust Analyzer[11:13:45.842000]             path: "/Volumes/Macintosh%20HD/Users/lily/Desktop/foo/src/main.rs",
Rust Analyzer[11:13:45.842000]             query: None,
Rust Analyzer[11:13:45.842000]             fragment: None,
Rust Analyzer[11:13:45.842000]         },
Rust Analyzer[11:13:45.842000]     },
Rust Analyzer[11:13:45.842000]     range: Range {
Rust Analyzer[11:13:45.842000]         start: Position {
Rust Analyzer[11:13:45.842000]             line: 5,
Rust Analyzer[11:13:45.842000]             character: 0,
Rust Analyzer[11:13:45.842000]         },
Rust Analyzer[11:13:45.842000]         end: Position {
Rust Analyzer[11:13:45.842000]             line: 5,
Rust Analyzer[11:13:45.842000]             character: 0,
Rust Analyzer[11:13:45.842000]         },
Rust Analyzer[11:13:45.842000]     },
Rust Analyzer[11:13:45.842000]     context: CodeActionContext {
Rust Analyzer[11:13:45.842000]         diagnostics: [],
Rust Analyzer[11:13:45.842000]         only: None,
Rust Analyzer[11:13:45.842000]     },
Rust Analyzer[11:13:45.842000]     work_done_progress_params: WorkDoneProgressParams {
Rust Analyzer[11:13:45.842000]         work_done_token: None,
Rust Analyzer[11:13:45.842000]     },
Rust Analyzer[11:13:45.842000]     partial_result_params: PartialResultParams {
Rust Analyzer[11:13:45.842000]         partial_result_token: None,
Rust Analyzer[11:13:45.842000]     },
Rust Analyzer[11:13:45.842000] }
Rust Analyzer[11:13:45.842000] 
Rust Analyzer[11:13:45.842000] thread '<unnamed>' panicked at 'index out of bounds: the len is 5 but the index is 5', crates/ide_db/src/line_index.rs:106:9
Rust Analyzer[11:13:45.842000] stack backtrace:
Rust Analyzer[11:13:45.843000] Panic context:
Rust Analyzer[11:13:45.843000] > 
Rust Analyzer[11:13:45.843000] version: ed4b312fa 2021-10-11 stable
Rust Analyzer[11:13:45.843000] request: textDocument/completion CompletionParams {
Rust Analyzer[11:13:45.843000]     text_document_position: TextDocumentPositionParams {
Rust Analyzer[11:13:45.843000]         text_document: TextDocumentIdentifier {
Rust Analyzer[11:13:45.843000]             uri: Url {
Rust Analyzer[11:13:45.843000]                 scheme: "file",
Rust Analyzer[11:13:45.843000]                 cannot_be_a_base: false,
Rust Analyzer[11:13:45.843000]                 username: "",
Rust Analyzer[11:13:45.843000]                 password: None,
Rust Analyzer[11:13:45.843000]                 host: None,
Rust Analyzer[11:13:45.843000]                 port: None,
Rust Analyzer[11:13:45.843000]                 path: "/Volumes/Macintosh%20HD/Users/lily/Desktop/foo/src/main.rs",
Rust Analyzer[11:13:45.843000]                 query: None,
Rust Analyzer[11:13:45.843000]                 fragment: None,
Rust Analyzer[11:13:45.843000]             },
Rust Analyzer[11:13:45.843000]         },
Rust Analyzer[11:13:45.843000]         position: Position {
Rust Analyzer[11:13:45.843000]             line: 5,
Rust Analyzer[11:13:45.843000]             character: 0,
Rust Analyzer[11:13:45.843000]         },
Rust Analyzer[11:13:45.843000]     },
Rust Analyzer[11:13:45.843000]     work_done_progress_params: WorkDoneProgressParams {
Rust Analyzer[11:13:45.843000]         work_done_token: None,
Rust Analyzer[11:13:45.843000]     },
Rust Analyzer[11:13:45.843000]     partial_result_params: PartialResultParams {
Rust Analyzer[11:13:45.843000]         partial_result_token: None,
Rust Analyzer[11:13:45.843000]     },
Rust Analyzer[11:13:45.843000]     context: Some(
Rust Analyzer[11:13:45.843000]         CompletionContext {
Rust Analyzer[11:13:45.843000]             trigger_kind: TriggerCharacter,
Rust Analyzer[11:13:45.843000]             trigger_character: Some(
Rust Analyzer[11:13:45.843000]                 "f",
Rust Analyzer[11:13:45.843000]             ),
Rust Analyzer[11:13:45.843000]         },
Rust Analyzer[11:13:45.843000]     ),
Rust Analyzer[11:13:45.843000] }
Rust Analyzer[11:13:45.843000] 
Rust Analyzer[11:13:45.843000] thread '<unnamed>' panicked at 'index out of bounds: the len is 5 but the index is 5', crates/ide_db/src/line_index.rs:106:9
Rust Analyzer[11:13:45.854000]    0: _rust_begin_unwind
Rust Analyzer[11:13:45.854000]    1: core::panicking::panic_fmt
Rust Analyzer[11:13:45.854000]    2: core::panicking::panic_bounds_check
Rust Analyzer[11:13:45.854000]    3: rust_analyzer::from_proto::text_range
Rust Analyzer[11:13:45.854000]    4: rust_analyzer::from_proto::file_range
Rust Analyzer[11:13:45.854000]    5: rust_analyzer::handlers::handle_code_action
Rust Analyzer[11:13:45.854000]    6: std::panicking::try
Rust Analyzer[11:13:45.854000]    7: <F as threadpool::FnBox>::call_box
Rust Analyzer[11:13:45.854000] note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
Rust Analyzer[11:13:45.854000] stack backtrace:
Rust Analyzer[11:13:45.854000]    0: _rust_begin_unwind
Rust Analyzer[11:13:45.854000]    1: core::panicking::panic_fmt
Rust Analyzer[11:13:45.854000]    2: core::panicking::panic_bounds_check
Rust Analyzer[11:13:45.854000]    3: rust_analyzer::from_proto::file_position
Rust Analyzer[11:13:45.854000]    4: rust_analyzer::handlers::handle_completion
Rust Analyzer[11:13:45.854000]    5: std::panicking::try
Rust Analyzer[11:13:45.854000]    6: <F as threadpool::FnBox>::call_box
Rust Analyzer[11:13:45.854000] note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
Rust Analyzer[11:13:45.855000] server panicked: index out of bounds: the len is 5 but the index is 5
Rust Analyzer[11:13:45.855000] server panicked: index out of bounds: the len is 5 but the index is 5
Rust Analyzer[11:13:46.012000] Panic context:
Rust Analyzer[11:13:46.012000] > 
Rust Analyzer[11:13:46.012000] version: ed4b312fa 2021-10-11 stable
Rust Analyzer[11:13:46.012000] notification: textDocument/didChange
Rust Analyzer[11:13:46.012000] 
Rust Analyzer[11:13:46.012000] thread 'main' panicked at 'index out of bounds: the len is 5 but the index is 5', crates/ide_db/src/line_index.rs:106:9
Rust Analyzer[11:13:46.012000] stack backtrace:
Rust Analyzer[11:13:46.012000]    0: _rust_begin_unwind
Rust Analyzer[11:13:46.012000]    1: core::panicking::panic_fmt
Rust Analyzer[11:13:46.012000]    2: core::panicking::panic_bounds_check
Rust Analyzer[11:13:46.012000]    3: rust_analyzer::from_proto::text_range
Rust Analyzer[11:13:46.012000]    4: rust_analyzer::lsp_utils::apply_document_changes
Rust Analyzer[11:13:46.012000]    5: core::ops::function::FnOnce::call_once
Rust Analyzer[11:13:46.012000]    6: rust_analyzer::dispatch::NotificationDispatcher::on
Rust Analyzer[11:13:46.012000]    7: rust_analyzer::main_loop::<impl rust_analyzer::global_state::GlobalState>::handle_event
Rust Analyzer[11:13:46.012000]    8: rust_analyzer::main_loop::<impl rust_analyzer::global_state::GlobalState>::run
Rust Analyzer[11:13:46.012000]    9: rust_analyzer::main_loop::main_loop
Rust Analyzer[11:13:46.012000]   10: rust_analyzer::try_main
Rust Analyzer[11:13:46.012000]   11: rust_analyzer::main
Rust Analyzer[11:13:46.012000] note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
Rust Analyzer[11:13:46.012000] thread '<unnamed>' panicked at 'called `Result::unwrap()` on an `Err` value: "SendError(..)"', /Users/runner/.cargo/registry/src/github.com-1ecc6299db9ec823/lsp-server-0.5.2/src/stdio.rs:29:37
Rust Analyzer[11:13:46.012000] stack backtrace:
Rust Analyzer[11:13:46.012000]    0: _rust_begin_unwind
Rust Analyzer[11:13:46.012000]    1: core::panicking::panic_fmt
Rust Analyzer[11:13:46.012000]    2: core::result::unwrap_failed
Rust Analyzer[11:13:46.012000] note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
Rust[11:13:46.329000] Error: Task โ€œrustโ€ exited with a non-zero exit status: 101.

</details>

To Reproduce Steps to reproduce the behavior:

  1. cargo new foo in the terminal
  2. Drag the new foo folder into Nova
  3. Open src/main.rs
  4. Edit the file

Additional information I haven't attempted to reproduce this with a fresh project.

In addition, after collecting that log, I cleared the console, opted to Restart Server, and then noticed that every cursor movement would log another panic (but would not tell me that the language server crashed). Hitting Save informed me of the crash again. I hit Restart Server again, and got

Rust Analyzer[11:18:54.454000] waiting for cargo metadata or cargo check
Rust Analyzer[11:18:56.002000] content modified

At this point every time I moved the cursor I'd get a line like

Rust Analyzer[11:18:56.878000] canceled by client

I hit Save again and this time it finally showed

Rust[11:19:05.080000]     Checking foo v0.1.0 (/Users/lily/Desktop/foo)
Rust[11:19:05.237000]     Finished dev [unoptimized + debuginfo] target(s) in 0.15s

Once I saw that, moving the cursor no longer logged anything, and I was able to make a trivial edit without logging too.

However, while writing up this report, after having left it alone for a while, I went back to Nova and inserted a single space at the end of the file, at which point the language server promptly crashed again.

Versions (please complete the following information):

  • macOS: macOS 11.6 (20G165)
  • Processor: Intel
  • Nova 7.5
  • Extension Version 1.0.3

created time in 10 days