Simple Static Analysis for Variable Usage
Static analysis is a crucial technique for improving code quality and catching potential errors before runtime. This challenge asks you to implement a simplified static analysis tool in Rust that checks for unused variables within a given Rust code snippet. This is a common and valuable static analysis task, helping developers avoid unnecessary code and potential confusion.
Problem Description
You are tasked with creating a function find_unused_variables that takes a string representing a simplified Rust code snippet as input and returns a vector of strings, where each string is the name of an unused variable. The code snippet will be syntactically valid (though potentially containing errors that your analysis won't catch - focus on unused variables only). The analysis should identify variables declared but never used within the provided code.
Key Requirements:
- Variable Identification: The function must correctly identify variable declarations. Assume variables are declared using the
letkeyword followed by a variable name (e.g.,let x = 5;). - Usage Detection: The function must determine if a variable is ever used. A variable is considered used if its name appears on the right-hand side of an assignment, in a function call, or anywhere else within the code snippet.
- Unused Variable Reporting: The function must return a vector containing the names of all variables that are declared but not used.
- Simple Syntax: Assume a simplified Rust syntax. No complex expressions, control flow statements (if/else, loops), or function definitions are present. Focus solely on variable declarations and assignments.
- No Error Handling: The input string is guaranteed to be syntactically valid for the purposes of this analysis. You don't need to handle parsing errors.
Expected Behavior:
The function should parse the input string, identify all declared variables, and then check if each variable is used anywhere in the code. If a variable is declared but not used, its name should be added to the output vector.
Edge Cases to Consider:
- Variables declared and immediately reassigned (e.g.,
let x = 5; x = 10;). The variable is considered used. - Multiple declarations of the same variable. Only report it as unused if all instances are unused.
- Variables appearing only on the right-hand side of an assignment (e.g.,
let y = x;). The variable on the left-hand side is considered used. - Empty input string. Should return an empty vector.
Examples
Example 1:
Input: "let x = 5; let y = 10; x = 20;"
Output: ["y"]
Explanation: `x` is declared and used (assigned the value 5, then 20). `y` is declared but never used.
Example 2:
Input: "let a = 1; let b = a; let c = 5;"
Output: ["b", "c"]
Explanation: `a` is declared and used (assigned to `b`). `b` and `c` are declared but never used.
Example 3:
Input: "let z = 10;"
Output: ["z"]
Explanation: `z` is declared but never used.
Example 4:
Input: ""
Output: []
Explanation: Empty input, no variables declared.
Example 5:
Input: "let var1 = 1; let var1 = 2;"
Output: []
Explanation: `var1` is declared and reassigned, so it's considered used.
Constraints
- Input String Length: The input string will be no longer than 500 characters.
- Variable Name Length: Variable names will consist of lowercase letters only and will be no longer than 20 characters.
- Performance: The solution should complete within 1 second for the given input size. While not a primary focus, avoid excessively inefficient algorithms.
- Rust Version: Use Rust 1.65 or later.
Notes
- You can use simple string manipulation techniques (splitting, searching) to parse the code snippet. Regular expressions are acceptable but not required.
- Focus on the core logic of identifying variables and detecting their usage. Don't worry about handling all possible Rust syntax.
- Consider using a
HashSetto efficiently track declared and used variables. - The problem is designed to be solvable with a relatively straightforward approach. Avoid over-engineering the solution.
- The goal is to demonstrate an understanding of static analysis principles, not to create a full-fledged Rust compiler.