A missing brace or parenthesis still often leads to pretty confusing error messages, I find. I don't know much about parsers, though, maybe that's just a really hard thing to deal with.
There are multiple problems that missing delimiters introduce for the parser when trying to recover. It depends heavily on the language's valid grammar how much you can infer about malformed code to identify likely places where the missing delimiter should go.
One thing you can do (that Rust does) is keep a stack of opened delimiters and their positions, and pop them as you find the closing one. If you encounter a mismatch or any other parse error, you look through the open delimiters to match for indentation level or valid alternatives. The more "flag posts" or redundancy the grammar has, the more likely you are to be able to recover from malformed code. If you are in a language with a sparse grammar, recovery is less feasible.
C++ is very hard to parse. For non-parseable C++, it is even harder to recover, that is, figure out where to restart parsing.
The classic example is forgetting to end a typedef struct foo with a semicolon, at the end of an #include file.
The compiler will happily combine that with whatever follows the #include# statement in the file including it, and report an error with a line number in the file doing the #include.