Hey Everyone!
Intro
Happy to see Swift accepted after another successful year with some exciting potential projects for this years Google Summer of Code! It was indeed quite difficult to pick due to the brilliant ideas set by the mentors that further improve, add new features, reuse code and lot more!
Description
As described by @Douglas_Gregor to summarise, the idea involves implementing lexical scopes as part of swift-syntax that would introduce a new library to perform lookups for names and scopes for particular syntax node just like in a typical implementation for any programming language.
Defining and Representing Scopes
We would define a class to represent lexical scopes and this would include information about the variable names and their respective visibilities depending on what context where we are talking here. At the basic level, we will have a record which can be a dictionary to store information like the name and it's parent, so while performing a lookup we can access it in a bottom-up fashion.
// A class to represent a lexical scope.
class LexicalScope {
var variables: [VariableDeclaration] = []
var parentScope: LexicalScope?
// Scope-related information...
}
// A structure to represent a variable declaration.
struct VariableDeclaration {
let name: String
let varType: <SomeType>
// Variable info...
}
Scope Tracking and Variable Extraction
The processNode
helper function during traversal would be used to break down and extract the type of node we are interested in, which for example can be a simple variable declaration. Again, the special cases like if let
, guard
, etc. or temporary scopes should have special handling to immediately destroy or remove it from the context.
// Function to traverse the syntax tree and track lexical scopes
func traverseSyntaxTree(node: SyntaxNode, currentScope: LexicalScope) {
// Process the current node
processNode(node, currentScope)
// Recursively traverse child nodes
for child in node.children {
let childScope = LexicalScope()
childScope.parentScope = currentScope
traverseSyntaxTree(node: child, currentScope: childScope)
}
}
// Function to process a syntax node and update the scope
func processNode(_ node: SyntaxNode, _ currentScope: LexicalScope) {
switch node.kind {
case .variableDeclaration:
if let variableName = extractVariableName(from: node) {
let variableDeclaration = VariableDeclaration(name: variableName)
currentScope.variables.append(variableDeclaration)
}
// Handle other kinds of nodes and scope-related logic
}
}
// Function to extract variable name from a variable declaration node
func extractVariableName(from node: SyntaxNode) -> String? {
// Extract variable name logic here based on the syntax tree
// For example, if node is a variable declaration, extract the name from its tokens
// This can be more complex depending on Swift syntax
return nil
}
Name Lookup API
Although, this is relatively simple, it can be further extended on the basic idea and additional features can be added depending on the requirements.
// Function to perform name lookup in lexical scopes
func findVariableInScopes(_ variableName: String, _ currentScope: LexicalScope) -> VariableDeclaration? {
var scope: LexicalScope? = currentScope
// Traverse up the scope hierarchy
while let current = scope {
if let variable = current.variables.first(where: { $0.name == variableName }) {
return variable
}
scope = current.parentScope
}
return nil
}
Integration with SourceKit-LSP
Once the APIs are all set we can consider integrating our library with SourceKit-LSP to enhance IDE support. This could involve building features like code completion, navigation, and refactoring based on lexical scope information. My personal favourite is the jump-to-definition (and callers) which really helps developers to navigate with ease.
Testing
The testing part will require a lot of work to handle comprehensive unit tests and validate the correctness of the scope tracking and name lookup implementation. Various Swift constructs can be used to cover a wide range of scenarios.
Potential Challenges
-
In my understanding the lookup part is the one that needs to be optimised the most and this could be discussed.
-
Tests may not be able to cover all scenarios initially and hence will need frequent updates in the beginning.
-
Integration with SourceKitLSP and Swift Syntax could get complex since the logic and implementation needs to conform and adapt with the libraries to have consistency in the codebase while still having good performance.
-
Currently the C++ implementation needs to be replaced which would require some work, as it is an important step in replacing scope-related logic within the Swift Compiler.
Conclusion
I am looking forward for feedback from @Douglas_Gregor and the Swift Community for further improving on this and would love to know what changes and additions can be made!
Thanks
-- Rajveer (GitHub - Rajveer100)