The point here is that what constitutes "parses exactly as it looks" means diametrically different things to you and to other people (including anyone coming from a great number of languages more commonly used than Swift): it's precisely this case that needs diagnostics.
Where the notation is ambiguous, people wouldn't be so certain of how they expect it to parse. Again, the purpose of this is to eliminate the "parses exactly as it looks" scenario. It's not about preventing people from writing ugly code, but from writing pretty code that means different things to different people.
Again, to make it plain, all of this design work is to address the following premise: There will be people who will see -x ** y and confidently know it behaves as it does in Python and other languages, parsing the expression as -(x ** y); this represents a problem that must be addressed. If you accept this premise, then there are only three possibilities:
(1) do not introduce ** into the language at all;
(2) introduce ** but forbid the scenario in question from arising with diagnostics; or
(3) introduce ** but eliminate the mismatched expectations by aligning behavior with that of Python and other languages.
Option 1 is the status quo; option 2 is the design I've worked on in the PR toolchain; option 3 is what I've just described as impossible given the direction of Swift. That is an exhaustive list of options.
Alternatively, you can reject the premise, which is fine
Well, yes, Swift has rules. They are definitely not simple, clear, and consistent rules, but they're rules. The design proposed for adding the diagnostics I describe is not ad hoc (nowhere in the compiler does it look for a hardcoded **), but based on the addition of another rule that can be reused for custom operators, so fortunately it doesn't "muddy things up."
We can debate whether it's the best rules-based solution to make the diagnostic a reality, and we can debate whether the problem needs to be addressed. It sounds like you're of the opinion that we don't need to deal with the problem in the first place--again, fine, but there are others here who want it dealt with, so there needs to be an exploration of how that might be done.
// E.g. 'fn() as Int << 2'.
// In this case '<<' has higher precedence than 'as', but the LHS should
// be 'fn() as Int' instead of 'Int'.
// If the left-hand-side is a 'try' or 'await', hoist it up turning
// "(try x) + y" into try (x + y).
// If this is an assignment operator, and the left operand is an optional
// evaluation, pull the operator into the chain.
// If the right operand is a try or await, it's an error unless the operator
// is an assignment or conditional operator and there's nothing to
// the right that didn't parse as part of the right operand.
//
// Generally, nothing to the right will fail to parse as part of the
// right operand because there are no standard operators that have
// lower precedence than assignment operators or the conditional
// operator.
//
// We allow the right operand of the conditional operator to begin
// with 'try' for consistency with the middle operand. This allows:
// x ? try foo() : try bar()
// but not:
// x ? try foo() : try bar() $#! 1
// assuming $#! is some crazy operator with lower precedence
// than the conditional operator.
// If the operator is a cast operator, the RHS can't extend past the type
// that's part of the cast production.
I like this idea! I also agree with you that it's orthogonal to this topic, as it does not solve the issue regarding -2 ** 4 meaning different things to different people, which does not involve an operator of higher precedence. (In fact, what you mention could probably reasonably be implemented as a QoI measure without going through Evolution.)