all repos

clerk @ fd46af1

missing tooling for ledger/hledger
5 files changed, 44 insertions(+), 0 deletions(-)
linter: add duplicated-commodity rule
Author: Oleksandr Smirnov olexsmir@gmail.com
Committed at: 2026-06-27 12:41:04 +0300
Authored at: 2026-06-26 15:56:17 +0300
Change ID: smkrkxztlskzqqlkvrnstsuknlkuuskz
Parent: 01a8040
M internal/linter/linter_test.go
···
        19
        19
         	"multiple-omitted-amounts": {&MultipleOmittedAmounts{}},

      
        20
        20
         	"orderdate":                {&OrderDate{}},

      
        21
        21
         	"duplicated-account":       {&DuplicatedAccount{}},

      
        
        22
        +	"duplicated-commodity":     {&DuplicatedCommodity{}},

      
        22
        23
         }

      
        23
        24
         

      
        24
        25
         func TestLinter(t *testing.T) {

      
A internal/linter/rule_duplicated_commodity.go
···
        
        1
        +package linter

      
        
        2
        +

      
        
        3
        +import "olexsmir.xyz/clerk/journal/ast"

      
        
        4
        +

      
        
        5
        +// DuplicatedCommodity flags commodity declarations that appear more than once.

      
        
        6
        +type DuplicatedCommodity struct{}

      
        
        7
        +

      
        
        8
        +func (DuplicatedCommodity) ID() RuleID         { return "duplicated-commodity" }

      
        
        9
        +func (DuplicatedCommodity) Severity() Severity { return SeverityWarning }

      
        
        10
        +func (d *DuplicatedCommodity) CheckJournal(j *ast.Journal) []Find {

      
        
        11
        +	var finds []Find

      
        
        12
        +	seen := make(map[string]bool)

      
        
        13
        +

      
        
        14
        +	for _, entry := range j.Entries {

      
        
        15
        +		cd, ok := entry.(*ast.CommodityDirective)

      
        
        16
        +		if !ok {

      
        
        17
        +			continue

      
        
        18
        +		}

      
        
        19
        +

      
        
        20
        +		name := cd.Commodity

      
        
        21
        +		if seen[name] {

      
        
        22
        +			finds = append(finds, Find{

      
        
        23
        +				Code:     d.ID(),

      
        
        24
        +				Severity: d.Severity(),

      
        
        25
        +				Message:  "duplicated commodity declaration: " + name,

      
        
        26
        +				Span:     cd.Span,

      
        
        27
        +			})

      
        
        28
        +		}

      
        
        29
        +

      
        
        30
        +		seen[name] = true

      
        
        31
        +	}

      
        
        32
        +

      
        
        33
        +	return finds

      
        
        34
        +}

      
M internal/linter/rules.go
···
        33
        33
         	&MultipleOmittedAmounts{},

      
        34
        34
         	&OrderDate{},

      
        35
        35
         	&DuplicatedAccount{},

      
        
        36
        +	&DuplicatedCommodity{},

      
        36
        37
         }

      
A internal/linter/testdata/duplicated-commodity.golden
···
        
        1
        +duplicated-commodity.journal:4:1: duplicated-commodity: duplicated commodity declaration: USD

      
        
        2
        +duplicated-commodity.journal:6:1: duplicated-commodity: duplicated commodity declaration: EUR

      
A internal/linter/testdata/duplicated-commodity.input
···
        
        1
        +commodity USD

      
        
        2
        +commodity EUR

      
        
        3
        +commodity GBP

      
        
        4
        +commodity USD

      
        
        5
        +commodity JPY

      
        
        6
        +commodity EUR