Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions lint/unused_variable_analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ var UnusedVariableAnalyzer = (func() *analysis.Analyzer {
var identifiers []identifierToCheck

var inInterfaceDepth int
var inFunctionDepth int

inspector.Elements(elementFilter, func(element ast.Element, push bool) bool {
switch decl := element.(type) {
Expand All @@ -77,6 +78,12 @@ var UnusedVariableAnalyzer = (func() *analysis.Analyzer {
}

case *ast.FunctionDeclaration:
if push {
inFunctionDepth++
} else {
inFunctionDepth--
}

// Collect all parameter identifiers,
// but ignore non-default interface functions
if push && (inInterfaceDepth == 0 ||
Expand All @@ -96,8 +103,9 @@ var UnusedVariableAnalyzer = (func() *analysis.Analyzer {
}

case *ast.VariableDeclaration:
if push {
// Collect all variable identifiers (at any nesting level)
// Ignore global/member variables with access(all),
// as they may be used externally.
if push && (inFunctionDepth > 0 || decl.Access != ast.AccessAll) {
identifiers = append(
identifiers,
identifierToCheck{
Expand Down
110 changes: 110 additions & 0 deletions lint/unused_variable_analyzer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -707,6 +707,116 @@ func TestUnusedVariableAnalyzer(t *testing.T) {
)
})

t.Run("unused access(all) global variable", func(t *testing.T) {

t.Parallel()

diagnostics := testAnalyzers(t,
`
access(all) let unused = 5

access(all) fun test() {
log("hello")
}
`,
lint.UnusedVariableAnalyzer,
)

require.Equal(t,
[]analysis.Diagnostic(nil),
diagnostics,
)
})

t.Run("unused access(self) global variable", func(t *testing.T) {

t.Parallel()

diagnostics := testAnalyzers(t,
`
access(self) let unused = 5

access(all) fun test() {
log("hello")
}
`,
lint.UnusedVariableAnalyzer,
)

require.Equal(t,
[]analysis.Diagnostic{
{
Range: ast.Range{
StartPos: ast.Position{Offset: 32, Line: 2, Column: 31},
EndPos: ast.Position{Offset: 37, Line: 2, Column: 36},
},
Location: testLocation,
Category: lint.UnusedVariableCategory,
Message: "variable 'unused' is declared but never used",
SuggestedFixes: []errors.SuggestedFix[ast.TextEdit]{
{
Message: "Prefix with underscore to mark as intentionally unused",
TextEdits: []ast.TextEdit{
{
Replacement: "_unused",
Range: ast.Range{
StartPos: ast.Position{Offset: 32, Line: 2, Column: 31},
EndPos: ast.Position{Offset: 37, Line: 2, Column: 36},
},
},
},
},
},
},
},
diagnostics,
)
})

t.Run("unused contract member variable", func(t *testing.T) {

t.Parallel()

diagnostics := testAnalyzers(t,
`
access(all) contract Foo {
access(all) let unused: Int
init() {
self.unused = 5
}
}
`,
lint.UnusedVariableAnalyzer,
)

require.Equal(t,
[]analysis.Diagnostic(nil),
diagnostics,
)
})

t.Run("unused struct member variable", func(t *testing.T) {

t.Parallel()

diagnostics := testAnalyzers(t,
`
access(all) struct Foo {
access(all) let unused: Int
init() {
self.unused = 5
}
}
`,
lint.UnusedVariableAnalyzer,
)

require.Equal(t,
[]analysis.Diagnostic(nil),
diagnostics,
)
})

t.Run("variable in loop", func(t *testing.T) {

t.Parallel()
Expand Down
Loading