/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Portable/Binder/Binder_QueryErrors.cs
246 строк
12 KB
Julien Couvreur
Extensions: address some follow-up comments (#78847)
12 июн 2025, 19:31
Не верифицирован
12 июн 2025, 19:31
225e7f2
Код
Авторство
О чём код?
// 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 Roslyn.Utilities; using System; namespace Microsoft.CodeAnalysis.CSharp { /// <summary> /// This portion of the binder reports errors arising from resolving queries. /// </summary> internal partial class Binder { /// <summary> /// This is a clone of the Dev10 logic for reporting query errors. /// </summary> internal void ReportQueryLookupFailed( SyntaxNode queryClause, BoundExpression instanceArgument, string name, ImmutableArray<Symbol> symbols, BindingDiagnosticBag diagnostics) { FromClauseSyntax fromClause = null; for (SyntaxNode node = queryClause; ; node = node.Parent) { var e = node as QueryExpressionSyntax; if (e != null) { fromClause = e.FromClause; break; } } CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); if (instanceArgument.Type.IsDynamic()) { // CS1979: Query expressions over source type 'dynamic' or with a join sequence of type 'dynamic' are not allowed diagnostics.Add( new DiagnosticInfoWithSymbols(ErrorCode.ERR_BadDynamicQuery, Array.Empty<object>(), symbols), new SourceLocation(queryClause)); } else if (ImplementsStandardQueryInterface(instanceArgument.Type, name, ref useSiteInfo)) { // Could not find an implementation of the query pattern for source type '{0}'. '{1}' not found. Are you missing required assembly references or a using directive for 'System.Linq'? diagnostics.Add(new DiagnosticInfoWithSymbols( ErrorCode.ERR_QueryNoProviderStandard, new object[] { instanceArgument.Type, name }, symbols), new SourceLocation(fromClause != null ? fromClause.Expression : queryClause)); } else if (fromClause != null && fromClause.Type == null && HasCastToQueryProvider(instanceArgument.Type, ref useSiteInfo)) { // Could not find an implementation of the query pattern for source type '{0}'. '{1}' not found. Consider explicitly specifying the type of the range variable '{2}'. diagnostics.Add(new DiagnosticInfoWithSymbols( ErrorCode.ERR_QueryNoProviderCastable, new object[] { instanceArgument.Type, name, fromClause.Identifier.ValueText }, symbols), new SourceLocation(fromClause.Expression)); } else { // Could not find an implementation of the query pattern for source type '{0}'. '{1}' not found. diagnostics.Add(new DiagnosticInfoWithSymbols( ErrorCode.ERR_QueryNoProvider, new object[] { instanceArgument.Type, name }, symbols), new SourceLocation(fromClause != null ? fromClause.Expression : queryClause)); } diagnostics.Add(queryClause, useSiteInfo); } private bool ImplementsStandardQueryInterface(TypeSymbol instanceType, string name, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo) { if (instanceType.TypeKind == TypeKind.Array || name == "Cast" && HasCastToQueryProvider(instanceType, ref useSiteInfo)) { return true; } bool nonUnique = false; var originalType = instanceType.OriginalDefinition; var ienumerable_t = Compilation.GetSpecialType(SpecialType.System_Collections_Generic_IEnumerable_T); var iqueryable_t = Compilation.GetWellKnownType(WellKnownType.System_Linq_IQueryable_T); bool isIenumerable = TypeSymbol.Equals(originalType, ienumerable_t, TypeCompareKind.ConsiderEverything2) || HasUniqueInterface(instanceType, ienumerable_t, ref nonUnique, ref useSiteInfo); bool isQueryable = TypeSymbol.Equals(originalType, iqueryable_t, TypeCompareKind.ConsiderEverything2) || HasUniqueInterface(instanceType, iqueryable_t, ref nonUnique, ref useSiteInfo); return isIenumerable != isQueryable && !nonUnique; } private static bool HasUniqueInterface(TypeSymbol instanceType, NamedTypeSymbol interfaceType, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo) { bool nonUnique = false; return HasUniqueInterface(instanceType, interfaceType, ref nonUnique, ref useSiteInfo); } private static bool HasUniqueInterface(TypeSymbol instanceType, NamedTypeSymbol interfaceType, ref bool nonUnique, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo) { TypeSymbol candidate = null; foreach (var i in instanceType.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo)) { if (TypeSymbol.Equals(i.OriginalDefinition, interfaceType, TypeCompareKind.ConsiderEverything2)) { if ((object)candidate == null) { candidate = i; } else if (!TypeSymbol.Equals(candidate, i, TypeCompareKind.ConsiderEverything2)) { nonUnique = true; return false; // not unique } } } return (object)candidate != null; } private bool HasCastToQueryProvider(TypeSymbol instanceType, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo) { var originalType = instanceType.OriginalDefinition; var ienumerable = Compilation.GetSpecialType(SpecialType.System_Collections_IEnumerable); var iqueryable = Compilation.GetWellKnownType(WellKnownType.System_Linq_IQueryable); bool isIenumerable = TypeSymbol.Equals(originalType, ienumerable, TypeCompareKind.ConsiderEverything2) || HasUniqueInterface(instanceType, ienumerable, ref useSiteInfo); bool isQueryable = TypeSymbol.Equals(originalType, iqueryable, TypeCompareKind.ConsiderEverything2) || HasUniqueInterface(instanceType, iqueryable, ref useSiteInfo); return isIenumerable != isQueryable; } private static bool IsJoinRangeVariableInLeftKey(SimpleNameSyntax node) { for (CSharpSyntaxNode parent = node.Parent; parent != null; parent = parent.Parent) { if (parent.Kind() == SyntaxKind.JoinClause) { var join = (JoinClauseSyntax)parent; if (join.LeftExpression.Span.Contains(node.Span) && join.Identifier.ValueText == node.Identifier.ValueText) return true; } } return false; } private static bool IsInJoinRightKey(SimpleNameSyntax node) { // TODO: refine this test to check if the identifier is the name of a range // variable of the enclosing query. for (CSharpSyntaxNode parent = node.Parent; parent != null; parent = parent.Parent) { if (parent.Kind() == SyntaxKind.JoinClause) { var join = (JoinClauseSyntax)parent; if (join.RightExpression.Span.Contains(node.Span)) return true; } } return false; } internal static void ReportQueryInferenceFailed(CSharpSyntaxNode queryClause, string methodName, BoundExpression receiver, AnalyzedArguments arguments, ImmutableArray<Symbol> symbols, BindingDiagnosticBag diagnostics) { string clauseKind = null; bool multiple = false; switch (queryClause.Kind()) { case SyntaxKind.JoinClause: clauseKind = SyntaxFacts.GetText(SyntaxKind.JoinKeyword); multiple = true; break; case SyntaxKind.LetClause: clauseKind = SyntaxFacts.GetText(SyntaxKind.LetKeyword); break; case SyntaxKind.SelectClause: clauseKind = SyntaxFacts.GetText(SyntaxKind.SelectKeyword); break; case SyntaxKind.WhereClause: clauseKind = SyntaxFacts.GetText(SyntaxKind.WhereKeyword); break; case SyntaxKind.OrderByClause: case SyntaxKind.AscendingOrdering: case SyntaxKind.DescendingOrdering: clauseKind = SyntaxFacts.GetText(SyntaxKind.OrderByKeyword); multiple = true; break; case SyntaxKind.QueryContinuation: clauseKind = SyntaxFacts.GetText(SyntaxKind.IntoKeyword); break; case SyntaxKind.GroupClause: clauseKind = SyntaxFacts.GetText(SyntaxKind.GroupKeyword) + " " + SyntaxFacts.GetText(SyntaxKind.ByKeyword); multiple = true; break; case SyntaxKind.FromClause: if (ReportQueryInferenceFailedSelectMany((FromClauseSyntax)queryClause, methodName, receiver, arguments, symbols, diagnostics)) { return; } clauseKind = SyntaxFacts.GetText(SyntaxKind.FromKeyword); break; default: throw ExceptionUtilities.UnexpectedValue(queryClause.Kind()); } diagnostics.Add(new DiagnosticInfoWithSymbols( multiple ? ErrorCode.ERR_QueryTypeInferenceFailedMulti : ErrorCode.ERR_QueryTypeInferenceFailed, new object[] { clauseKind, methodName }, symbols), queryClause.GetFirstToken().GetLocation()); } private static bool ReportQueryInferenceFailedSelectMany(FromClauseSyntax fromClause, string methodName, BoundExpression receiver, AnalyzedArguments arguments, ImmutableArray<Symbol> symbols, BindingDiagnosticBag diagnostics) { Debug.Assert(methodName == "SelectMany"); // Estimate the return type of Select's lambda argument BoundExpression arg = arguments.Argument(arguments.IncludesReceiverAsArgument ? 1 : 0); TypeSymbol type = null; if (arg.Kind == BoundKind.UnboundLambda) { var unbound = (UnboundLambda)arg; foreach (var t in unbound.Data.InferredReturnTypes()) { if (!t.IsErrorType()) { type = t; break; } } } if ((object)type == null || type.IsErrorType()) { return false; } TypeSymbol receiverType = receiver?.Type; diagnostics.Add(new DiagnosticInfoWithSymbols( ErrorCode.ERR_QueryTypeInferenceFailedSelectMany, new object[] { type, receiverType, methodName }, symbols), fromClause.Expression.Location); return true; } } }