[clangd-dev] [cfe-dev] RFC: add RecoveryExpr to represent invalid constructs in AST

Alex Lorenz via clangd-dev clangd-dev at lists.llvm.org
Fri May 10 09:34:33 PDT 2019


Hi Sam,

Thanks for working on this! Clang’s tooling will definitely benefit from this work.

> On May 10, 2019, at 1:01 AM, Sam McCall via cfe-dev <cfe-dev at lists.llvm.org> wrote:
> 
> Thanks, Jan. I'm not familiar with all of the relevant parts of Parser/Sema, so these are useful pointers.
> 
> On Thu, May 9, 2019 at 9:32 PM Jan Korous <jkorous at apple.com <mailto:jkorous at apple.com>> wrote:
> Hi Sam,
> 
> This seems really useful indeed.
> 
> I am just wondering about how this would play with speculative actions.
> 
> I am more familiar with speculative stuff in Parser (peculativeEvaluationRAII, TentativeParsingAction, Parser::TryParseLambdaIntroducer
> I'm not sure if we'd want to use this to represent errors at the parser level, I don't know enough yet about the parser to really have an opinion.
>  
> but it seems that sometimes we also use the same approach in Sema:
> - SFINAETrap (Which IIUC isn't used just for SFINAE - for example in RebuildForRangeWithDereference.)
> SFINAETrap is diagnostic-based, so we need to ensure when emitting RecoveryExpr:
>  - we must also emit a diagnostic that causes substitution failure
>  - we must not cause a diagnostic that is emitted even on substitution failure
> Other than that, I expect this to interact OK: when substitution fails, the subtrees with RecoveryExprs will be discarded.
> It may hurt the *performance* of substitution failure though, as we create more nodes rather than just cascading the failure immediately.
> (RebuildForRangeWithDereference is indeed not SFINAE but should work the same way)
> 
> - ContextRAII
> I'm not familiar with this class or how it's used for speculative actions - can you elaborate?
>  
> - TypoExpr
> These are somewhat complementary in that TypoExpr is used when an identifier can't be resolved, and RecoveryExpr is useful to preserve subexpressions that *can* be resolved.
> 
> That said there are cases where these intersect like `invalid_function(valid_parameter)` - it would be nice to generate something sensible here: a RecoveryExpr(CallExpr) where invalid_function is dropped/represented by a TypoExpr/represented by a RecoveryExpr(DeclRefExpr)...
> I'm not sure exactly how this should work mechanically, and also don't think it needs to work immediately.
> 
> 
> Any thoughts on that?
> 
> Cheers,
> 
> Jan
> 
>> On May 9, 2019, at 8:13 AM, Tom Honermann via clangd-dev <clangd-dev at lists.llvm.org <mailto:clangd-dev at lists.llvm.org>> wrote:
>> 
>> This would be helpful.  Within Coverity, we hacked together our own error recovery mechanism (mostly involving demoting errors to warnings and discarding function bodies and variable initializers with errors), but our mechanism sometimes leads to other down stream errors (I investigated a spurious "enumerator 'x' does not exist in instantiation of 'y' just this week).  The ability to explicitly indicate errors in the AST would be helpful.
>> 
>> Ideally, I'd like to have ErrorDecl, ErrorStmt, ErrorExpr, and ErrorType nodes.  But there is no reason they would all have to be introduced at the same time.
>> 
>> Tom.
>> 
>> On 5/9/2019 7:31 AM, Sam McCall via cfe-dev wrote:
>>> In the presence of broken code, the AST clang generates is often very limited.
>>> Often Sema diagnoses errors and doesn't create AST nodes. For example, if the arguments to a function call are wrong, we get no CallExpr, and all subexpressions are discarded.
>>> IDE tools like clangd see a lot of broken code, and rely on the resulting AST, so I'd like to improve this situation.
>>> 
>>> If the user writes the following erroneous code:
>>>   a(b, c).d()  // error: a only takes one parameter
>>> then no AST is emitted for this expression, and:
>>> 1) clangd's "go to definition" doesn't work on a, even though there's no overloading
>>> 2) "go to definition" also doesn't work on b and c or their subexpressions, even though these expressions are valid
>>> 3) code completion doesn't work properly for d(), even though we know what type a() returns. (go to definition doesn't either).
>>> There are lots of similar cases: (foo->bar when bar doesn't exist, etc).
>>> 
>>> My proposal is to add a fairly generic RecoveryExpr that the AST can emit in these situations. It would capture:
>>>  - the approximate StmtClass we were trying to create (e.g. CallExpr)
>>>  - the subexpressions - (e.g. an overloadexpr for the callee, and the arguments)
>>>  - the type, if we can make a reasonable guess (e.g. all overloads have the same type)
>>> 
>>> I also considered other approaches, and think they don't work so well:
>>>  - just generate the e.g. CallExpr etc anyway, and maybe add an "invalid" flag. This means weakening many invariants in many different node types. It's subtle and error-prone, and adds conceptual and implementation complexity to many nodes that now have to be able to represent various error cases. Even within clang it's hard to find all the analyses that need to be updated.  (http://lists.llvm.org/pipermail/cfe-dev/2019-April/062151.html <https://urldefense.proofpoint.com/v2/url?u=http-3A__lists.llvm.org_pipermail_cfe-2Ddev_2019-2DApril_062151.html&d=DwMFaQ&c=DPL6_X_6JkXFx7AXWqB0tg&r=oOJsFtgkFCCcDlfLWKmfrVDtpXLihXJTBGJfSapXCV0&m=k3m3ocpqnJDQ1DBLtX6DNqMbG51U4RJ9VyH8qj2SfSc&s=jddDcS9V5Twt16Gxg9n1WTXzsxqOYx-KQIOIvXEovEc&e=>)
>>>  - extract the extra information we need from Sema, rather than the AST. e.g. Sema should invoke a callback whenever an identifier is resolved to a Decl. This solves go-to-definition pretty well, but isn't a very flexible approach. The AST is rich, and provides context, can be dumped etc, a Sema callback would be an ad-hoc solution that is less convenient to use and understand.
>>>  - add a hierarchy of new nodes to represent errors (RecoveryCallExpr etc). It seems like a lot of work to model all the different ways code can be invalid, and I don't think the extra structure would be useful enough to justify it.
>>> 
>>> I have a very rough prototype of this idea: https://reviews.llvm.org/D61722 <https://urldefense.proofpoint.com/v2/url?u=https-3A__reviews.llvm.org_D61722&d=DwMFaQ&c=DPL6_X_6JkXFx7AXWqB0tg&r=oOJsFtgkFCCcDlfLWKmfrVDtpXLihXJTBGJfSapXCV0&m=k3m3ocpqnJDQ1DBLtX6DNqMbG51U4RJ9VyH8qj2SfSc&s=x4VyJNvrS0sFh72jDS_z6ox6SL4zxZLE4lIP3i7tnLc&e=> (emits RecoveryExpr for failed overload resolution).
>>> It does create a form of recovery that affects diagnostics. But the changes exposed by tests are few and seem mostly good (especially compared to emitting CallExpr).
>>> Some open questions:
>>>  - what's the type of the expression when we can't guess? (IntTy in the patch)
>>>  - will there be surprises if RecoveryExpr is emitted in more places and they start to interact?
>>>  - should RecursiveASTVisitor, ASTMatchers etc skip these nodes by default, for compatibility with external code?

I think being conservative when it comes to the RecursiveASTVisitor is probably correct, but at the same time it would be nice if it was traversed by default. That way the existing tools can visit the children of the expression that the compiler thinks are correct so they will have access to a more complete AST. The typo expressions have a bit of a prior art there I suppose, since they actually don’t correspond to the real source, but to the source that the compiler thinks should’ve been written.


>>>  - what are the important use cases that I haven't thought about?
>>> 
>>> 
>>> 
>>> _______________________________________________
>>> cfe-dev mailing list
>>> cfe-dev at lists.llvm.org <mailto:cfe-dev at lists.llvm.org>
>>> https://urldefense.proofpoint.com/v2/url?u=https-3A__lists.llvm.org_cgi-2Dbin_mailman_listinfo_cfe-2Ddev&d=DwIGaQ&c=DPL6_X_6JkXFx7AXWqB0tg&r=oOJsFtgkFCCcDlfLWKmfrVDtpXLihXJTBGJfSapXCV0&m=k3m3ocpqnJDQ1DBLtX6DNqMbG51U4RJ9VyH8qj2SfSc&s=pBwBCrRoYbCV6AZvGdWTEDd5gKfDDajyTQ8VRoQ38LY&e= <https://urldefense.proofpoint.com/v2/url?u=https-3A__lists.llvm.org_cgi-2Dbin_mailman_listinfo_cfe-2Ddev&d=DwIGaQ&c=DPL6_X_6JkXFx7AXWqB0tg&r=oOJsFtgkFCCcDlfLWKmfrVDtpXLihXJTBGJfSapXCV0&m=k3m3ocpqnJDQ1DBLtX6DNqMbG51U4RJ9VyH8qj2SfSc&s=pBwBCrRoYbCV6AZvGdWTEDd5gKfDDajyTQ8VRoQ38LY&e=>
>> _______________________________________________
>> clangd-dev mailing list
>> clangd-dev at lists.llvm.org <mailto:clangd-dev at lists.llvm.org>
>> https://lists.llvm.org/cgi-bin/mailman/listinfo/clangd-dev <https://lists.llvm.org/cgi-bin/mailman/listinfo/clangd-dev>
> 
> _______________________________________________
> cfe-dev mailing list
> cfe-dev at lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/clangd-dev/attachments/20190510/ce6f7814/attachment.html>


More information about the clangd-dev mailing list