It's just a problem of imprecise terminology and specific implementations.
In the "classical" model, the compiler converts high level code to assembly, the assembler assembles it to machine code that gets stored into object files, which then are linked to generate an executable file.
Normally all these steps are mostly hidden (especially the assembly part) because typically you invoke the compiler through a "compiler driver", which automatically invokes all the parts of this toolchain, although generally there are options to stop the flow at some level to inspect what's going on (stopping at the assembly level to inspect the work of the compiler is interesting enough that there are even several sites dedicated just to that).
Still, this is both quite a high level view, and depending on the language and implementation some steps may be missing or handled differently - for example, you can have the compiler generate straight machine code, or the linker generate assembly/machine code instead of just linking (this happens all the time when you enable link time code generation). So, the schema above is just that - a useful schema to understand the basic flow, it's by no means exhaustive of the possibilities that can be done. As long as high level language enters, some kind of executable code exits, anything goes.
binutils
is even a separate package from gcc itself. So some compilers certainly do compile this way. Clang doesn't, and other less-portable compilers also often go straight to machine code. – Peter Cordes