Convert path module to OS path for parsing source code (via syn)?

Hello dear Rust community,

I would like to know if there is a way to transform the Path of module tree to an absolute OS path for parsing with "syn".

let module_path : &str = "crate::module::xxx";
// convert module_path to os_path ( crate::module::xxx => ./src/module/xxx.rs )
let source_code_file_path : &str = magic_convert(module_path); // <==== something like this
let syntax: syn::File = syn::parse_file(source_code_file_path).unwrap();

or another way to parse the module source code using its Path directly without converting ..

thanks a lot. :pray:

What kind of module paths do you want to support? Only those starting with crate or also those starting with super or self? How about paths to external crates? Also, do you need to support platforms like Windows where ./src/module/xx.rs is not a valid path?

1 Like

I just want to parse the contents of a module with its path,

  • crate:module:xxx
  • self:module:xxx
  • super:module:xxx
  • :module:xxx

for example, this code won't work, because it requires an OS path and not a Path with module tree format :

let syntax: syn::File = syn::parse_file("crate:module:xxx").unwrap();

I want to use syn::parse with a path like "{crate|self|super}:module:xxx".

the aim is to create a macro that receives a list of modules as a parameter, and scans them to list all the functions contained in each module.

is there a way to parse the module with its path in Rust format, or if not, convert the path to absolute format (OS folders) and then use the function syn::parse_file ?

In the general case it is not possible to determine what the path is without (partly) compiling the program, because

  • modules can have their source path overridden (#[path] attribute)
  • macros can generate modules
  • modules can be re-exported from other modules
2 Likes

I don't know your exact use case, but I don't think there's a general solution, and you- probably have to implement it yourself for your specific need.

generally, module paths don't have a definitive map to file paths, some examples:

// lib.rs
#[path = "generated/foobar.rs"]
mod foo;
pub use foo as foo2;
mod bar {
    #[path = "not-foo.rs"]
    pub mod foo;
    pub  use crate::foo as foo2;
    mod baz {
        pub use super::foo;
    }
    pub use self::baz as baz2;
}
pub use crate::bar as bar2;
3 Likes

I see more clearly now, this constraint greatly limits the possibilities of the Macros, so I need to find another way to simulate "Reflection".

I wonder, how does rust-analyzer parse and determine the path of source files only by typing lines like xxx:yyyy:func() ?

it is non trvial to replicate the behavior of rust-anaylzer, as rust-analyzer has the full context around the module path: e.g. it parses the entire code from the crate root module, in addition to information about all the depencencies via cargo metadata,it compiles and runs build.rs, it can expands macros (both declarative and procedural, which needs to run proc-macro crates in a host process), and it even understands semantics of the code to certain extent (not as much as the type checker, but still enough to provide useful code actions). in other words, it basically IS a compiler front end, just less capable (and maybe incorrect sometimes) than e.g. rustc.

1 Like

by the way, are you writing an developer tool, or a user library? if you are writing a "reflection" library, you might just use the information provided by the compiler. for example, your library can provide a attribute-like procedural macro (let's call it reflect), and the user must annotate their code, something like:

#[reflect(functions, constants, sub_modules)]
mod foo {
    mod bar {
        #[reflect(fields)]
        struct Foo {
            x: i32,
        }
    }
}

if you are writing a tool, did you consider use a clippy lint?

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.