/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Portable/Binder/UsingStatementBinder.cs
341 строка
16 KB
Jan Jones
Unsafe evolution: handle `new()` constraint (#82647)
12 мар 2026, 17:01
Не верифицирован
12 мар 2026, 17:01
836ffa6
Код
Авторство
О чём код?
// 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; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { internal sealed class UsingStatementBinder : LockOrUsingBinder { private readonly UsingStatementSyntax _syntax; public UsingStatementBinder(Binder enclosing, UsingStatementSyntax syntax) : base(enclosing) { _syntax = syntax; } protected override ImmutableArray<LocalSymbol> BuildLocals() { ExpressionSyntax expressionSyntax = TargetExpressionSyntax; VariableDeclarationSyntax declarationSyntax = _syntax.Declaration; Debug.Assert((expressionSyntax == null) ^ (declarationSyntax == null)); // Can't have both or neither. if (expressionSyntax != null) { var locals = ArrayBuilder<LocalSymbol>.GetInstance(); ExpressionVariableFinder.FindExpressionVariables(this, locals, expressionSyntax); return locals.ToImmutableAndFree(); } else { var locals = ArrayBuilder<LocalSymbol>.GetInstance(declarationSyntax.Variables.Count); // gather expression-declared variables from invalid array dimensions. eg. using(int[x is var y] z = new int[0]) declarationSyntax.Type.VisitRankSpecifiers((rankSpecifier, args) => { foreach (var size in rankSpecifier.Sizes) { if (size.Kind() != SyntaxKind.OmittedArraySizeExpression) { ExpressionVariableFinder.FindExpressionVariables(args.binder, args.locals, size); } } }, (binder: this, locals: locals)); foreach (VariableDeclaratorSyntax declarator in declarationSyntax.Variables) { locals.Add(MakeLocal(declarationSyntax, declarator, LocalDeclarationKind.UsingVariable, allowScoped: true)); // also gather expression-declared variables from the bracketed argument lists and the initializers ExpressionVariableFinder.FindExpressionVariables(this, locals, declarator); } return locals.ToImmutableAndFree(); } } protected override ExpressionSyntax TargetExpressionSyntax { get { return _syntax.Expression; } } internal override BoundStatement BindUsingStatementParts(BindingDiagnosticBag diagnostics, Binder originalBinder) { ExpressionSyntax expressionSyntax = TargetExpressionSyntax; VariableDeclarationSyntax declarationSyntax = _syntax.Declaration; bool hasAwait = _syntax.AwaitKeyword.Kind() != default; Debug.Assert((expressionSyntax == null) ^ (declarationSyntax == null)); // Can't have both or neither. var boundUsingStatement = BindUsingStatementOrDeclarationFromParts((CSharpSyntaxNode)expressionSyntax ?? declarationSyntax, _syntax.UsingKeyword, _syntax.AwaitKeyword, originalBinder, this, diagnostics); Debug.Assert(boundUsingStatement is BoundUsingStatement); return boundUsingStatement; } #nullable enable internal static BoundStatement BindUsingStatementOrDeclarationFromParts(SyntaxNode syntax, SyntaxToken usingKeyword, SyntaxToken awaitKeyword, Binder originalBinder, UsingStatementBinder? usingBinderOpt, BindingDiagnosticBag diagnostics) { bool isUsingDeclaration = syntax.Kind() == SyntaxKind.LocalDeclarationStatement; bool isExpression = !isUsingDeclaration && syntax.Kind() != SyntaxKind.VariableDeclaration; bool hasAwait = awaitKeyword != default; if (isUsingDeclaration) { CheckFeatureAvailability(usingKeyword, MessageID.IDS_FeatureUsingDeclarations, diagnostics); } else if (hasAwait) { CheckFeatureAvailability(awaitKeyword, MessageID.IDS_FeatureAsyncUsing, diagnostics); } Debug.Assert(isUsingDeclaration || usingBinderOpt != null); bool hasErrors = false; ImmutableArray<BoundLocalDeclaration> declarationsOpt = default; BoundMultipleLocalDeclarations? multipleDeclarationsOpt = null; BoundExpression? expressionOpt = null; TypeSymbol? declarationTypeOpt = null; MethodArgumentInfo? patternDisposeInfo; TypeSymbol? awaitableTypeOpt; if (isExpression) { expressionOpt = usingBinderOpt!.BindTargetExpression(diagnostics, originalBinder); hasErrors |= !bindDisposable(fromExpression: true, out patternDisposeInfo, out awaitableTypeOpt); Debug.Assert(expressionOpt is not null); if (expressionOpt.Type is not null) { CheckRestrictedTypeInAsyncMethod(originalBinder.ContainingMemberOrLambda, expressionOpt.Type, diagnostics, expressionOpt.Syntax); } } else { VariableDeclarationSyntax declarationSyntax = isUsingDeclaration ? ((LocalDeclarationStatementSyntax)syntax).Declaration : (VariableDeclarationSyntax)syntax; originalBinder.BindForOrUsingOrFixedDeclarations(declarationSyntax, LocalDeclarationKind.UsingVariable, diagnostics, out declarationsOpt); Debug.Assert(!declarationsOpt.IsEmpty && declarationsOpt[0].DeclaredTypeOpt != null); multipleDeclarationsOpt = new BoundMultipleLocalDeclarations(declarationSyntax, declarationsOpt); declarationTypeOpt = declarationsOpt[0].DeclaredTypeOpt!.Type; if (declarationTypeOpt.IsDynamic()) { patternDisposeInfo = null; awaitableTypeOpt = null; } else { hasErrors |= !bindDisposable(fromExpression: false, out patternDisposeInfo, out awaitableTypeOpt); } } BoundAwaitableInfo? awaitOpt = null; if (hasAwait) { // even if we don't have a proper value to await, we'll still report bad usages of `await` originalBinder.ReportBadAwaitDiagnostics(awaitKeyword, diagnostics, ref hasErrors); if (awaitableTypeOpt is null) { awaitOpt = new BoundAwaitableInfo(syntax, awaitableInstancePlaceholder: null, isDynamic: true, getAwaiter: null, isCompleted: null, getResult: null, runtimeAsyncAwaitCall: null, runtimeAsyncAwaitCallPlaceholder: null) { WasCompilerGenerated = true }; } else { hasErrors |= ReportUseSite(awaitableTypeOpt, diagnostics, awaitKeyword); var placeholder = new BoundAwaitableValuePlaceholder(syntax, awaitableTypeOpt).MakeCompilerGenerated(); awaitOpt = originalBinder.BindAwaitInfo(placeholder, syntax, diagnostics, ref hasErrors); } } // This is not awesome, but its factored. // In the future it might be better to have a separate shared type that we add the info to, and have the callers create the appropriate bound nodes from it if (isUsingDeclaration) { return new BoundUsingLocalDeclarations(syntax, patternDisposeInfo, awaitOpt, declarationsOpt, hasErrors); } else { BoundStatement boundBody = originalBinder.BindPossibleEmbeddedStatement(usingBinderOpt!._syntax.Statement, diagnostics); return new BoundUsingStatement( usingBinderOpt._syntax, usingBinderOpt.Locals, multipleDeclarationsOpt, expressionOpt, boundBody, awaitOpt, patternDisposeInfo, hasErrors); } bool bindDisposable(bool fromExpression, out MethodArgumentInfo? patternDisposeInfo, out TypeSymbol? awaitableType) { patternDisposeInfo = null; awaitableType = null; Debug.Assert(!fromExpression || expressionOpt != null); TypeSymbol? type = fromExpression ? expressionOpt!.Type : declarationTypeOpt; // Pattern-based binding // If this is a ref struct, or we're in a valid asynchronous using, try binding via pattern. if (type is object && (type.IsRefLikeType || hasAwait)) { BoundExpression? receiver = fromExpression ? expressionOpt : new BoundLocal(syntax, declarationsOpt[0].LocalSymbol, null, type) { WasCompilerGenerated = true }; BindingDiagnosticBag patternDiagnostics = BindingDiagnosticBag.GetInstance(diagnostics); MethodSymbol disposeMethod = originalBinder.TryFindDisposePatternMethod(receiver, syntax, hasAwait, patternDiagnostics, out bool expanded); if (disposeMethod is object) { Debug.Assert(!disposeMethod.IsExtensionMethod && !disposeMethod.IsExtensionBlockMember(), "No extension disposal. See TryFindDisposePatternMethod"); diagnostics.AddRangeAndFree(patternDiagnostics); MessageID.IDS_FeatureDisposalPattern.CheckFeatureAvailability(diagnostics, originalBinder.Compilation, syntax.Location); var argumentsBuilder = ArrayBuilder<BoundExpression>.GetInstance(disposeMethod.ParameterCount); ImmutableArray<int> argsToParams = default; originalBinder.BindDefaultArguments( // If this is a using statement, then we want to use the whole `using (expr) { }` as the argument location. These arguments // will be represented in the IOperation tree and the "correct" node for them, given that they are an implicit invocation // at the end of the using statement, is on the whole using statement, not on the current expression. usingBinderOpt?._syntax ?? syntax, disposeMethod.Parameters, extensionReceiver: null, argumentsBuilder, argumentRefKindsBuilder: null, namesBuilder: null, ref argsToParams, out BitVector defaultArguments, expanded, enableCallerInfo: true, diagnostics); Debug.Assert(argsToParams.IsDefault); patternDisposeInfo = new MethodArgumentInfo(disposeMethod, argumentsBuilder.ToImmutableAndFree(), defaultArguments, expanded); if (hasAwait) { awaitableType = disposeMethod.ReturnType; } return true; } patternDiagnostics.Free(); } // Interface binding NamedTypeSymbol disposableInterface = getDisposableInterface(hasAwait); Debug.Assert((object)disposableInterface != null); bool implementsIDisposable = implementsInterface(fromExpression, disposableInterface, diagnostics); if (implementsIDisposable) { if (hasAwait) { awaitableType = originalBinder.Compilation.GetWellKnownType(WellKnownType.System_Threading_Tasks_ValueTask); } else { var disposeMethod = originalBinder.Compilation.GetSpecialTypeMember(SpecialMember.System_IDisposable__Dispose); if (disposeMethod != null) { originalBinder.ReportDiagnosticsIfUnsafeMemberAccess(diagnostics, disposeMethod, syntax); } } return !ReportUseSite(disposableInterface, diagnostics, hasAwait ? awaitKeyword : usingKeyword); } if (type is null || !type.IsErrorType()) { // Retry with a different assumption about whether the `using` is async NamedTypeSymbol alternateInterface = getDisposableInterface(!hasAwait); bool implementsAlternateIDisposable = implementsInterface(fromExpression, alternateInterface, BindingDiagnosticBag.Discarded); ErrorCode errorCode = implementsAlternateIDisposable ? (hasAwait ? ErrorCode.ERR_NoConvToIAsyncDispWrongAsync : ErrorCode.ERR_NoConvToIDispWrongAsync) : (hasAwait ? ErrorCode.ERR_NoConvToIAsyncDisp : ErrorCode.ERR_NoConvToIDisp); Error(diagnostics, errorCode, syntax, declarationTypeOpt ?? expressionOpt!.Display); } return false; } bool implementsInterface(bool fromExpression, NamedTypeSymbol targetInterface, BindingDiagnosticBag diagnostics) { var conversions = originalBinder.Conversions; CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = originalBinder.GetNewCompoundUseSiteInfo(diagnostics); bool result; bool needSupportForRefStructInterfaces; if (fromExpression) { Debug.Assert(expressionOpt is { }); result = conversions.HasImplicitConversionToOrImplementsVarianceCompatibleInterface(expressionOpt, targetInterface, ref useSiteInfo, out needSupportForRefStructInterfaces); } else { Debug.Assert(declarationTypeOpt is { }); result = conversions.HasImplicitConversionToOrImplementsVarianceCompatibleInterface(declarationTypeOpt, targetInterface, ref useSiteInfo, out needSupportForRefStructInterfaces); } diagnostics.Add(syntax, useSiteInfo); if (needSupportForRefStructInterfaces && (fromExpression ? expressionOpt!.Type : declarationTypeOpt)!.ContainingModule != originalBinder.Compilation.SourceModule) { CheckFeatureAvailability(syntax, MessageID.IDS_FeatureRefStructInterfaces, diagnostics); } return result; } NamedTypeSymbol getDisposableInterface(bool isAsync) { return isAsync ? originalBinder.Compilation.GetWellKnownType(WellKnownType.System_IAsyncDisposable) : originalBinder.Compilation.GetSpecialType(SpecialType.System_IDisposable); } } internal override ImmutableArray<LocalSymbol> GetDeclaredLocalsForScope(SyntaxNode scopeDesignator) { if (_syntax == scopeDesignator) { return this.Locals; } throw ExceptionUtilities.Unreachable(); } internal override ImmutableArray<LocalFunctionSymbol> GetDeclaredLocalFunctionsForScope(CSharpSyntaxNode scopeDesignator) { throw ExceptionUtilities.Unreachable(); } internal override SyntaxNode ScopeDesignator { get { return _syntax; } } } }