Files
gasm-sdk/lint/data_test.go
T

205 lines
5.6 KiB
Go
Raw Normal View History

// 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"
)
func TestDataWidth(t *testing.T) {
// Both assemblers reject the integer widths beyond 1, 2, 4 and 8 (go
// tool asm: "bad int size for DATA argument: 3").
diags := lintSrc(t, `
#include "textflag.h"
DATA ·tab+0(SB)/3, $0x010203
GLOBL ·tab(SB), RODATA, $8
`)
if codes(diags)[CodeDataWidth] != 1 {
t.Fatalf("want one data-width, got %+v", diags)
}
for _, d := range diags {
if d.Code == CodeDataWidth && d.Severity != Error {
t.Fatalf("bad width must be error severity: %+v", d)
}
}
// The missing suffix is not a default ("expect /size").
diags = lintSrc(t, `
#include "textflag.h"
DATA ·tab+0(SB), $7
GLOBL ·tab(SB), RODATA, $8
`)
if codes(diags)[CodeDataWidth] != 1 {
t.Fatalf("missing width must be flagged: %+v", diags)
}
// A float needs its full 4 or 8 IEEE-754 bytes ("bad float size").
diags = lintSrc(t, `
#include "textflag.h"
DATA ·f+0(SB)/2, $1.5
GLOBL ·f(SB), RODATA, $8
`)
if codes(diags)[CodeDataWidth] != 1 {
t.Fatalf("float in a narrow width must be flagged: %+v", diags)
}
// A string accepts any width: GOROOT's own messages use /84 and the
// bytes are zero-padded.
diags = lintSrc(t, `
#include "textflag.h"
DATA ·s+0(SB)/3, $"hi"
GLOBL ·s(SB), RODATA, $3
`)
if codes(diags)[CodeDataWidth] != 0 {
t.Fatalf("string data in width 3 must not be flagged: %+v", diags)
}
// The valid integer widths and float widths are clean.
diags = lintSrc(t, `
#include "textflag.h"
DATA ·tab+0(SB)/1, $1
DATA ·tab+1(SB)/2, $2
DATA ·tab+4(SB)/4, $4
DATA ·f+0(SB)/4, $1.5
GLOBL ·tab(SB), RODATA, $8
GLOBL ·f(SB), RODATA, $4
`)
if codes(diags)[CodeDataWidth] != 0 {
t.Fatalf("valid widths must not be flagged: %+v", diags)
}
}
func TestDataValueOverflow(t *testing.T) {
// $0x1ff into one byte: both assemblers keep 0xff silently.
diags := lintSrc(t, `
#include "textflag.h"
DATA ·tab+0(SB)/1, $0x1ff
GLOBL ·tab(SB), RODATA, $8
`)
if codes(diags)[CodeDataValueOverflow] != 1 {
t.Fatalf("want one data-value-overflow, got %+v", diags)
}
// The full signed and unsigned range of the width round-trips.
diags = lintSrc(t, `
#include "textflag.h"
DATA ·tab+0(SB)/1, $255
DATA ·tab+1(SB)/1, $-128
DATA ·tab+2(SB)/2, $65535
GLOBL ·tab(SB), RODATA, $8
`)
if codes(diags)[CodeDataValueOverflow] != 0 {
t.Fatalf("in-range values must not be flagged: %+v", diags)
}
// Width 8 holds every int64 (the regression where the range itself
// overflowed and flagged 0).
diags = lintSrc(t, `
#include "textflag.h"
DATA ·tab+0(SB)/8, $0
DATA ·tab+8(SB)/8, $16
GLOBL ·tab(SB), RODATA, $16
`)
if codes(diags)[CodeDataValueOverflow] != 0 {
t.Fatalf("width 8 must hold every int64: %+v", diags)
}
// -disable silences the rule.
f, _ := parser.Parse("t_amd64.s", "#include \"textflag.h\"\nDATA ·tab+0(SB)/1, $0x1ff\nGLOBL ·tab(SB), RODATA, $8\n")
diags = File(f, Config{Arch: arch.AMD64, Disable: map[string]bool{CodeDataValueOverflow: true}})
if codes(diags)[CodeDataValueOverflow] != 0 {
t.Fatalf("disabled rule must stay silent: %+v", diags)
}
}
func TestDataStringWidth(t *testing.T) {
// Both assemblers reject a string longer than the field.
diags := lintSrc(t, `
#include "textflag.h"
DATA ·msg+0(SB)/2, $"hello"
GLOBL ·msg(SB), RODATA, $5
`)
if codes(diags)[CodeDataStringWidth] != 1 {
t.Fatalf("want one data-string-width, got %+v", diags)
}
// A shorter string is zero-padded to the width, the documented layout.
diags = lintSrc(t, `
#include "textflag.h"
DATA ·msg+0(SB)/8, $"hi"
GLOBL ·msg(SB), RODATA, $8
`)
if codes(diags)[CodeDataStringWidth] != 0 {
t.Fatalf("padded string must not be flagged: %+v", diags)
}
}
func TestDataWithoutGlobl(t *testing.T) {
// gasm asm rejects an unpaired initialiser ("no matching GLOBL"); go
// tool asm sizes the symbol implicitly.
diags := lintSrc(t, `
#include "textflag.h"
DATA ·tab+0(SB)/8, $0x0102030405060708
`)
if codes(diags)[CodeDataNoGlobl] != 1 {
t.Fatalf("want one data-without-globl, got %+v", diags)
}
// The pairing is order-independent: both assemblers resolve by name.
diags = lintSrc(t, `
#include "textflag.h"
GLOBL ·tab(SB), RODATA, $8
DATA ·tab+0(SB)/8, $0x0102030405060708
`)
if codes(diags)[CodeDataNoGlobl] != 0 {
t.Fatalf("GLOBL before DATA must not be flagged: %+v", diags)
}
diags = lintSrc(t, `
#include "textflag.h"
DATA ·tab+0(SB)/8, $0x0102030405060708
GLOBL ·tab(SB), RODATA, $8
`)
if codes(diags)[CodeDataNoGlobl] != 0 {
t.Fatalf("DATA before GLOBL must not be flagged: %+v", diags)
}
// A static symbol pairs with its own static GLOBL.
diags = lintSrc(t, `
#include "textflag.h"
DATA mask<>+0(SB)/4, $0x80020100
GLOBL mask<>(SB), RODATA, $4
`)
if codes(diags)[CodeDataNoGlobl] != 0 {
t.Fatalf("static pairing must not be flagged: %+v", diags)
}
}
func TestDataExceedsGlobl(t *testing.T) {
// gasm asm bounds every initialiser by the declared size; go tool asm
// grows the symbol silently, so the declared size stops matching the
// data.
diags := lintSrc(t, `
#include "textflag.h"
GLOBL ·tab(SB), RODATA, $4
DATA ·tab+0(SB)/8, $0x0102030405060708
`)
if codes(diags)[CodeDataExceedsGlobl] != 1 {
t.Fatalf("want one data-exceeds-globl, got %+v", diags)
}
// An initialiser that fits the declaration is clean, at either order.
diags = lintSrc(t, `
#include "textflag.h"
DATA ·tab+0(SB)/4, $1
DATA ·tab+4(SB)/4, $2
GLOBL ·tab(SB), RODATA, $8
`)
if codes(diags)[CodeDataExceedsGlobl] != 0 {
t.Fatalf("fitting initialisers must not be flagged: %+v", diags)
}
}