cbindgen icon indicating copy to clipboard operation
cbindgen copied to clipboard

Handle fully qualified paths and `use` aliases

Open eqrion opened this issue 8 years ago • 14 comments
trafficstars

Each Rust Item has a path that includes the crate and mods needed to get to it. Currently cbindgen just ignores all segments of a path except the last one, which is the name of the item.

This works as long as there are no duplicates in different modules, but isn't correct and will probably cause someone an issue someday.

eqrion avatar May 09 '17 20:05 eqrion

My comments earlier were wrong.

cbindgen used to refuse to parse paths that had multiple segments. This wasn't great so I fixed it in 015572877a9eec5b538c71ff73ddedae40e6210c.

eqrion avatar Oct 10 '17 02:10 eqrion

Any updates on this? I've got multiple struct Configs in different modules and want to export them all.

  • What's the best workaround currently?
  • Do you have any specific plans on implementing this?
  • If it's not very hard, I could try to implement the feature (equipped with some instructions).

AdamRzepka avatar May 21 '18 09:05 AdamRzepka

Unfortunately, I'm not sure there is a workaround yet besides renaming the types. My first thought would have been to use the structs behind type aliases with different names, but that won't work if you want to expose them..

I don't currently have specific plans on implementing this. Unfortunately this is a tricky issue with a couple different options.

  1. Implement a custom path resolution algorithm on top of syn
  2. Use rls-analysis to hook into the work going on there for goto definition
  3. Write a rustc integration to get this data

I've been told by @Gankro that option 1 is not going to be fun as this is a very complicated and not well understood part of rustc and has evolved. I think this part of the rust reference is relevant.

Using rls-analysis could be viable, but needs investigation. My concern with using rls would be if it is using a lossy/mostly correct goto definition feature and not focused on strict correctness.

I have no idea of how feasible option 3 would be.

eqrion avatar May 21 '18 19:05 eqrion

Issue: Import renames à la use Foo as Bar; are not resolved properly.

A minimalistic crate like this …

// lib.rs

pub struct Foo { pub field: bool }

use Foo as Bar;

#[no_mangle]
pub unsafe extern "C" fn placebo(_bar: &Bar) {}

… generates a header like this …

// lib.h

#include <cstdint>
#include <cstdlib>

extern "C" {

void placebo(const Bar *_bar);

} // extern "C"

… emitting the following warning:

WARN: Can't find Bar. This usually means that this type was incompatible or not found.

… while it should generate something like this instead:

// lib.h

#include <cstdint>
#include <cstdlib>

struct Foo;

extern "C" {

void placebo(const Foo *_bar);

} // extern "C"

What's wrong:

  1. cbindgen doesn't recognize that Foo is used in an exported fn thus does not generate it.
  2. _bar: &Bar is not properly resolved to _bar: &Foo in the function argument.

(Cross-posted from https://github.com/eqrion/cbindgen/issues/200, which was closed in favor of this original issue)

regexident avatar Aug 22 '18 20:08 regexident

Issue: Incorrect type resolving for differently scoped types of same name.

A minimalistic crate like this …

// lib.rs

pub mod foo {
    pub struct Foobar<T> { pub field: T }
}

pub mod bar {
    pub struct Foobar { pub field: u8 }

    #[no_mangle]
    pub unsafe extern "C" fn placebo(_foobar: &Foobar) {}
}

… generates a header like this …

// lib.h

#include <cstdint>
#include <cstdlib>

template<typename T>
struct Foobar;

extern "C" {

void placebo(const Foobar *_foobar);

} // extern "C"

… while it should generate something like this instead:

#include <cstdint>
#include <cstdlib>

struct Foobar;

extern "C" {

void placebo(const Foobar *_foobar);

} // extern "C"

What's wrong:

  1. Foobar<T> is not used in any exported fn and hence shouldn't be generated.
  2. Foobar however is used in an exported fn and hence should be generated.

Observations:

  1. Changing the order of mod foo and mod bar makes it behave correctly.
  2. Adding #[repr(C)] to bar::Foobar makes it behave correctly, strangely enough.
  3. The <T> doesn't seem to be strictly necessary for triggering this bug, but makes things easier to spot in the generated header.

(Cross-posted from https://github.com/eqrion/cbindgen/issues/199, which was closed in favor of this original issue)

regexident avatar Aug 22 '18 20:08 regexident

I just pushed a PR (https://github.com/eqrion/cbindgen/pull/201) that aims to move us a bit closer towards a direction of fixing this.

regexident avatar Aug 22 '18 21:08 regexident

This is an issue with https://github.com/KZen-networks/multi-party-ecdsa

ysangkok avatar Oct 13 '18 18:10 ysangkok

Did #233 fix this?

saschanaz avatar May 09 '20 19:05 saschanaz

Not really, though it added the infrastructure to potentially do it.

emilio avatar Sep 28 '20 17:09 emilio

Any updates here? Would love to see this implemented 🙏

UebelAndre avatar Nov 30 '20 15:11 UebelAndre

Not really, though it added the infrastructure to potentially do it.

Can you elaborate on this a bit? I know nothing about the internals of this project but would really like to see this feature implemented 😅

UebelAndre avatar Dec 07 '20 16:12 UebelAndre

@UebelAndre A look at the diff shows that the PR replaces the use of bare strings with

pub struct Path {
    name: String,
}

As such the PR has improved type-safety, but has not actually made the jump from "name-based" to "full qualified path-based", unlike what the name might suggest.

regexident avatar Dec 07 '20 19:12 regexident

My workaround for the "Issue: Import renames à la use Foo as Bar; are not resolved properly." issue is a macro for opaque newtype wrapping.

macro_rules! opaque {
    ($exported:ident, $internal:ty) => {
        pub struct $exported($internal);
        impl From<$internal> for $exported {
            fn from(v: $internal) -> Self {
                Self(v)
            }
        }
        impl From<$exported> for $internal {
            fn from(value: $exported) -> Self {
                value.0
            }
        }
    };
}

opaque!(LoadConfig, load::Config);
opaque!(EmitConfig, emit::Config);

(where the parse section of the cbindgen config carefully doesn't include either of the load or emit crates)

This only works in my specific case because these types are opaque (they're always provided behind a pointer). I don't know a workaround for non-opaque types.

aidanhs avatar Nov 10 '23 18:11 aidanhs