Makefile strange variable substitution

In this case you need to handle wildcards explicitly, with the wildcard function (at least in GNU Make):
    cp $< $@

foos = $(patsubst,,$(wildcard *.bar))

test: $(foos)
    echo $(foos)

$(wildcard *.bar) expands to all the files ending in .bar, the patsubst call replaces .bar with .foo, and all the targets are then processed as you’d expect.

There is no *.foo file to begin with. So what make does is look for how to make *.foo literaly and the first rule does this. Make expands $< to the first pre-requisite (*.bar, which happens to be in this case). Make then runs the shell command cp *.foo. Since there is no *.foo, shell expands it to cp *.foo literally. That's how you get a *.foo file.

You can verify this by running make -d test.

You can get the effect you want by generating the list of targets based on list of prerequisites.

TARGETS = $(patsubst,,$(wildcard *.bar))
    @cp $< $@
test: $(TARGETS)
    @echo $(TARGETS)
    echo *.foo