| File: Binder\Binder.QueryTranslationState.cs | Web Access |
| Project: src\roslyn\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; namespace Microsoft.CodeAnalysis.CSharp { internal partial class Binder { private class QueryTranslationState { // Represents the current translation state for a query. Consider a query of the form // from ID in EXPR { clauses } SELECT ... #if DEBUG /// <summary> /// For debug assert only /// </summary> public string nextInvokedMethodName; #endif // EXPR, above public BoundExpression fromExpression; // ID, above. This may possibly be the special "transparent" identifier public RangeVariableSymbol rangeVariable; // the clauses, above. The top of the stack is the leftmost clause public readonly Stack<QueryClauseSyntax> clauses = new Stack<QueryClauseSyntax>(); // the SELECT clause above (or a groupby clause in its place) public SelectOrGroupClauseSyntax selectOrGroup; // all query variables in scope, including those visible through transparent identifiers // introduced in previous translation phases. Every query variable in scope is a key in // this dictionary. To compute its value, one consults the list of strings that are the // value in this map. If it is empty, there is a lambda parameter for the variable. If it // is nonempty, one starts with the transparent lambda identifier, and follows fields of the // given names in reverse order. So, for example, if the strings are ["a", "b"], the query // variable is represented by the expression TRANSPARENT.b.a where TRANSPARENT is a parameter // of the current lambda expression. public readonly Dictionary<RangeVariableSymbol, ArrayBuilder<string>> allRangeVariables = new Dictionary<RangeVariableSymbol, ArrayBuilder<string>>(); public static RangeVariableMap RangeVariableMap(params RangeVariableSymbol[] parameters) { var result = new RangeVariableMap(); foreach (var vars in parameters) { result.Add(vars, ImmutableArray<string>.Empty); } return result; } public RangeVariableMap RangeVariableMap() { var result = new RangeVariableMap(); foreach (var vars in allRangeVariables.Keys) { result.Add(vars, allRangeVariables[vars].ToImmutable()); } return result; } internal RangeVariableSymbol AddRangeVariable(Binder binder, SyntaxToken identifier, BindingDiagnosticBag diagnostics) { string name = identifier.ValueText; var result = new RangeVariableSymbol(name, binder.ContainingMemberOrLambda, identifier.GetLocation()); bool error = false; Debug.Assert(identifier.Parent is { }); binder.ReportFieldContextualKeywordConflictIfAny(result, identifier.Parent, identifier, diagnostics); foreach (var existingRangeVariable in allRangeVariables.Keys) { if (existingRangeVariable.Name == name) { diagnostics.Add(ErrorCode.ERR_QueryDuplicateRangeVariable, identifier.GetLocation(), name); error = true; } } if (!error && (object)diagnostics != BindingDiagnosticBag.Discarded) { var collisionDetector = new LocalScopeBinder(binder); collisionDetector.ValidateDeclarationNameConflictsInScope(result, diagnostics); } allRangeVariables.Add(result, ArrayBuilder<string>.GetInstance()); return result; } // Add a new lambda that is a transparent identifier, by providing the name that is the // field of the new transparent lambda parameter that contains the old variables. internal void AddTransparentIdentifier(string name) { foreach (var b in allRangeVariables.Values) { b.Add(name); } } private int _nextTransparentIdentifierNumber; internal string TransparentRangeVariableName() { return transparentIdentifierPrefix + _nextTransparentIdentifierNumber++; } internal RangeVariableSymbol TransparentRangeVariable(Binder binder) { var transparentIdentifier = TransparentRangeVariableName(); return new RangeVariableSymbol(transparentIdentifier, binder.ContainingMemberOrLambda, null, true); } public void Clear() { #if DEBUG Debug.Assert(nextInvokedMethodName is null); nextInvokedMethodName = null; #endif fromExpression = null; rangeVariable = null; selectOrGroup = null; foreach (var b in allRangeVariables.Values) b.Free(); allRangeVariables.Clear(); clauses.Clear(); } public void Free() { Clear(); } } } }