|
| 1 | +//! Parsing and validation of source item definitions |
| 2 | +//! |
| 3 | +//! This module contains structs for each of the source item types that can be documented with `NatSpec`. |
| 4 | +//! The [`Definition`] type provides a unified interface to interact with the various types. |
| 5 | +//! The [`find_items`] function takes the tree root [`Cursor`] from a Solidity document and returns all the parsed |
| 6 | +//! definitions contained within. |
| 7 | +//! Some helper functions allow to extract useful information from [`Cursor`]s. |
1 | 8 | use constructor::ConstructorDefinition; |
2 | 9 | use derive_more::{Display, From}; |
3 | 10 | use enumeration::EnumDefinition; |
@@ -304,7 +311,7 @@ impl Definition { |
304 | 311 | } |
305 | 312 | } |
306 | 313 |
|
307 | | -/// Find source item definitions from a root CST cursor |
| 314 | +/// Find source item definitions from a root CST [`Cursor`] |
308 | 315 | pub fn find_items(cursor: Cursor) -> Vec<Definition> { |
309 | 316 | let mut out = Vec::new(); |
310 | 317 | for m in cursor.query(vec![ |
@@ -357,7 +364,7 @@ pub fn find_items(cursor: Cursor) -> Vec<Definition> { |
357 | 364 |
|
358 | 365 | /// Extract parameters from a function-like source item. |
359 | 366 | /// |
360 | | -/// The node kind that holds the `Identifier` (`Parameter`, `EventParameter`) is provided as `kind`. |
| 367 | +/// The node kind that holds the `Identifier` (`Parameter`, `EventParameter`) must be provided with `kind`. |
361 | 368 | #[must_use] |
362 | 369 | pub fn extract_params(cursor: &Cursor, kind: NonterminalKind) -> Vec<Identifier> { |
363 | 370 | let mut cursor = cursor.spawn(); |
@@ -474,6 +481,60 @@ pub fn extract_identifiers(cursor: &Cursor) -> Vec<Identifier> { |
474 | 481 | out |
475 | 482 | } |
476 | 483 |
|
| 484 | +/// Extract the attributes (visibility and override) from a function-like item or state variable |
| 485 | +#[must_use] |
| 486 | +pub fn extract_attributes(cursor: &Cursor) -> Attributes { |
| 487 | + let mut cursor = cursor.spawn(); |
| 488 | + let mut out = Attributes::default(); |
| 489 | + while cursor.go_to_next_terminal_with_kinds(&[ |
| 490 | + TerminalKind::ExternalKeyword, |
| 491 | + TerminalKind::InternalKeyword, |
| 492 | + TerminalKind::PrivateKeyword, |
| 493 | + TerminalKind::PublicKeyword, |
| 494 | + TerminalKind::OverrideKeyword, |
| 495 | + ]) { |
| 496 | + match cursor |
| 497 | + .node() |
| 498 | + .as_terminal() |
| 499 | + .expect("should be terminal kind") |
| 500 | + .kind |
| 501 | + { |
| 502 | + TerminalKind::ExternalKeyword => out.visibility = Visibility::External, |
| 503 | + TerminalKind::InternalKeyword => out.visibility = Visibility::Internal, |
| 504 | + TerminalKind::PrivateKeyword => out.visibility = Visibility::Private, |
| 505 | + TerminalKind::PublicKeyword => out.visibility = Visibility::Public, |
| 506 | + TerminalKind::OverrideKeyword => out.r#override = true, |
| 507 | + _ => unreachable!(), |
| 508 | + } |
| 509 | + } |
| 510 | + out |
| 511 | +} |
| 512 | + |
| 513 | +/// Find the parent's name (contract, interface, library), if any |
| 514 | +#[must_use] |
| 515 | +pub fn extract_parent_name(mut cursor: Cursor) -> Option<Parent> { |
| 516 | + while cursor.go_to_parent() { |
| 517 | + if let Some(parent) = cursor.node().as_nonterminal_with_kinds(&[ |
| 518 | + NonterminalKind::ContractDefinition, |
| 519 | + NonterminalKind::InterfaceDefinition, |
| 520 | + NonterminalKind::LibraryDefinition, |
| 521 | + ]) { |
| 522 | + for child in &parent.children { |
| 523 | + if child.is_terminal_with_kind(TerminalKind::Identifier) { |
| 524 | + let name = child.node.unparse().trim().to_string(); |
| 525 | + return Some(match parent.kind { |
| 526 | + NonterminalKind::ContractDefinition => Parent::Contract(name), |
| 527 | + NonterminalKind::InterfaceDefinition => Parent::Interface(name), |
| 528 | + NonterminalKind::LibraryDefinition => Parent::Library(name), |
| 529 | + _ => unreachable!(), |
| 530 | + }); |
| 531 | + } |
| 532 | + } |
| 533 | + } |
| 534 | + } |
| 535 | + None |
| 536 | +} |
| 537 | + |
477 | 538 | /// Check a list of params to see if they are documented with a corresponding item in the [`NatSpec`], and generate a |
478 | 539 | /// diagnostic for each missing one or if there are more than 1 entry per param. |
479 | 540 | #[must_use] |
@@ -542,60 +603,6 @@ pub fn check_returns( |
542 | 603 | res |
543 | 604 | } |
544 | 605 |
|
545 | | -/// Extract the attributes (visibility and override) from a function-like item or state variable |
546 | | -#[must_use] |
547 | | -pub fn extract_attributes(cursor: &Cursor) -> Attributes { |
548 | | - let mut cursor = cursor.spawn(); |
549 | | - let mut out = Attributes::default(); |
550 | | - while cursor.go_to_next_terminal_with_kinds(&[ |
551 | | - TerminalKind::ExternalKeyword, |
552 | | - TerminalKind::InternalKeyword, |
553 | | - TerminalKind::PrivateKeyword, |
554 | | - TerminalKind::PublicKeyword, |
555 | | - TerminalKind::OverrideKeyword, |
556 | | - ]) { |
557 | | - match cursor |
558 | | - .node() |
559 | | - .as_terminal() |
560 | | - .expect("should be terminal kind") |
561 | | - .kind |
562 | | - { |
563 | | - TerminalKind::ExternalKeyword => out.visibility = Visibility::External, |
564 | | - TerminalKind::InternalKeyword => out.visibility = Visibility::Internal, |
565 | | - TerminalKind::PrivateKeyword => out.visibility = Visibility::Private, |
566 | | - TerminalKind::PublicKeyword => out.visibility = Visibility::Public, |
567 | | - TerminalKind::OverrideKeyword => out.r#override = true, |
568 | | - _ => unreachable!(), |
569 | | - } |
570 | | - } |
571 | | - out |
572 | | -} |
573 | | - |
574 | | -/// Find the parent's name (contract, interface, library), if any |
575 | | -#[must_use] |
576 | | -pub fn extract_parent_name(mut cursor: Cursor) -> Option<Parent> { |
577 | | - while cursor.go_to_parent() { |
578 | | - if let Some(parent) = cursor.node().as_nonterminal_with_kinds(&[ |
579 | | - NonterminalKind::ContractDefinition, |
580 | | - NonterminalKind::InterfaceDefinition, |
581 | | - NonterminalKind::LibraryDefinition, |
582 | | - ]) { |
583 | | - for child in &parent.children { |
584 | | - if child.is_terminal_with_kind(TerminalKind::Identifier) { |
585 | | - let name = child.node.unparse().trim().to_string(); |
586 | | - return Some(match parent.kind { |
587 | | - NonterminalKind::ContractDefinition => Parent::Contract(name), |
588 | | - NonterminalKind::InterfaceDefinition => Parent::Interface(name), |
589 | | - NonterminalKind::LibraryDefinition => Parent::Library(name), |
590 | | - _ => unreachable!(), |
591 | | - }); |
592 | | - } |
593 | | - } |
594 | | - } |
595 | | - } |
596 | | - None |
597 | | -} |
598 | | - |
599 | 606 | #[cfg(test)] |
600 | 607 | mod tests { |
601 | 608 | use similar_asserts::assert_eq; |
|
0 commit comments