Attempt to pass more detailed information about query expressions to the outside world #82
Loading…
Reference in a new issue
No description provided.
Delete branch "DetailedQueryExpressionInformation"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
NRefactory can currently resolve query experssions, in the sense that it can determine that
from a in arr select a + 1is a call toEnumerable.Select<int>, but it does not report other interesting semantics, such asfrom a in arr let b = a + 1 select a + bis transformed to something likearr.Select(a => new { a, b = a + 1 }).Select(x => x.a + x.b)This commit is a POC of how an API for this could look. It contains the following changes:
RangeVariableMapthat can be used to determine how to access local variables inside the lambda body.QueryExpressionLambdas.x => new { x, newValue = expr }(it used to be meaningless).This is by no means complete work, it's just intended as a starting point for a discussion of how the feature could look. It is also not implemented for the entire pattern, but as a test expression, it works for
(and I also modified the demo program to show the relevant info).
What do you think?
(Btw, thanks for giving me credit as an author yesterday, it's a great honor).
I would have preferred to make the body of the
QueryExpressionLambdacontain the actual lambda, but this requires:i, it substitutes something else (which wasn't hard to do, I did it but then I discarded the implementation), andI think this is going to get horribly complicated very fast. The current query expression resolve logic already is one of the more complicated portions of the resolver.
I think the current semantic tree is OK - the lambda body will point to the actual range variable. The whole "transparent identifier" mess is only really interesting for compilers - for other analysis tools, it might even make the analysis harder (e.g. analysing how the data is flowing through the whole lambda).
Alternative idea: query expressions are defined as a pure syntactic transformation in the C# specification. We could implement that transformation in NRefactory, and you could just apply it after parsing, prior to resolving.
In fact the syntactic transformation could also be done within the ResolveVisitor, using a nested visitor to resolve the transformed AST.
But that causes the same issues as the nested visitor in general: the children of the queries' ResolveResult aren't related to the ResolveResults for the child AST nodes anymore. (the
Dictionary<LambdaResolveResult, LambdaExpression>idea from #40).I think you just can't have both at the same time - either you resolve range variables to a
LocalResolveResultas the code currently does, or you resolve them to their underlying representation (lambda parameter, member of anonymous type, ...).Maybe we could introduce an option on
CSharpAstResolverthat you can use to determine how you want queries to be resolved?As for the implementation of the syntactic transformation in the ResolveVisitor:
class QueryExpression, add a "Expression Translate()" method. This would clone the AST node and perform query expression translation (§7.16.2)1b. The translate method also somehow needs to provide a mapping between old AST nodes and new AST nodes (
Dictionary<AstNode, AstNode>?)LambdaTypeHypothesis.MergeInto), using theDictionary<AstNode, AstNode>to translate between new AST and original AST.But it's probably easier just to implement an in-place
QueryExpression.Translate()and let the compiler call that.My only reason for doing this was that the code that does much of the work that I need is already some 500 lines of complicated code so I thought it would be less work to modify that than to start from scratch. But you're probably right that it is going to be horribly complicated so it will be less work to just implement the syntactic transformation in a separate method.
If this new hypothetical method also returns the mappings, it could probably be used to uncomplicate things in the ResolveVisitor (eg. get rid of the
ResolveVisitor.GetTypeForQueryVariablemethod which won't work when someone does funky stuff).There are better ways to achieve the goal
Pull request closed