Simple Static Analysis Tool for Go Code
This challenge asks you to implement a basic static analysis tool for Go code. Static analysis is a crucial part of software development, allowing you to identify potential issues (like unused variables, style violations, or potential bugs) without actually running the code. This exercise will give you a taste of how such tools work and the challenges involved.
Problem Description
You are tasked with creating a Go program that performs a simple static analysis on a given Go source file. The analysis should focus on identifying unused variables. The program should read a Go source file from the command line argument, parse it, and report any variables that are declared but never used within their scope.
What needs to be achieved:
- File Input: The program should accept a Go source file path as a command-line argument.
- Parsing: The program should parse the Go source file. You can use the
go/parserpackage for this. - Symbol Table: Create a simple symbol table (e.g., a map) to track declared variables.
- Usage Tracking: Traverse the Abstract Syntax Tree (AST) generated by the parser and identify variable usages.
- Unused Variable Detection: Compare the declared variables with the used variables. Any declared variable not found in the usage list is considered unused.
- Reporting: Print the names of any unused variables to the console, one variable per line.
Key Requirements:
- The program must handle basic Go syntax correctly.
- The program should identify unused variables within their scope (e.g., a variable declared inside a function should only be checked for usage within that function).
- The program should not report variables that are used as function parameters.
- The program should not report variables that are imported.
Expected Behavior:
When given a Go source file as input, the program should output a list of unused variables, each on a new line. If no unused variables are found, the program should output nothing.
Important Edge Cases to Consider:
- Comments: The parser should ignore comments.
- Strings: Variables used within string literals should be considered used.
- Multiple Declarations: Handle cases where a variable is declared multiple times within the same scope.
- Nested Scopes: Ensure that variable usage is checked within the correct scope.
- Error Handling: Gracefully handle cases where the input file does not exist or is not a valid Go source file.
Examples
Example 1:
Input: test.go (content: package main; var unusedVar int; func main() { var usedVar int = 10; } )
Output: unusedVar
Explanation: The variable `unusedVar` is declared but never used within its scope. `usedVar` is used within the `main` function.
Example 2:
Input: test.go (content: package main; func main() { var a int = 1; var b string = "hello"; } )
Output:
Explanation: Both `a` and `b` are declared and used within the `main` function.
Example 3:
Input: test.go (content: package main; var globalUnused int; func main() { var localUnused int; } )
Output: globalUnused
localUnused
Explanation: `globalUnused` is declared globally but never used. `localUnused` is declared locally within `main` but never used.
Constraints
- Input File Size: The input Go source file should not exceed 100KB.
- Variable Name Length: Variable names will consist of alphanumeric characters and underscores, and will be no longer than 32 characters.
- Performance: The analysis should complete within 1 second for files up to the specified size.
- Error Handling: The program should exit with a non-zero exit code if an error occurs during file reading or parsing.
Notes
- The
go/parserpackage provides the necessary tools for parsing Go code. Explore its documentation to understand how to traverse the AST. - Consider using a recursive function to traverse the AST and identify variable usages.
- Focus on identifying declared but unused variables. Don't worry about complex analysis like detecting potential null pointer dereferences or race conditions.
- This is a simplified static analysis tool. Real-world static analysis tools are significantly more complex and sophisticated.
- Start with a small, simple Go file and gradually increase the complexity of the input to test your program thoroughly.