feat(lint): eleven new rules over directives, data and addressing
Assisted-by: GLM 5.3
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package lint
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"sourcedock.dev/petrbalvin/gasm-sdk/arch"
|
||||
"sourcedock.dev/petrbalvin/gasm-sdk/parser"
|
||||
)
|
||||
|
||||
// TestUnnamedFPReference pins the toolchain contract "cannot reference FP
|
||||
// without a symbol": a numeric offset against FP parses as a memory operand
|
||||
// with FP in the base, and both assemblers reject it at assembly time.
|
||||
func TestUnnamedFPReference(t *testing.T) {
|
||||
diags := lintSrc(t, `
|
||||
#include "textflag.h"
|
||||
TEXT ·f(SB), NOSPLIT, $0-8
|
||||
MOVQ 0(FP), AX
|
||||
RET
|
||||
`)
|
||||
if codes(diags)[CodeUnnamedFPRef] != 1 {
|
||||
t.Fatalf("want one unnamed-fp-reference, got %+v", diags)
|
||||
}
|
||||
for _, d := range diags {
|
||||
if d.Code == CodeUnnamedFPRef && d.Severity != Error {
|
||||
t.Fatalf("unnamed FP reference must be error severity: %+v", d)
|
||||
}
|
||||
}
|
||||
|
||||
// The named spelling is the language's requirement.
|
||||
diags = lintSrc(t, `
|
||||
#include "textflag.h"
|
||||
TEXT ·f(SB), NOSPLIT, $0-8
|
||||
MOVQ x+0(FP), AX
|
||||
RET
|
||||
`)
|
||||
if codes(diags)[CodeUnnamedFPRef] != 0 {
|
||||
t.Fatalf("named FP reference must not be flagged: %+v", diags)
|
||||
}
|
||||
|
||||
// -disable silences the rule.
|
||||
f, _ := parser.Parse("t_amd64.s", "#include \"textflag.h\"\nTEXT ·f(SB), NOSPLIT, $0-8\n\tMOVQ 0(FP), AX\n\tRET\n")
|
||||
diags = File(f, Config{Arch: arch.AMD64, Disable: map[string]bool{CodeUnnamedFPRef: true}})
|
||||
if codes(diags)[CodeUnnamedFPRef] != 0 {
|
||||
t.Fatalf("disabled rule must stay silent: %+v", diags)
|
||||
}
|
||||
}
|
||||
|
||||
// TestHardwareSPAddressing pins the one-character edge between the virtual
|
||||
// frame and the real stack pointer: a negative unnamed offset against (SP)
|
||||
// is the local-frame spelling with the name missing, while the runtime's
|
||||
// deliberate hardware-SP references all carry positive offsets.
|
||||
func TestHardwareSPAddressing(t *testing.T) {
|
||||
diags := lintSrc(t, `
|
||||
#include "textflag.h"
|
||||
TEXT ·f(SB), NOSPLIT, $16
|
||||
MOVQ $1, -8(SP)
|
||||
RET
|
||||
`)
|
||||
if codes(diags)[CodeHardwareSP] != 1 {
|
||||
t.Fatalf("want one hardware-sp-addressing, got %+v", diags)
|
||||
}
|
||||
|
||||
// The named local in the virtual frame, the intended spelling.
|
||||
diags = lintSrc(t, `
|
||||
#include "textflag.h"
|
||||
TEXT ·f(SB), NOSPLIT, $16
|
||||
MOVQ $1, x-8(SP)
|
||||
RET
|
||||
`)
|
||||
if codes(diags)[CodeHardwareSP] != 0 {
|
||||
t.Fatalf("named SP local must not be flagged: %+v", diags)
|
||||
}
|
||||
|
||||
// Positive hardware-SP offsets are the runtime's caller-frame idiom.
|
||||
diags = lintSrc(t, `
|
||||
#include "textflag.h"
|
||||
TEXT ·f(SB), NOSPLIT, $0
|
||||
MOVQ 8(SP), AX
|
||||
RET
|
||||
`)
|
||||
if codes(diags)[CodeHardwareSP] != 0 {
|
||||
t.Fatalf("positive hardware-SP offset must not be flagged: %+v", diags)
|
||||
}
|
||||
|
||||
// The bare register is not a memory reference.
|
||||
diags = lintSrc(t, `
|
||||
#include "textflag.h"
|
||||
TEXT ·f(SB), NOSPLIT, $0
|
||||
MOVQ SP, AX
|
||||
RET
|
||||
`)
|
||||
if codes(diags)[CodeHardwareSP] != 0 {
|
||||
t.Fatalf("bare SP register must not be flagged: %+v", diags)
|
||||
}
|
||||
|
||||
// The rule is architecture-neutral: riscv64 numeric (SP) parses the
|
||||
// same way (the parser documents 0(SP) as a RISC-V shape).
|
||||
diags = lintSrcArch(t, "f_riscv64.s", `
|
||||
#include "textflag.h"
|
||||
TEXT ·f(SB), NOSPLIT, $16
|
||||
MOV $1, -8(SP)
|
||||
RET
|
||||
`)
|
||||
if codes(diags)[CodeHardwareSP] != 1 {
|
||||
t.Fatalf("want one hardware-sp-addressing on riscv64, got %+v", diags)
|
||||
}
|
||||
|
||||
// -disable silences the rule.
|
||||
f, _ := parser.Parse("t_amd64.s", "#include \"textflag.h\"\nTEXT ·f(SB), NOSPLIT, $16\n\tMOVQ $1, -8(SP)\n\tRET\n")
|
||||
diags = File(f, Config{Arch: arch.AMD64, Disable: map[string]bool{CodeHardwareSP: true}})
|
||||
if codes(diags)[CodeHardwareSP] != 0 {
|
||||
t.Fatalf("disabled rule must stay silent: %+v", diags)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user