all repos

clerk @ 01a80401838beb68ea961357c6cfd74346b9e864

missing tooling for ledger/hledger
5 files changed, 42 insertions(+), 0 deletions(-)
linter: add duplicated-account
Author: Oleksandr Smirnov olexsmir@gmail.com
Committed at: 2026-06-27 12:41:04 +0300
Authored at: 2026-06-26 15:55:07 +0300
Change ID: oxukxnmkqvtytttzryvvlnrsqstluzun
Parent: 731ba24
M internal/linter/linter_test.go
···
        18
        18
         	"account-depth":            {&AccountDepthLimit{MaxDepth: 3}},

      
        19
        19
         	"multiple-omitted-amounts": {&MultipleOmittedAmounts{}},

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

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

      
        21
        22
         }

      
        22
        23
         

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

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

      
        
        2
        +

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

      
        
        4
        +

      
        
        5
        +// DuplicatedAccount flags account declarations that appear more than once.

      
        
        6
        +type DuplicatedAccount struct{}

      
        
        7
        +

      
        
        8
        +func (DuplicatedAccount) ID() RuleID         { return "duplicated-account" }

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

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

      
        
        11
        +	var finds []Find

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

      
        
        13
        +

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

      
        
        15
        +		ad, ok := entry.(*ast.AccountDirective)

      
        
        16
        +		if !ok {

      
        
        17
        +			continue

      
        
        18
        +		}

      
        
        19
        +

      
        
        20
        +		name := ad.Account.String()

      
        
        21
        +		if seen[name] {

      
        
        22
        +			finds = append(finds, Find{

      
        
        23
        +				Code:     d.ID(),

      
        
        24
        +				Severity: d.Severity(),

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

      
        
        26
        +				Span:     ad.Account.Span,

      
        
        27
        +			})

      
        
        28
        +		}

      
        
        29
        +		seen[name] = true

      
        
        30
        +	}

      
        
        31
        +	return finds

      
        
        32
        +}

      
M internal/linter/rules.go
···
        32
        32
         	&AccountDepthLimit{MaxDepth: 4},

      
        33
        33
         	&MultipleOmittedAmounts{},

      
        34
        34
         	&OrderDate{},

      
        
        35
        +	&DuplicatedAccount{},

      
        35
        36
         }

      
A internal/linter/testdata/duplicated-account.golden
···
        
        1
        +duplicated-account.journal:4:9: duplicated-account: duplicated account declaration: Expenses:Food

      
        
        2
        +duplicated-account.journal:6:9: duplicated-account: duplicated account declaration: Expenses:Transport

      
A internal/linter/testdata/duplicated-account.input
···
        
        1
        +account Expenses:Food

      
        
        2
        +account Expenses:Transport

      
        
        3
        +account Income:Salary

      
        
        4
        +account Expenses:Food

      
        
        5
        +account Assets:Cash

      
        
        6
        +account Expenses:Transport