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

feat: Disable typecheck #89

Merged
merged 3 commits into from
Oct 10, 2024
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
10 changes: 10 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
linters-settings:
typecheck:
# Keep the linter enabled (since it can't be disabled), but exclude all its messages
disable: false

exclude-rules:
- linters:
- typecheck
- gosimple
text: '.*'
12 changes: 6 additions & 6 deletions cmd/tlin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,18 @@ const (
)

type Config struct {
Timeout time.Duration
CyclomaticComplexity bool
CyclomaticThreshold int
IgnoreRules string
FuncName string
Output string
Paths []string
Timeout time.Duration
CyclomaticThreshold int
ConfidenceThreshold float64
CyclomaticComplexity bool
CFGAnalysis bool
FuncName string
AutoFix bool
DryRun bool
JsonOutput bool
Output string
ConfidenceThreshold float64
}

func main() {
Expand Down
6 changes: 3 additions & 3 deletions formatter/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,14 @@ func getFormatter(rule string) IssueFormatter {
/***** Issue Formatter Builder *****/

type IssueFormatterBuilder struct {
snippet *internal.SourceCode
padding string
commonIndent string
result strings.Builder
issue tt.Issue
snippet *internal.SourceCode
startLine int
endLine int
maxLineNumWidth int
padding string
commonIndent string
}

func NewIssueFormatterBuilder(issue tt.Issue, snippet *internal.SourceCode) *IssueFormatterBuilder {
Expand Down
2 changes: 1 addition & 1 deletion formatter/formatter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,8 @@ Note: Unnecessary type conversions can make the code less readable and may sligh
func TestFindCommonIndent(t *testing.T) {
tests := []struct {
name string
lines []string
expected string
lines []string
}{
{
name: "whitespace indent",
Expand Down
6 changes: 2 additions & 4 deletions internal/analysis/cfg/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,11 @@ type CFGBuilder interface {
type CFG struct {
// Sentinel nodes for single-entry CFG. Not in original AST.
Entry *ast.BadStmt

// Sentinel nodes for single-exit CFG. Not in original AST.
Exit *ast.BadStmt

Exit *ast.BadStmt
blocks map[ast.Stmt]*block
// All defers found in CFG, disjoint from blocks. May be flowed to after Exit.
Defers []*ast.DeferStmt
blocks map[ast.Stmt]*block
}

type block struct {
Expand Down
2 changes: 1 addition & 1 deletion internal/branch/branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import (

// Branch stores the branch's information within an if-else statement.
type Branch struct {
BranchKind
Call
BranchKind
HasDecls bool
}

Expand Down
41 changes: 8 additions & 33 deletions internal/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,17 @@ import (
)

// Engine manages the linting process.
// TODO: use symbol table
type Engine struct {
SymbolTable *SymbolTable
rules []LintRule
ignoredRules map[string]bool
defaultRules []LintRule
nolintMgr *nolint.Manager
rules []LintRule
defaultRules []LintRule
}

// NewEngine creates a new lint engine.
func NewEngine(rootDir string, source []byte) (*Engine, error) {
st, err := BuildSymbolTable(rootDir, source)
if err != nil {
return nil, fmt.Errorf("error building symbol table: %w", err)
}

engine := &Engine{SymbolTable: st}
engine := &Engine{}
engine.initDefaultRules()

return engine, nil
Expand Down Expand Up @@ -103,16 +98,14 @@ func (e *Engine) Run(filename string) ([]tt.Issue, error) {
}
wg.Wait()

filtered := e.filterUndefinedIssues(allIssues)

// map issues back to .gno file if necessary
if strings.HasSuffix(filename, ".gno") {
for i := range filtered {
filtered[i].Filename = filename
for i := range allIssues {
allIssues[i].Filename = filename
}
}

return filtered, nil
return allIssues, nil
}

// Run applies all lint rules to the given source and returns a slice of Issues.
Expand Down Expand Up @@ -149,9 +142,7 @@ func (e *Engine) RunSource(source []byte) ([]tt.Issue, error) {
}
wg.Wait()

filtered := e.filterUndefinedIssues(allIssues)

return filtered, nil
return allIssues, nil
}

func (e *Engine) IgnoreRule(rule string) {
Expand Down Expand Up @@ -191,22 +182,6 @@ func (e *Engine) cleanupTemp(temp string) {
}
}

// filterUndefinedIssue filters out golangci-lint's undefined symbol issues.
// TODO: This is a temporary fix. need to find a better way to handle this.
func (e *Engine) filterUndefinedIssues(issues []tt.Issue) []tt.Issue {
filtered := make([]tt.Issue, 0, len(issues))
for _, issue := range issues {
if issue.Rule == "typecheck" && strings.HasPrefix(issue.Message, "undefined:") {
symbol := strings.TrimSpace(issue.Message[10:])
if e.SymbolTable.IsDefined(symbol) {
continue
}
}
filtered = append(filtered, issue)
}
return filtered
}

// filterNolintIssues filters issues based on nolint comments.
func (e *Engine) filterNolintIssues(issues []tt.Issue) []tt.Issue {
if e.nolintMgr == nil {
Expand Down
24 changes: 2 additions & 22 deletions internal/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"strings"
"testing"

"github.com/gnolang/tlin/internal/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand All @@ -20,7 +19,7 @@ func TestNewEngine(t *testing.T) {
engine, err := NewEngine(tempDir, nil)
assert.NoError(t, err)
assert.NotNil(t, engine)
assert.NotNil(t, engine.SymbolTable)
// assert.NotNil(t, engine.SymbolTable)
assert.NotEmpty(t, engine.rules)
}

Expand All @@ -37,7 +36,7 @@ func (ts TestStruct) TestMethod() {}
engine, err := NewEngine("", []byte(fileContent))
assert.NoError(t, err)
assert.NotNil(t, engine)
assert.NotNil(t, engine.SymbolTable)
// assert.NotNil(t, engine.SymbolTable)
assert.NotEmpty(t, engine.rules)
}

Expand Down Expand Up @@ -107,25 +106,6 @@ func TestReadSourceCode(t *testing.T) {
assert.Equal(t, "package main", sourceCode.Lines[0])
}

func BenchmarkFilterUndefinedIssues(b *testing.B) {
engine := &Engine{
SymbolTable: &SymbolTable{},
}

issues := []types.Issue{
{Rule: "typecheck", Message: "undefined: someSymbol"},
{Rule: "other", Message: "some other issue"},
{Rule: "typecheck", Message: "undefined: anotherSymbol"},
{Rule: "typecheck", Message: "some other typecheck issue"},
}

b.ResetTimer()

for i := 0; i < b.N; i++ {
engine.filterUndefinedIssues(issues)
}
}

// create dummy source code for benchmark
var testSrc = strings.Repeat("hello world", 5000)

Expand Down
4 changes: 2 additions & 2 deletions internal/fixer/fixer.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ const (

// Fixer handles the fixing of issues in Gno code files.
type Fixer struct {
DryRun bool
MinConfidence float64
buffer bytes.Buffer
MinConfidence float64
DryRun bool
}

// New creates a new Fixer instance.
Expand Down
2 changes: 1 addition & 1 deletion internal/fixer/fixer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ func TestAutoFixer(t *testing.T) {
tests := []struct {
name string
input string
issues []tt.Issue
expected string
issues []tt.Issue
dryRun bool
}{
{
Expand Down
2 changes: 1 addition & 1 deletion internal/lints/default_golangci.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type golangciOutput struct {
}

func RunGolangciLint(filename string) ([]tt.Issue, error) {
cmd := exec.Command("golangci-lint", "run", "--disable=gosimple,typecheck", "--out-format=json", filename)
cmd := exec.Command("golangci-lint", "run", "--config=./.golangci.yml", "--out-format=json", filename)
output, _ := cmd.CombinedOutput()

var golangciResult golangciOutput
Expand Down
4 changes: 2 additions & 2 deletions internal/lints/defers.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import (
)

type DeferChecker struct {
issues []tt.Issue
filename string
fset *token.FileSet
filename string
issues []tt.Issue
}

func NewDeferChecker(filename string, fset *token.FileSet) *DeferChecker {
Expand Down
19 changes: 19 additions & 0 deletions internal/lints/detect_cycle_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
package lints

import (
"go/ast"
"go/parser"
"go/token"
"testing"
)

func TestAnalyzeFuncDeclWithBodylessFunction(t *testing.T) {
c := newCycle()

bodylessFunc := &ast.FuncDecl{
Name: &ast.Ident{Name: "bodylessFunction"},
Body: nil,
}

c.analyzeFuncDecl(bodylessFunc)

if len(c.dependencies["bodylessFunction"]) != 0 {
t.Errorf("there should be no dependency on bodyless function. got: %v", c.dependencies["bodylessFunction"])
}
if _, exists := c.dependencies["bodylessFunction"]; !exists {
t.Error("bodyless function should be added to dependency map")
}
}

func TestDetectCycle(t *testing.T) {
t.Parallel()
src := `
Expand Down
25 changes: 14 additions & 11 deletions internal/lints/detect_cycles.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,22 @@ func (c *cycle) analyzeFuncDecl(fn *ast.FuncDecl) {
name := fn.Name.Name
c.dependencies[name] = []string{}

ast.Inspect(fn.Body, func(n ast.Node) bool {
switch x := n.(type) {
case *ast.CallExpr:
if ident, ok := x.Fun.(*ast.Ident); ok {
if ident.Name == name {
c.dependencies[name] = append(c.dependencies[name], ident.Name)
// ignore bodyless function
if fn.Body != nil {
ast.Inspect(fn.Body, func(n ast.Node) bool {
switch x := n.(type) {
case *ast.CallExpr:
if ident, ok := x.Fun.(*ast.Ident); ok {
if ident.Name == name {
c.dependencies[name] = append(c.dependencies[name], ident.Name)
}
}
case *ast.FuncLit:
c.analyzeFuncLit(x, name)
}
case *ast.FuncLit:
c.analyzeFuncLit(x, name)
}
return true
})
return true
})
}
}

func (c *cycle) analyzeFuncLit(fn *ast.FuncLit, parentName string) {
Expand Down
10 changes: 5 additions & 5 deletions internal/lints/gno_analyzer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@ func TestRunLinter(t *testing.T) {
testDir := filepath.Join(filepath.Dir(current), "..", "..", "testdata", "pkg")

tests := []struct {
filename string
expectedIssues []struct {
rule string
message string
}
expectedDeps map[string]struct {
isGno bool
isUsed bool
isIgnored bool
}
filename string
expectedIssues []struct {
rule string
message string
}
}{
{
filename: filepath.Join(testDir, "pkg0.gno"),
Expand Down
2 changes: 1 addition & 1 deletion internal/lints/lint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ func TestDetectUnnecessarySliceLength(t *testing.T) {
tests := []struct {
name string
code string
expected int
message string
expected int
}{
{
name: "suggests to use slice[:]",
Expand Down
2 changes: 1 addition & 1 deletion internal/nolint/nolint.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ type Manager struct {

// scope represents a range in the code where nolint applies.
type scope struct {
rules map[string]struct{}
start token.Position
end token.Position
rules map[string]struct{} // empty, null => apply to all lint rules
}

// ParseComments parses nolint comments in the given AST file and returns a nolintManager.
Expand Down
12 changes: 6 additions & 6 deletions internal/nolint/nolint_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,15 +79,15 @@ func main() {
manager := ParseComments(node, fset)

tests := []struct {
line int
rule string
line int
expected bool
}{
{5, "anyrule", true}, // Line 5 is covered by nolint without rules
{6, "anyrule", false}, // Line 6 is not covered
{7, "rule1", true}, // Line 7 is covered by nolint:rule1
{9, "rule2", true}, // Line 9 is covered by nolint:rule2
{9, "rule3", false}, // Line 9 is not covered for rule3
{"anyrule", 5, true}, // Line 5 is covered by nolint without rules
{"anyrule", 6, false}, // Line 6 is not covered
{"rule1", 7, true}, // Line 7 is covered by nolint:rule1
{"rule2", 9, true}, // Line 9 is covered by nolint:rule2
{"rule3", 9, false}, // Line 9 is not covered for rule3
}

for _, test := range tests {
Expand Down
Loading
Loading