Assemblers (and by extension assembler/linker combos) never get rid of uncalled functions, since they cannot know if the function is truly unused.
You might have some complicated calling system that constructs the address of a function and jumps to it via a table in RAM or an RTS instruction. Or have some other external code call those function.
In either case the assembler cannot know these things, and I think it's better that it doesn't assume things about the code.
One way to get rid of functions would be to use conditional blocks.
Code:
.if __symbol
Assembly here
.endif
This will only assemble the contents of the block if __symbol is not 0 (or undefined). Its still manual as you have to tell it which block to include or not, so it's only slightly better than mass commenting parts of your code.
Another much better option is (like you mentioned) to simply do the same thing cc65 libraries do and place each function in it's own file, assemble them to object files, and the combine them all into a library file with AR65.
Now when you assemble and link a program you can include that library file and the linker will only use the code from files that were imported by the program.