all repos

clerk @ fbfbb0b

missing tooling for ledger/hledger
3 files changed, 69 insertions(+), 14 deletions(-)
linter: improve missing commodity
Author: Oleksandr Smirnov olexsmir@gmail.com
Committed at: 2026-06-22 18:00:13 +0300
Authored at: 2026-06-20 15:31:36 +0300
Change ID: lstrrlyqzqpuqqstmmnruyvwxulmnwkp
Parent: 470344c
M internal/linter/rule_missing_commodity.go
···
        8
        8
         func (MissingCommodity) ID() RuleID         { return "missing-commodity" }

      
        9
        9
         func (MissingCommodity) Severity() Severity { return SeverityWarning }

      
        10
        10
         func (m *MissingCommodity) CheckEntry(entry ast.Entry) []Find {

      
        11
        
        -	txn, ok := entry.(*ast.Transaction)

      
        12
        
        -	if !ok || (txn.Postings != nil && len(txn.Postings) == 0) {

      
        13
        
        -		return nil

      
        
        11
        +	var finds []Find

      
        
        12
        +	switch e := entry.(type) {

      
        
        13
        +	case *ast.Transaction:

      
        
        14
        +		m.checkPostings(&finds, e.Postings)

      
        
        15
        +	case *ast.PeriodicTransaction:

      
        
        16
        +		m.checkPostings(&finds, e.Postings)

      
        
        17
        +	case *ast.AutomatedTransaction:

      
        
        18
        +		m.checkPostings(&finds, e.Postings)

      
        
        19
        +	case *ast.ConversionDirective:

      
        
        20
        +		m.check(&finds, e.From)

      
        
        21
        +		m.check(&finds, e.To)

      
        
        22
        +	case *ast.CommodityDirective:

      
        
        23
        +		m.check(&finds, e.Format)

      
        
        24
        +	case *ast.DefaultCommodityDirective:

      
        
        25
        +		m.check(&finds, e.Amount)

      
        
        26
        +	case *ast.MarketPriceDirective:

      
        
        27
        +		m.check(&finds, e.Amount)

      
        14
        28
         	}

      
        
        29
        +	return finds

      
        
        30
        +}

      
        15
        31
         

      
        16
        
        -	var finds []Find

      
        17
        
        -	for _, posting := range txn.Postings {

      
        
        32
        +func (m *MissingCommodity) checkPostings(finds *[]Find, postings []*ast.Posting) {

      
        
        33
        +	for _, posting := range postings {

      
        18
        34
         		if posting.Amount == nil {

      
        19
        35
         			continue

      
        20
        36
         		}

      
        21
        
        -		if posting.Amount.Commodity == "" {

      
        22
        
        -			finds = append(finds, Find{

      
        23
        
        -				Code:     m.ID(),

      
        24
        
        -				Severity: m.Severity(),

      
        25
        
        -				Message:  "amount missing commodity",

      
        26
        
        -				Span:     posting.Amount.Span,

      
        27
        
        -			})

      
        
        37
        +		m.check(finds, *posting.Amount)

      
        
        38
        +		if posting.Cost != nil {

      
        
        39
        +			m.check(finds, posting.Cost.Amount)

      
        
        40
        +		}

      
        
        41
        +		if posting.Balance != nil {

      
        
        42
        +			m.check(finds, posting.Balance.Amount)

      
        
        43
        +			if posting.Balance.Cost != nil {

      
        
        44
        +				m.check(finds, posting.Balance.Cost.Amount)

      
        
        45
        +			}

      
        28
        46
         		}

      
        29
        47
         	}

      
        30
        
        -	return finds

      
        
        48
        +}

      
        
        49
        +

      
        
        50
        +func (m *MissingCommodity) check(finds *[]Find, am ast.Amount) {

      
        
        51
        +	if am.Commodity == "" {

      
        
        52
        +		*finds = append(*finds, Find{

      
        
        53
        +			Code:     m.ID(),

      
        
        54
        +			Severity: m.Severity(),

      
        
        55
        +			Message:  "amount missing commodity",

      
        
        56
        +			Span:     am.Span,

      
        
        57
        +		})

      
        
        58
        +	}

      
        31
        59
         }

      
M internal/linter/testdata/missing-commodity.golden
···
        1
        1
         missing-commodity.journal:2:21: missing-commodity: amount missing commodity

      
        2
        2
         missing-commodity.journal:10:21: missing-commodity: amount missing commodity

      
        
        3
        +missing-commodity.journal:11:20: missing-commodity: amount missing commodity

      
        
        4
        +missing-commodity.journal:11:27: missing-commodity: amount missing commodity

      
        
        5
        +missing-commodity.journal:15:18: missing-commodity: amount missing commodity

      
        
        6
        +missing-commodity.journal:19:3: missing-commodity: amount missing commodity

      
        
        7
        +missing-commodity.journal:21:3: missing-commodity: amount missing commodity

      
        
        8
        +missing-commodity.journal:26:20: missing-commodity: amount missing commodity

      
        
        9
        +missing-commodity.journal:30:20: missing-commodity: amount missing commodity

      
M internal/linter/testdata/missing-commodity.input
···
        7
        7
             assets:cash

      
        8
        8
         

      
        9
        9
         2024-06-03 "another missing"

      
        10
        
        -    expenses:food   100

      
        
        10
        +    expenses:food   100 @ 1$

      
        
        11
        +    expenses:rent  1000 @ 1

      
        
        12
        +    expenses:rent2  1000 USD @ 1 UAH

      
        11
        13
             assets:cash

      
        
        14
        +

      
        
        15
        +P 2026-06-20 USD 2

      
        
        16
        +P 2026-06-20 USD 2 UAH

      
        
        17
        +

      
        
        18
        +D 100 USD

      
        
        19
        +D 100

      
        
        20
        +

      
        
        21
        +C 1 = 40 UAH

      
        
        22
        +C 1 USD = 40 UAH

      
        
        23
        +

      
        
        24
        +~ monthly

      
        
        25
        +    expenses:rent  $2000

      
        
        26
        +    expenses:food  2000

      
        
        27
        +    assets:bank

      
        
        28
        +

      
        
        29
        += /restaurant/

      
        
        30
        +    expenses:food  20.00

      
        
        31
        +    assets:checking