Ruleguard by example: Underlying types

package gorules

import "github.com/quasilyte/go-ruleguard/dsl"

// The binary.Write function from the "encoding/binary" package
// does a simple Write call if the data argument is []byte.
//
// In that case we may want to suggest calling the Write method
// directly, without extra binary.Write overhead.
//
// But what if the data is not exactly []byte, but some named
// type that is defined like this: `type myBytes []byte`.
// It's still bytes, right?

func binaryWrite(m dsl.Matcher) {
	// When we consider myBytes type we need to remember that
	// it's *underlying* type is []byte. The underlying type
	// of []byte is []byte.
	//
	// This leads us to the Underlying() method of the ExprType.

	m.Match(`binary.Write($w, $_, $v)`).
		Where(m["v"].Type.Underlying().Is(`[]byte`)).
		Report(`consider doing $w.Write($v) instead`)
}
package main

import (
	"bytes"
	"encoding/binary"
)

// myByteSlice underlying type is []byte
type myByteSlice []byte

// myByteSlice2 underlying type is still []byte.
type myByteSlice2 myByteSlice

func main() {
	var b []byte
	var customBytes myByteSlice
	buf := &bytes.Buffer{}

	binary.Write(buf, binary.LittleEndian, b)

	// These 2 lines will not give any warnings if we don't use Underlying().
	binary.Write(buf, binary.LittleEndian, customBytes)
	binary.Write(buf, binary.LittleEndian, myByteSlice2("123"))

	binary.Write(buf, binary.LittleEndian, 14) // OK: not []byte
}

Notes: