Skip to content

Commit 771a68a

Browse files
authored
Merge branch 'main' into fix/coverage-errors
2 parents fa8bf4f + b188144 commit 771a68a

File tree

11 files changed

+140
-29
lines changed

11 files changed

+140
-29
lines changed

.github/workflows/test.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ jobs:
2424
- name: Build
2525
run: go build -v ./...
2626

27+
- name: Build WASM
28+
run: GOOS=js GOARCH=wasm go build -o /dev/null ./wasm/
29+
2730
test:
2831
name: Test
2932
runs-on: ${{ matrix.os }}

.golangci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ linters:
1111
- errcheck
1212
- govet
1313
- staticcheck
14+
- misspell
15+
- gocritic
1416
settings:
1517
errcheck:
1618
exclude-functions:
@@ -25,6 +27,9 @@ linters:
2527
- "all"
2628
- "-QF*"
2729
- "-ST*"
30+
gocritic:
31+
disabled-checks:
32+
- ifElseChain
2833

2934
issues:
3035
max-issues-per-linter: 0

cmd/gosqlx/cmd/action.go

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,23 @@ import (
1818
"github.com/ajitpratap0/GoSQLX/pkg/sql/parser"
1919
)
2020

21+
// builtinRules returns all built-in lint rules. This is the single source of
22+
// truth — both defaultLinter() and allRules() derive from it.
23+
func builtinRules() []linter.Rule {
24+
return []linter.Rule{
25+
whitespace.NewTrailingWhitespaceRule(), // L001
26+
whitespace.NewMixedIndentationRule(), // L002
27+
whitespace.NewConsecutiveBlankLinesRule(2), // L003
28+
whitespace.NewIndentationDepthRule(10, 4), // L004
29+
whitespace.NewLongLinesRule(120), // L005
30+
style.NewColumnAlignmentRule(), // L006
31+
keywords.NewKeywordCaseRule(keywords.CaseUpper), // L007
32+
style.NewCommaPlacementRule(style.CommaTrailing), // L008
33+
style.NewAliasingConsistencyRule(true), // L009
34+
whitespace.NewRedundantWhitespaceRule(), // L010
35+
}
36+
}
37+
2138
var (
2239
actionFiles string
2340
actionRules string
@@ -293,19 +310,40 @@ type lintViolation struct {
293310
Message string
294311
}
295312

296-
// defaultLinter creates a linter with standard rules.
313+
// defaultLinter creates a linter with all built-in rules.
297314
func defaultLinter() *linter.Linter {
298-
return linter.New(
299-
whitespace.NewTrailingWhitespaceRule(),
300-
whitespace.NewMixedIndentationRule(),
301-
keywords.NewKeywordCaseRule(keywords.CaseUpper),
302-
style.NewColumnAlignmentRule(),
303-
)
315+
return linter.New(builtinRules()...)
316+
}
317+
318+
// allRules returns every built-in rule keyed by its ID.
319+
func allRules() map[string]linter.Rule {
320+
all := builtinRules()
321+
m := make(map[string]linter.Rule, len(all))
322+
for _, r := range all {
323+
m[r.ID()] = r
324+
}
325+
return m
304326
}
305327

306328
// lintFile runs the linter on a file and returns violations.
307-
func lintFile(filePath string, _ []string) []lintViolation {
308-
l := defaultLinter()
329+
// When ruleList is non-empty only the rules whose IDs match are executed.
330+
func lintFile(filePath string, ruleList []string) []lintViolation {
331+
var l *linter.Linter
332+
if len(ruleList) == 0 {
333+
l = defaultLinter()
334+
} else {
335+
available := allRules()
336+
var selected []linter.Rule
337+
for _, id := range ruleList {
338+
if r, ok := available[id]; ok {
339+
selected = append(selected, r)
340+
}
341+
}
342+
if len(selected) == 0 {
343+
return nil
344+
}
345+
l = linter.New(selected...)
346+
}
309347
result := l.LintFile(filePath)
310348

311349
if result.Error != nil {

cmd/gosqlx/cmd/action_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package cmd
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"strings"
7+
"testing"
8+
)
9+
10+
func writeTempSQL(t *testing.T, content string) string {
11+
t.Helper()
12+
p := filepath.Join(t.TempDir(), "test.sql")
13+
if err := os.WriteFile(p, []byte(content), 0o644); err != nil {
14+
t.Fatal(err)
15+
}
16+
return p
17+
}
18+
19+
func TestLintFile_FilterSpecificRule(t *testing.T) {
20+
// Trailing whitespace triggers L001; lowercase keyword triggers L007.
21+
path := writeTempSQL(t, "select 1; \n")
22+
23+
violations := lintFile(path, []string{"L001"})
24+
if len(violations) == 0 {
25+
t.Fatal("expected at least one L001 violation for trailing whitespace")
26+
}
27+
for _, v := range violations {
28+
if !strings.Contains(v.Message, "L001") {
29+
t.Errorf("expected only L001 violations, got: %s", v.Message)
30+
}
31+
}
32+
}
33+
34+
func TestLintFile_EmptyRulesRunsAll(t *testing.T) {
35+
// Trailing whitespace (L001) + lowercase keyword (L007).
36+
path := writeTempSQL(t, "select 1; \n")
37+
38+
violations := lintFile(path, nil)
39+
ruleIDs := make(map[string]bool)
40+
for _, v := range violations {
41+
if i := strings.Index(v.Message, "["); i >= 0 {
42+
if j := strings.Index(v.Message[i:], "]"); j > 0 {
43+
ruleIDs[v.Message[i+1:i+j]] = true
44+
}
45+
}
46+
}
47+
if len(ruleIDs) < 2 {
48+
t.Errorf("expected violations from multiple rules, got: %v", ruleIDs)
49+
}
50+
}
51+
52+
func TestLintFile_InvalidRuleIgnored(t *testing.T) {
53+
path := writeTempSQL(t, "SELECT 1;\n")
54+
55+
violations := lintFile(path, []string{"INVALID_RULE"})
56+
if len(violations) != 0 {
57+
t.Errorf("expected no violations for invalid rule, got %d", len(violations))
58+
}
59+
}

cmd/gosqlx/cmd/parser_cmd.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,9 +257,8 @@ func (p *Parser) displayTree(astObj *ast.AST) error {
257257

258258
fmt.Fprintf(p.Out, "%s %s\n", prefix, stmtType)
259259

260-
// Add basic tree structure for different statement types
261-
switch s := stmt.(type) {
262-
case *ast.SelectStatement:
260+
// Add basic tree structure for SELECT statements
261+
if s, ok := stmt.(*ast.SelectStatement); ok {
263262
if len(s.Columns) > 0 {
264263
fmt.Fprintf(p.Out, "%s├── Columns (%d items)\n", childPrefix, len(s.Columns))
265264
}

examples/distinct_on_example.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,21 +82,29 @@ func main() {
8282

8383
func parseAndDisplay(tokens []token.Token) {
8484
p := parser.NewParser()
85-
defer p.Release()
8685

8786
astObj, err := p.Parse(tokens)
8887
if err != nil {
88+
p.Release()
8989
log.Fatalf("Parse error: %v", err)
9090
}
91+
92+
displayAST(p, astObj)
93+
}
94+
95+
func displayAST(p *parser.Parser, astObj *ast.AST) {
96+
defer p.Release()
9197
defer ast.ReleaseAST(astObj)
9298

9399
if len(astObj.Statements) == 0 {
94-
log.Fatal("No statements parsed")
100+
fmt.Println("No statements parsed")
101+
return
95102
}
96103

97104
stmt, ok := astObj.Statements[0].(*ast.SelectStatement)
98105
if !ok {
99-
log.Fatalf("Expected SelectStatement, got %T", astObj.Statements[0])
106+
fmt.Printf("Expected SelectStatement, got %T\n", astObj.Statements[0])
107+
return
100108
}
101109

102110
fmt.Println("Parsed Successfully!")

pkg/advisor/rules.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ func hasJoinCondition(expr ast.Expression, tableNames []string) bool {
171171
return false
172172
}
173173

174-
switch e := expr.(type) {
174+
switch e := expr.(type) { //nolint:gocritic // singleCaseSwitch: intentional — may grow to handle more expression types
175175
case *ast.BinaryExpression:
176176
if e.Operator == "AND" || e.Operator == "OR" {
177177
return hasJoinCondition(e.Left, tableNames) || hasJoinCondition(e.Right, tableNames)
@@ -376,7 +376,7 @@ func containsOrCondition(expr ast.Expression) bool {
376376
return false
377377
}
378378

379-
switch e := expr.(type) {
379+
switch e := expr.(type) { //nolint:gocritic // singleCaseSwitch: intentional — may grow to handle more expression types
380380
case *ast.BinaryExpression:
381381
if strings.EqualFold(e.Operator, "OR") {
382382
// Check if the OR operates on different columns

pkg/config/lsp.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -392,8 +392,7 @@ func ValidateLSPValue(section, key string, value interface{}) error {
392392
}
393393

394394
case "gosqlx.server":
395-
switch key {
396-
case "logLevel":
395+
if key == "logLevel" {
397396
if v, ok := value.(string); ok {
398397
config.Server.LogLevel = v
399398
} else {

pkg/linter/rules/keywords/keyword_case.go

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -206,15 +206,13 @@ func tokenizeLine(line string) []wordToken {
206206
wordStart = i
207207
}
208208
currentWord.WriteRune(ch)
209-
} else {
210-
if wordStart >= 0 {
211-
words = append(words, wordToken{
212-
text: currentWord.String(),
213-
column: wordStart + 1, // 1-indexed
214-
})
215-
currentWord.Reset()
216-
wordStart = -1
217-
}
209+
} else if wordStart >= 0 {
210+
words = append(words, wordToken{
211+
text: currentWord.String(),
212+
column: wordStart + 1, // 1-indexed
213+
})
214+
currentWord.Reset()
215+
wordStart = -1
218216
}
219217
}
220218

pkg/sql/ast/ast.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1207,6 +1207,7 @@ type UpdateStatement struct {
12071207
}
12081208

12091209
// GetUpdates returns Assignments for backward compatibility.
1210+
//
12101211
// Deprecated: Use Assignments directly instead.
12111212
func (u *UpdateStatement) GetUpdates() []UpdateExpression {
12121213
return u.Assignments

0 commit comments

Comments
 (0)