1use crate::{
2 diagnostic::{self, DiagnosticExt as _, DiagnosticPrinter},
3 preproc::Preprocessor
4};
5use camino::Utf8Path;
6use cargo_metadata::{Edition, Metadata, Package, Target};
7use either::Either;
8use hashbrown::{HashMap, HashSet};
9use miette::{IntoDiagnostic as _, NamedSource};
10use proc_macro2::{Span, TokenStream, TokenTree};
11use quote::ToTokens as _;
12use semver::{Comparator, Op, Version, VersionReq};
13use serde::Serialize;
14use std::{
15 collections::VecDeque,
16 fmt::{self, Debug, Formatter},
17 fs::File,
18 io::{self, BufReader, Cursor, Read, Write},
19 path::Path,
20 process::{Command, Output}
21};
22use syn::{
23 parse::{Parse, ParseStream},
24 spanned::Spanned as _,
25 Expr, ExprLit, Ident, Item, ItemMacro, ItemUse, Lit, LitStr, Meta, Token, UsePath,
26 UseTree, Visibility
27};
28
29type ScopeScope = HashMap<String, VecDeque<(LinkType, String)>>;
30
31pub struct Scope {
32 pub scope: ScopeScope,
34 pub privmods: HashSet<String>
36}
37
38impl Debug for Scope {
39 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
40 struct ScopeScopeDebug<'a>(&'a ScopeScope);
41 impl Debug for ScopeScopeDebug<'_> {
42 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
43 let mut dbg = f.debug_map();
44 for (name, paths) in self.0 {
45 match paths.len() {
46 0 => {
47 continue;
49 },
50 1 => {
51 dbg.entry(name, &paths[0]);
52 },
53 _ => {
54 dbg.entry(name, paths);
55 }
56 }
57 }
58 dbg.finish()
59 }
60 }
61
62 f.debug_struct("Scope")
63 .field("scope", &ScopeScopeDebug(&self.scope))
64 .field("privmods", &self.privmods)
65 .finish()
66 }
67}
68
69#[derive(Clone, Copy, Debug, Eq, PartialEq)]
70pub enum LinkType {
71 Attr,
73 Const,
74 Derive,
76 Enum,
77 ExternCrate,
78 Function,
79 Macro,
81 Mod,
82 Static,
83 Struct,
84 Trait,
85 TraitAlias,
86 Type,
87 Union,
88
89 Use,
91 PubUse,
93
94 Primitive
96}
97
98fn make_prelude<const N: usize>(
99 prelude: [(&'static str, &'static str, LinkType); N]
100) -> ScopeScope {
101 prelude
102 .into_iter()
103 .flat_map(|(name, path, link_type)| {
104 let path = match path {
105 "" => format!("::std::{name}"),
106 _ => format!("::std::{path}::{name}")
107 };
108 let items: VecDeque<_> = [(link_type, path)].into_iter().collect();
109 match link_type {
110 LinkType::Macro => Either::Left(
111 [(name.into(), items.clone()), (format!("{name}!"), items)]
112 .into_iter()
113 ),
114 _ => Either::Right([(name.into(), items)].into_iter())
115 }
116 })
117 .collect()
118}
119
120impl Scope {
121 fn insert<K, V>(&mut self, key: K, ty: LinkType, value: V)
122 where
123 K: Into<String>,
124 V: Into<String>
125 {
126 self.scope
127 .entry(key.into())
128 .or_default()
129 .push_front((ty, value.into()));
130 }
131
132 pub(crate) fn empty() -> Self {
133 Self {
134 scope: HashMap::new(),
135 privmods: HashSet::new()
136 }
137 }
138
139 pub fn prelude(edition: Edition) -> Self {
141 let mut scope = Self {
142 scope: make_prelude([
143 ("bool", "", LinkType::Primitive),
145 ("char", "", LinkType::Primitive),
146 ("f32", "", LinkType::Primitive),
147 ("f64", "", LinkType::Primitive),
148 ("i128", "", LinkType::Primitive),
149 ("i16", "", LinkType::Primitive),
150 ("i32", "", LinkType::Primitive),
151 ("i64", "", LinkType::Primitive),
152 ("i8", "", LinkType::Primitive),
153 ("isize", "", LinkType::Primitive),
154 ("str", "", LinkType::Primitive),
155 ("u128", "", LinkType::Primitive),
156 ("u16", "", LinkType::Primitive),
157 ("u32", "", LinkType::Primitive),
158 ("u64", "", LinkType::Primitive),
159 ("u8", "", LinkType::Primitive),
160 ("usize", "", LinkType::Primitive),
161 ("Copy", "marker", LinkType::Trait),
163 ("Send", "marker", LinkType::Trait),
164 ("Sized", "marker", LinkType::Trait),
165 ("Sync", "marker", LinkType::Trait),
166 ("Unpin", "marker", LinkType::Trait),
167 ("Drop", "ops", LinkType::Trait),
168 ("Fn", "ops", LinkType::Trait),
169 ("FnMut", "ops", LinkType::Trait),
170 ("FnOnce", "ops", LinkType::Trait),
171 ("drop", "mem", LinkType::Function),
172 ("Box", "boxed", LinkType::Struct),
173 ("ToOwned", "borrow", LinkType::Trait),
174 ("Clone", "clone", LinkType::Trait),
175 ("PartialEq", "cmp", LinkType::Trait),
176 ("PartialOrd", "cmp", LinkType::Trait),
177 ("Eq", "cmp", LinkType::Trait),
178 ("Ord", "cmp", LinkType::Trait),
179 ("AsRef", "convert", LinkType::Trait),
180 ("AsMut", "convert", LinkType::Trait),
181 ("Into", "convert", LinkType::Trait),
182 ("From", "convert", LinkType::Trait),
183 ("Default", "default", LinkType::Trait),
184 ("Iterator", "iter", LinkType::Trait),
185 ("Extend", "iter", LinkType::Trait),
186 ("IntoIterator", "iter", LinkType::Trait),
187 ("DoubleEndedIterator", "iter", LinkType::Trait),
188 ("ExactSizeIterator", "iter", LinkType::Trait),
189 ("Option", "option", LinkType::Enum),
190 ("Some", "option::Option", LinkType::Use),
191 ("None", "option::Option", LinkType::Use),
192 ("Result", "result", LinkType::Struct),
193 ("Ok", "result::Result", LinkType::Use),
194 ("Err", "result::Result", LinkType::Use),
195 ("String", "string", LinkType::Struct),
196 ("ToString", "string", LinkType::Trait),
197 ("Vec", "vec", LinkType::Struct),
198 ("assert", "", LinkType::Macro),
200 ("assert_eq", "", LinkType::Macro),
201 ("assert_ne", "", LinkType::Macro),
202 ("cfg", "", LinkType::Macro),
203 ("column", "", LinkType::Macro),
204 ("compile_error", "", LinkType::Macro),
205 ("concat", "", LinkType::Macro),
206 ("dbg", "", LinkType::Macro),
207 ("debug_assert", "", LinkType::Macro),
208 ("debug_assert_eq", "", LinkType::Macro),
209 ("debug_assert_ne", "", LinkType::Macro),
210 ("env", "", LinkType::Macro),
211 ("eprint", "", LinkType::Macro),
212 ("eprintln", "", LinkType::Macro),
213 ("file", "", LinkType::Macro),
214 ("format", "", LinkType::Macro),
215 ("format_args", "", LinkType::Macro),
216 ("include", "", LinkType::Macro),
217 ("include_bytes", "", LinkType::Macro),
218 ("include_str", "", LinkType::Macro),
219 ("is_x86_feature_detected", "", LinkType::Macro),
220 ("line", "", LinkType::Macro),
221 ("matches", "", LinkType::Macro),
222 ("module_path", "", LinkType::Macro),
223 ("option_env", "", LinkType::Macro),
224 ("panic", "", LinkType::Macro),
225 ("print", "", LinkType::Macro),
226 ("println", "", LinkType::Macro),
227 ("stringify", "", LinkType::Macro),
228 ("thread_local", "", LinkType::Macro),
229 ("todo", "", LinkType::Macro),
230 ("unimplemented", "", LinkType::Macro),
231 ("unreachable", "", LinkType::Macro),
232 ("vec", "", LinkType::Macro),
233 ("write", "", LinkType::Macro),
234 ("writeln", "", LinkType::Macro)
235 ]),
236 privmods: HashSet::new()
237 };
238
239 if edition >= Edition::E2021 {
240 for (key, value) in make_prelude([
242 ("TryInto", "convert", LinkType::Use),
243 ("TryFrom", "convert", LinkType::Use),
244 ("FromIterator", "iter", LinkType::Use)
245 ]) {
246 scope.scope.insert(key, value);
247 }
248 }
249
250 if edition >= Edition::E2024 {
251 for (key, value) in make_prelude([
253 ("Future", "future", LinkType::Trait),
254 ("IntoFuture", "future", LinkType::Trait)
255 ]) {
256 scope.scope.insert(key, value);
257 }
258 }
259
260 scope
261 }
262}
263
264#[derive(Debug)]
265pub struct CrateCode {
266 pub filename: String,
267 pub code: String
268}
269
270impl From<&CrateCode> for NamedSource<String> {
271 fn from(code: &CrateCode) -> Self {
272 NamedSource::new(&code.filename, code.code.clone()).with_language("Rust")
273 }
274}
275
276impl CrateCode {
277 pub fn new_unknown() -> Self {
278 Self {
279 filename: "<unknown>".into(),
280 code: String::new()
281 }
282 }
283
284 fn read_from<R>(filename: String, read: R) -> io::Result<Self>
285 where
286 R: io::BufRead
287 {
288 let mut preproc = Preprocessor::new(read);
289 let mut code = String::new();
290 preproc.read_to_string(&mut code)?;
291
292 Ok(Self { filename, code })
293 }
294
295 pub fn read_from_disk<P>(path: P) -> io::Result<Self>
296 where
297 P: AsRef<Utf8Path>
298 {
299 let path = path.as_ref();
300 let filename = path.file_name().unwrap_or(path.as_str());
301 Self::read_from(filename.into(), BufReader::new(File::open(path)?))
302 }
303
304 pub fn read_expansion<P>(
305 manifest_path: Option<P>,
306 package: Option<&str>,
307 target: &Target,
308 features: Option<&str>,
309 no_default_features: bool,
310 all_features: bool
311 ) -> miette::Result<CrateCode>
312 where
313 P: AsRef<Path>
314 {
315 let mut cmd = Command::new("cargo");
316 cmd.arg("+nightly").arg("rustc");
317 if let Some(manifest_path) = manifest_path {
318 cmd.arg("--manifest-path").arg(manifest_path.as_ref());
319 }
320 if let Some(package) = package {
321 cmd.arg("-p").arg(package);
322 }
323 if let Some(features) = features {
324 cmd.arg("--features").arg(features);
325 }
326 if no_default_features {
327 cmd.arg("--no-default-features");
328 }
329 if all_features {
330 cmd.arg("--all-features");
331 }
332 if target.is_lib() {
333 cmd.arg("--lib");
334 } else if target.is_bin() {
335 cmd.arg("--bin").arg(&target.name);
336 }
337 cmd.arg("--").arg("-Zunpretty=expanded");
338
339 let Output {
340 stdout,
341 stderr,
342 status
343 } = cmd.output()
344 .map_err(|err| diagnostic::ExecError::new(err, &cmd))?;
345
346 if !status.success() {
347 io::stdout()
350 .lock()
351 .write_all(stderr.as_slice())
352 .expect("Failed to write cargo errors to stdout");
353
354 let err = diagnostic::ExecError::new(
355 format!("Command finished with non-zero exit code {status}"),
356 &cmd
357 );
358 return Err(err.into());
359 }
360
361 Self::read_from("<cargo-expand>".into(), Cursor::new(stdout)).into_diagnostic()
362 }
363}
364
365#[derive(Clone, Copy, Debug, Serialize)]
369#[serde(rename_all = "lowercase")]
370pub enum TargetType {
371 Bin,
372 Lib
373}
374
375#[derive(Debug)]
376pub struct InputFile {
377 pub crate_name: String,
379 pub crate_version: Version,
381 pub target_type: TargetType,
383 pub repository: Option<String>,
385 pub license: Option<String>,
387 pub rust_version: Option<Version>,
389 pub rustdoc: String,
391 pub dependencies: HashMap<String, Dependency>,
394 pub scope: Scope
396}
397
398impl InputFile {
399 pub fn dummy() -> Self {
400 Self {
401 crate_name: "N/A".into(),
402 crate_version: Version::new(0, 0, 0),
403 target_type: TargetType::Lib,
404 repository: None,
405 license: None,
406 rust_version: None,
407 rustdoc: String::new(),
408 dependencies: HashMap::new(),
409 scope: Scope::empty()
410 }
411 }
412}
413
414pub struct Dependency {
415 pub crate_name: String,
417
418 pub req: VersionReq,
420
421 pub version: Version
423}
424
425impl Dependency {
426 pub fn new(crate_name: String, req: VersionReq, version: Version) -> Self {
427 Self {
428 crate_name,
429 req,
430 version
431 }
432 }
433
434 pub fn as_tuple(&self) -> (&str, Option<&Version>) {
435 (self.crate_name.as_str(), Some(&self.version))
436 }
437}
438
439impl Debug for Dependency {
440 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
441 write!(
442 f,
443 "{} = \"{}\" ({})",
444 self.crate_name, self.req, self.version
445 )
446 }
447}
448
449pub fn read_code<W: io::Write>(
450 metadata: &Metadata,
451 pkg: &Package,
452 code: &CrateCode,
453 target_type: TargetType,
454 printer_stderr: W
455) -> miette::Result<InputFile> {
456 let crate_name = pkg.name.as_str().to_owned();
457 let crate_version = pkg.version.clone();
458 let repository = pkg.repository.clone();
459 let license = pkg.license.clone();
460 let rust_version = pkg.rust_version.clone();
461
462 let file = match syn::parse_file(code.code.as_str()) {
463 Ok(file) => file,
464 Err(err) => {
465 let err = diagnostic::SyntaxError::new_rust(code, err);
466 return Err(err.into());
467 }
468 };
469
470 let mut printer = DiagnosticPrinter::new(code, printer_stderr);
471
472 let rustdoc = read_rustdoc_from_file(&file, &mut printer)?;
473 let dependencies = resolve_dependencies(metadata, pkg, &mut printer);
474 let scope = read_scope_from_file(pkg, &file, &mut printer)?;
475
476 Ok(InputFile {
477 crate_name,
478 crate_version,
479 target_type,
480 repository,
481 license,
482 rust_version,
483 rustdoc,
484 dependencies,
485 scope
486 })
487}
488
489fn read_rustdoc_from_file<W: io::Write>(
490 file: &syn::File,
491 printer: &mut DiagnosticPrinter<'_, W>
492) -> miette::Result<String> {
493 let mut doc = String::new();
494 let mut errs = Vec::new();
495 for attr in &file.attrs {
496 match &attr.meta {
497 Meta::NameValue(nv) if nv.path.is_ident("doc") => {
498 match parse_doc_attr(&nv.value, printer) {
499 Ok(Some(str)) => {
500 doc.push('\n');
501 doc.push_str(&str.value());
502 },
503 Ok(None) => {},
504 Err(err) => {
505 errs.push(err);
506 }
507 }
508 },
509
510 Meta::List(l) if l.path.is_ident("cfg_attr") => {
511 parse_cfg_attr(l.tokens.clone(), attr.span(), printer);
512 },
513
514 _ => {}
515 }
516 }
517 if !errs.is_empty() {
518 let err = diagnostic::SyntaxError::new_rust_multi(printer.code, errs);
519 Err(err.into())
520 } else {
521 Ok(doc)
522 }
523}
524
525fn parse_doc_attr<W: io::Write>(
528 expr: &Expr,
529 printer: &mut DiagnosticPrinter<'_, W>
530) -> syn::Result<Option<LitStr>> {
531 match expr {
532 Expr::Lit(ExprLit {
533 lit: Lit::Str(lit), ..
534 }) => Ok(Some(lit.clone())),
535 Expr::Macro(makro) => {
536 diagnostic::MacroNotExpanded::new(printer.code, makro.span()).print(printer);
537 Ok(None)
538 },
539 expr => Err(syn::Error::new(
540 expr.span(),
541 "Expected string literal or macro invokation"
542 ))
543 }
544}
545
546fn parse_cfg_attr<W: io::Write>(
549 tokens: TokenStream,
550 span: Span,
551 printer: &mut DiagnosticPrinter<'_, W>
552) {
553 struct CfgAttr;
554
555 impl Parse for CfgAttr {
556 fn parse(input: ParseStream) -> syn::Result<Self> {
557 while !input.peek(Token![,]) {
559 let _: TokenTree = input.parse()?;
560 }
561 let _: Token![,] = input.parse()?;
562
563 let path: syn::Path = input.parse()?;
564 if !path.is_ident("doc") {
565 return Err(syn::Error::new(
566 path.span(),
567 format!("Expected `doc`, found `{}`", path.into_token_stream())
568 ));
569 }
570
571 let _: Token![=] = input.parse()?;
572 let _: TokenStream = input.parse()?;
573 Ok(CfgAttr)
574 }
575 }
576
577 if syn::parse2::<CfgAttr>(tokens).is_ok() {
578 diagnostic::MacroNotExpanded::new(printer.code, span).print(printer);
579 }
580}
581
582fn sanitize_crate_name<T: AsRef<str>>(name: T) -> String {
583 name.as_ref().replace('-', "_")
584}
585
586fn resolve_dependencies<W: io::Write>(
587 metadata: &Metadata,
588 pkg: &Package,
589 printer: &mut DiagnosticPrinter<'_, W>
590) -> HashMap<String, Dependency> {
591 let mut deps = HashMap::new();
592
593 let version = pkg.version.clone();
599 deps.insert(
600 sanitize_crate_name(&pkg.name),
601 Dependency::new(
602 pkg.name.as_str().to_owned(),
603 [Comparator {
604 op: Op::Exact,
605 major: version.major,
606 minor: Some(version.minor),
607 patch: Some(version.patch),
608 pre: version.pre.clone()
609 }]
610 .into_iter()
611 .collect(),
612 version
613 )
614 );
615
616 for dep in &pkg.dependencies {
617 let dep_name = sanitize_crate_name(&dep.name);
618 let version = metadata
619 .packages
620 .iter()
621 .filter(|pkg| pkg.name.as_str() == dep.name)
622 .map(|pkg| &pkg.version)
623 .find(|pkgver| dep.req.matches(pkgver));
624 let rename = dep.rename.as_ref().unwrap_or(&dep_name);
625
626 if let Some(version) = version {
627 if deps
628 .get(rename)
629 .map(|dep| dep.version < *version)
630 .unwrap_or(true)
631 {
632 deps.insert(
633 rename.to_owned(),
634 Dependency::new(dep_name, dep.req.clone(), version.to_owned())
635 );
636 }
637 } else {
638 diagnostic::NoDependencyVersion::new(dep.name.clone()).print(printer);
639 }
640 }
641
642 deps
643}
644
645struct ScopeEditor<'a, 'w, W: io::Write> {
646 scope: &'a mut Scope,
647 crate_name: &'a str,
648 printer: &'a mut DiagnosticPrinter<'w, W>,
649 glob_warn: Option<diagnostic::GlobImport>
650}
651
652impl<W: io::Write> Drop for ScopeEditor<'_, '_, W> {
653 fn drop(&mut self) {
654 if let Some(warning) = self.glob_warn.take() {
655 self.printer.print(warning);
656 }
657 }
658}
659
660impl<'a, 'w, W: io::Write> ScopeEditor<'a, 'w, W> {
661 fn new(
662 scope: &'a mut Scope,
663 crate_name: &'a str,
664 printer: &'a mut DiagnosticPrinter<'w, W>
665 ) -> Self {
666 Self {
667 scope,
668 crate_name,
669 printer,
670 glob_warn: None
671 }
672 }
673
674 fn add_privmod(&mut self, ident: &Ident) {
675 self.scope.privmods.insert(ident.to_string());
676 }
677
678 fn insert(&mut self, ident: &Ident, ty: LinkType) {
679 let path = format!("::{}::{ident}", self.crate_name);
680 self.scope.insert(ident.to_string(), ty, path);
681 }
682
683 fn insert_fun(&mut self, ident: &Ident) {
684 let path = format!("::{}::{ident}", self.crate_name);
685 self.scope
686 .insert(ident.to_string(), LinkType::Function, &path);
687 self.scope
688 .insert(format!("{ident}()"), LinkType::Function, path);
689 }
690
691 fn insert_macro(&mut self, ident: &Ident) {
692 let path = format!("::{}::{ident}", self.crate_name);
693 self.scope.insert(ident.to_string(), LinkType::Macro, &path);
694 self.scope
695 .insert(format!("{ident}!"), LinkType::Macro, path);
696 }
697
698 fn insert_use_tree(&mut self, vis: &Visibility, tree: &UseTree) {
699 self.insert_use_tree_impl(vis, String::new(), tree)
700 }
701
702 fn insert_use_tree_impl(&mut self, vis: &Visibility, prefix: String, tree: &UseTree) {
703 match tree {
704 UseTree::Path(path) if prefix.is_empty() && path.ident == "self" => {
705 self.insert_use_tree_impl(vis, prefix, &path.tree)
706 },
707 UseTree::Path(path) => self.insert_use_tree_impl(
708 vis,
709 format!("{prefix}{}::", path.ident),
710 &path.tree
711 ),
712 UseTree::Name(name) => {
713 if !prefix.is_empty() {
716 self.insert_use_item(vis, &prefix, &name.ident, &name.ident);
717 }
718 },
719 UseTree::Rename(name) => {
720 self.insert_use_item(vis, &prefix, &name.rename, &name.ident);
721 },
722 UseTree::Glob(glob) => {
723 let span = glob.star_token.spans[0];
724 if let Some(warning) = &mut self.glob_warn {
725 warning.add_span(span);
726 } else {
727 self.glob_warn =
728 Some(diagnostic::GlobImport::new(self.printer.code, span));
729 }
730 },
731 UseTree::Group(group) => {
732 for tree in &group.items {
733 self.insert_use_tree_impl(vis, prefix.clone(), tree);
734 }
735 },
736 };
737 }
738
739 fn insert_use_item(
740 &mut self,
741 vis: &Visibility,
742 prefix: &str,
743 rename: &Ident,
744 ident: &Ident
745 ) {
746 if matches!(vis, Visibility::Public(_)) {
747 self.insert(rename, LinkType::PubUse);
748 }
749 self.scope.insert(
750 rename.to_string(),
751 LinkType::Use,
752 format!("{prefix}{ident}")
753 );
754 }
755}
756
757fn is_public(vis: &Visibility) -> bool {
758 matches!(vis, Visibility::Public(_))
759}
760
761fn is_exported(mac: &ItemMacro) -> bool {
762 mac.attrs
763 .iter()
764 .any(|attr| attr.path().is_ident("macro_export"))
765}
766
767fn read_scope_from_file<W: io::Write>(
768 pkg: &Package,
769 file: &syn::File,
770 printer: &mut DiagnosticPrinter<'_, W>
771) -> miette::Result<Scope> {
772 let crate_name = sanitize_crate_name(&pkg.name);
773 let mut scope = Scope::prelude(pkg.edition);
774 let mut editor = ScopeEditor::new(&mut scope, &crate_name, printer);
775 let mut errs = Vec::new();
776
777 'items: for i in &file.items {
778 match i {
779 Item::Const(i) if is_public(&i.vis) => {
780 editor.insert(&i.ident, LinkType::Const)
781 },
782 Item::Enum(i) if is_public(&i.vis) => editor.insert(&i.ident, LinkType::Enum),
783 Item::ExternCrate(i)
784 if is_public(&i.vis) && i.ident != "self" && i.rename.is_some() =>
785 {
786 editor.scope.insert(
787 i.rename.as_ref().unwrap().1.to_string(),
788 LinkType::ExternCrate,
789 format!("::{}", i.ident)
790 );
791 },
792 Item::Fn(i) if is_public(&i.vis) => {
793 for a in &i.attrs {
794 let ap = match &a.meta {
795 Meta::Path(p) => p,
796 Meta::List(l) => &l.path,
797 Meta::NameValue(nv) => &nv.path
798 };
799 let ai = match ap.get_ident() {
800 Some(ai) => ai,
801 None => continue
802 };
803 if ai == "proc_macro" {
804 editor.insert_macro(&i.sig.ident);
805 } else if ai == "proc_macro_attribute" {
806 editor.insert(&i.sig.ident, LinkType::Attr);
807 } else if ai == "proc_macro_derive" {
808 let Meta::List(l) = &a.meta else {
809 errs.push(syn::Error::new(
810 a.meta.span(),
811 "Expected proc_macro_derive to specify a name for the derive macro"
812 ));
813 continue 'items;
814 };
815 let Some(derive_ident) =
816 l.tokens.clone().into_iter().next().and_then(|token| {
817 match token {
818 TokenTree::Ident(ident) => Some(ident),
819 _ => None
820 }
821 })
822 else {
823 errs.push(syn::Error::new(
824 l.tokens.span(),
825 "Expected proc_macro_derive to specify a name for the derive macro"
826 ));
827 continue 'items;
828 };
829 editor.insert(&derive_ident, LinkType::Derive);
830 } else {
831 continue;
832 }
833 continue 'items;
836 }
837 editor.insert_fun(&i.sig.ident)
838 },
839 Item::Macro(i) if is_exported(i) && i.ident.is_some() => {
840 editor.insert_macro(i.ident.as_ref().unwrap())
841 },
842 Item::Mod(i) if is_public(&i.vis) => editor.insert(&i.ident, LinkType::Mod),
843 Item::Mod(i) => editor.add_privmod(&i.ident),
844 Item::Static(i) if is_public(&i.vis) => {
845 editor.insert(&i.ident, LinkType::Static)
846 },
847 Item::Struct(i) if is_public(&i.vis) => {
848 editor.insert(&i.ident, LinkType::Struct)
849 },
850 Item::Trait(i) if is_public(&i.vis) => {
851 editor.insert(&i.ident, LinkType::Trait)
852 },
853 Item::TraitAlias(i) if is_public(&i.vis) => {
854 editor.insert(&i.ident, LinkType::TraitAlias)
855 },
856 Item::Type(i) if is_public(&i.vis) => editor.insert(&i.ident, LinkType::Type),
857 Item::Union(i) if is_public(&i.vis) => {
858 editor.insert(&i.ident, LinkType::Union)
859 },
860 Item::Use(i) if !is_prelude_import(i) => {
861 editor.insert_use_tree(&i.vis, &i.tree)
862 },
863 _ => {}
864 };
865 }
866 drop(editor);
867
868 for values in &mut scope.scope.values_mut() {
870 let mut i = 0;
871 while i < values.len() {
872 if values[i].0 == LinkType::Use {
873 let path = &values[i].1;
874 if (!path.starts_with("::")
875 || path.starts_with(&format!("::{crate_name}::")))
876 && Some(path.split("::").collect::<Vec<_>>())
877 .map(|segments| {
878 segments.len() > 1 && scope.privmods.contains(segments[0])
879 })
880 .unwrap()
881 {
882 values.remove(i);
883 continue;
884 }
885 }
886
887 i += 1;
888 }
889 }
890
891 if !errs.is_empty() {
892 let err = diagnostic::SyntaxError::new_rust_multi(printer.code, errs);
893 Err(err.into())
894 } else {
895 Ok(scope)
896 }
897}
898
899fn is_prelude_import(item_use: &ItemUse) -> bool {
900 match &item_use.tree {
901 UseTree::Path(UsePath { ident, tree, .. })
902 if ident == "std" || ident == "core" =>
903 {
904 match tree.as_ref() {
905 UseTree::Path(UsePath { ident, .. }) => ident == "prelude",
906 _ => false
907 }
908 },
909 _ => false
910 }
911}