Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Efficient Node Localization in Syntax Tree for Unit Tests #2251

Open
Matejkob opened this issue Sep 29, 2023 · 2 comments
Open

Introduce Efficient Node Localization in Syntax Tree for Unit Tests #2251

Matejkob opened this issue Sep 29, 2023 · 2 comments
Labels
enhancement New feature or request

Comments

@Matejkob
Copy link
Contributor

Description

Currently, there is no mechanism to efficiently refer to specific node in syntax tree for the need of unit test. For example to create Diagnostic.highlights and attach them to a specific node in a syntax tree within diagnostic formatters unit tests. The absence of such a feature makes it cumbersome to identify and highlight syntax nodes for diagnostic or verification purposes.

Proposed Solution by @ahoppen:

Introduce a NodeLocator struct to facilitate the process. Below is a proposed implementation idea:

/// Refers to the first node in a syntax tree after `marker` that satisfies `match`.
///
/// For example, if you have
/// ```swift
/// var x: Int
/// 1️⃣func foo() {}
/// ```
///
/// Then `NodeLocator(marker: "1️⃣")` refers to the `CodeBlockItemSyntax` that
/// contains the function declaration and
/// `NodeLocator(marker: "1️⃣", matches: { $0.is(FunctionDeclSyntax.self) })`
/// refers to the function within.
struct NodeLocator {
  let marker: String
  let match: (Syntax) -> Bool = { _ in true }

  func findNode(in tree: Syntax) -> Syntax? {
    // TODO: Implement
  }
}

Note

Before starting implementation, let's discuss this approach here.

This feature could be a significant step towards enabling more precise and easier-to-write unit tests, especially those involving diagnostic highlights.

@Matejkob
Copy link
Contributor Author

For the current needs, I've implemented unit tests to examine how the range of source code to highlight is calculated. The relevant tests can be found here:

func testComputeHighlightRangesFirstHighlightExcludesLeadingTrivia() {
let decl: DeclSyntax =
"@PropertyWrapper private(set) var variable: Decimal = 123 * 456"
// 123456789012345678901234567890
let variableDecl = decl.cast(VariableDeclSyntax.self)
assertSyntaxHighlightRanges(
inSyntaxTree: variableDecl,
withHighlights: [
(node: variableDecl.attributes, expectedNodeDescription: "@PropertyWrapper "),
(node: variableDecl.modifiers, expectedNodeDescription: "private(set) "),
],
matchExpectedRanges: [1..<18, 18..<30]
)
}

func testPartialHighlightForMultiLineVariableDeclarationStart() {
let decl: DeclSyntax =
"""
class MyClass {
var abc =
132
}
"""
let variableDecl = decl.cast(ClassDeclSyntax.self).memberBlock.members.first!.decl.cast(VariableDeclSyntax.self)
assertSyntaxHighlightRanges(
inSyntaxTree: decl,
withHighlights: [
(node: variableDecl, expectedNodeDescription: "\n var abc =\n 132")
],
atLineNumber: 2,
usingLineSource: " var abc =\n",
matchExpectedRanges: [3..<12]
)
}

And this approach is far from optimal.

@ahoppen
Copy link
Collaborator

ahoppen commented Sep 29, 2023

Tracked in Apple’s issue tracker as rdar://116256883

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants