I think it's just the result of differing needs:
-
ASTPrinter needs to print a source-like view of code rather than a tree structure. (This is extra important now with the work on textual interfaces.) ASTDumper usually uses this when referring to some other AST node that isn't really a child, like the underlying type of a typealias.
-
PrintExpr and PrintStmt are subclasses of ASTVisitor; keeping them separate helps keep the code clean by separating concerns. (At least, that's the theory, anyway.)
-
If none of this functionality is needed, there's no reason not to print directly to the output stream.
Anything that makes the code cleaner is probably a welcome patch, but I wouldn't say the current division of labor is something we consider a problem.