diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 806cac131a1a5..805b05793cfc6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -389,3 +389,8 @@ stages: - script: ./eng/build.sh --solution Roslyn.sln --restore --build --configuration Debug --prepareMachine --ci --binaryLog --runanalyzers --warnaserror /p:RoslynEnforceCodeStyle=true displayName: Build with analyzers + + - template: eng/pipelines/publish-logs.yml + parameters: + jobName: Correctness_Analyzers + configuration: Debug diff --git a/docs/Language Feature Status.md b/docs/Language Feature Status.md index 361f44b3e96c0..fe565913a002f 100644 --- a/docs/Language Feature Status.md +++ b/docs/Language Feature Status.md @@ -10,6 +10,7 @@ efforts behind them. | Feature | Branch | State | Developer | Reviewer | LDM Champ | | ------- | ------ | ----- | --------- | -------- | --------- | +| [Inline Arrays](https://github.com/dotnet/csharplang/blob/main/proposals/inline-arrays.md) | [InlineArrays](https://github.com/dotnet/roslyn/tree/features/InlineArrays) | [In Progress](https://github.com/dotnet/roslyn/issues/67826) | [AlekseyTs](https://github.com/AlekseyTs) | [cston](https://github.com/cston), [jjonescz](https://github.com/jjonescz) | | | [Using aliases for any type](https://github.com/dotnet/csharplang/issues/4284) | [UsingAliasTypes](https://github.com/dotnet/roslyn/tree/features/UsingAliasTypes) | [Merged into 17.6.P3](https://github.com/dotnet/roslyn/issues/56323) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | [jcouv](https://github.com/jcouv) [cston](https://github.com/cston) | | | [Primary Constructors](https://github.com/dotnet/csharplang/issues/2691) | [PrimaryConstructors](https://github.com/dotnet/roslyn/tree/features/PrimaryConstructors) | [Merged into 17.6.P2](https://github.com/dotnet/roslyn/issues/65697) | [AlekseyTs](https://github.com/AlekseyTs) | [cston](https://github.com/cston), [jjonescz](https://github.com/jjonescz) | [MadsTorgersen](https://github.com/MadsTorgersen) | | [Semi-auto-properties](https://github.com/dotnet/csharplang/issues/140) | [semi-auto-props](https://github.com/dotnet/roslyn/tree/features/semi-auto-props) | [In Progress](https://github.com/dotnet/roslyn/issues/57012) | [Youssef1313](https://github.com/Youssef1313) | [333fred](https://github.com/333fred), [RikkiGibson](https://github.com/RikkiGibson) | [CyrusNajmabadi](https://github.com/CyrusNajmabadi) | diff --git a/eng/targets/Settings.props b/eng/targets/Settings.props index 5e9984f3f52e9..f00c32423d9f3 100644 --- a/eng/targets/Settings.props +++ b/eng/targets/Settings.props @@ -53,14 +53,63 @@ true - - net6.0 - net8.0;$(SourceBuildTargetFrameworks) - net8.0 + + + + + + $(NetPrevious) + $(SourceBuildToolsetTargetFramework) + $(NetCurrent);$(NetPrevious) + + - $(SourceBuildTargetFrameworks) - $(SourceBuildTargetFrameworksNetFx);net472 - + + + + $(NetCurrent) + $(SourceBuildToolsetTargetFramework) + $(NetCurrent) + + + + + + + net6.0 + $(NetCurrent);$(NetPrevious);$(SourceBuildToolsetTargetFramework) + $(SourceBuildToolsetTargetFrameworks) + + + + + + + net6.0 + $(SourceBuildToolsetTargetFramework);net7.0 + $(SourceBuildToolsetTargetFrameworks) + + + - - + diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.cs b/src/Compilers/CSharp/Portable/Binder/Binder.cs index ca2362131ea6c..a47b97af47889 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.cs @@ -739,7 +739,7 @@ internal static void ReportDiagnosticsIfObsoleteInternal(BindingDiagnosticBag di } } - internal static void ReportDiagnosticsIfUnmanagedCallersOnly(BindingDiagnosticBag diagnostics, MethodSymbol symbol, Location location, bool isDelegateConversion) + internal static void ReportDiagnosticsIfUnmanagedCallersOnly(BindingDiagnosticBag diagnostics, MethodSymbol symbol, SyntaxNodeOrToken syntax, bool isDelegateConversion) { var unmanagedCallersOnlyAttributeData = symbol.GetUnmanagedCallersOnlyAttributeData(forceComplete: false); if (unmanagedCallersOnlyAttributeData != null) @@ -747,13 +747,14 @@ internal static void ReportDiagnosticsIfUnmanagedCallersOnly(BindingDiagnosticBa // Either we haven't yet bound the attributes of this method, or there is an UnmanagedCallersOnly present. // In the former case, we use a lazy diagnostic that may end up being ignored later, to avoid causing a // binding cycle. + Debug.Assert(syntax.GetLocation() != null); diagnostics.Add(unmanagedCallersOnlyAttributeData == UnmanagedCallersOnlyAttributeData.Uninitialized - ? (DiagnosticInfo)new LazyUnmanagedCallersOnlyMethodCalledDiagnosticInfo(symbol, isDelegateConversion) + ? new LazyUnmanagedCallersOnlyMethodCalledDiagnosticInfo(symbol, isDelegateConversion) : new CSDiagnosticInfo(isDelegateConversion ? ErrorCode.ERR_UnmanagedCallersOnlyMethodsCannotBeConvertedToDelegate : ErrorCode.ERR_UnmanagedCallersOnlyMethodsCannotBeCalledDirectly, symbol), - location); + syntax.GetLocation()!); } } diff --git a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs index 88e343755753b..13e97132b18c4 100644 --- a/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs +++ b/src/Compilers/CSharp/Portable/Binder/BinderFactory.BinderFactoryVisitor.cs @@ -544,7 +544,7 @@ private Symbol GetMemberSymbol(string memberName, TextSpan memberSpan, NamedType { Debug.Assert(kind is SymbolKind.Method or SymbolKind.Property or SymbolKind.Event); - if (container is SourceMemberContainerTypeSymbol { PrimaryConstructor: not null } sourceMemberContainerTypeSymbol) + if (container is SourceMemberContainerTypeSymbol { HasPrimaryConstructor: true } sourceMemberContainerTypeSymbol) { foreach (Symbol sym in sourceMemberContainerTypeSymbol.GetMembersToMatchAgainstDeclarationSpan()) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_AnonymousTypes.cs b/src/Compilers/CSharp/Portable/Binder/Binder_AnonymousTypes.cs index d8373936fba69..94c4679db30e1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_AnonymousTypes.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_AnonymousTypes.cs @@ -20,7 +20,7 @@ internal partial class Binder { private BoundExpression BindAnonymousObjectCreation(AnonymousObjectCreationExpressionSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureAnonymousTypes.CheckFeatureAvailability(diagnostics, node, node.NewKeyword.GetLocation()); + MessageID.IDS_FeatureAnonymousTypes.CheckFeatureAvailability(diagnostics, node.NewKeyword); // prepare var initializers = node.Initializers; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs index e90c8e703b2da..ab1233623adf2 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs @@ -57,8 +57,7 @@ internal static void BindAttributeTypes( // Check the attribute type (unless the attribute type is already an error). if (boundTypeSymbol.TypeKind != TypeKind.Error) { - var location = attributeToBind.Name.GetLocation(); - binder.CheckDisallowedAttributeDependentType(boundType, location, diagnostics); + binder.CheckDisallowedAttributeDependentType(boundType, attributeToBind.Name, diagnostics); } boundAttributeTypes[i] = boundTypeSymbol; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs index 9f234db5e7507..bc4ae144fe345 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Await.cs @@ -18,7 +18,7 @@ internal partial class Binder { private BoundExpression BindAwait(AwaitExpressionSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureAsync.CheckFeatureAvailability(diagnostics, node, node.AwaitKeyword.GetLocation()); + MessageID.IDS_FeatureAsync.CheckFeatureAvailability(diagnostics, node.AwaitKeyword); BoundExpression expression = BindRValueWithoutTargetType(node.Expression, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 525e00a5d611b..9f4e83b3466f1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -1477,7 +1477,7 @@ private bool MethodGroupConversionHasErrors( CheckValidScopedMethodConversion(syntax, selectedMethod, delegateOrFuncPtrType, isExtensionMethod, diagnostics); if (!isAddressOf) { - ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, selectedMethod, location, isDelegateConversion: true); + ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, selectedMethod, syntax, isDelegateConversion: true); } ReportDiagnosticsIfObsolete(diagnostics, selectedMethod, syntax, hasBaseReceiver: false); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 4e4f7788ca830..65b67de3ce739 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -835,7 +835,7 @@ private BoundExpression BindScopedType(ExpressionSyntax node, BindingDiagnosticB private BoundExpression BindThrowExpression(ThrowExpressionSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureThrowExpression.CheckFeatureAvailability(diagnostics, node, node.ThrowKeyword.GetLocation()); + MessageID.IDS_FeatureThrowExpression.CheckFeatureAvailability(diagnostics, node.ThrowKeyword); bool hasErrors = node.HasErrors; if (!IsThrowExpressionInProperContext(node)) @@ -1358,29 +1358,29 @@ private BoundExpression BindTypeOf(TypeOfExpressionSyntax node, BindingDiagnosti } /// Called when an "attribute-dependent" type such as 'dynamic', 'string?', etc. is not permitted. - private void CheckDisallowedAttributeDependentType(TypeWithAnnotations typeArgument, Location errorLocation, BindingDiagnosticBag diagnostics) + private void CheckDisallowedAttributeDependentType(TypeWithAnnotations typeArgument, NameSyntax attributeName, BindingDiagnosticBag diagnostics) { typeArgument.VisitType(type: null, static (typeWithAnnotations, arg, _) => { - var (errorLocation, diagnostics) = arg; + var (attributeName, diagnostics) = arg; var type = typeWithAnnotations.Type; if (type.IsDynamic() || (typeWithAnnotations.NullableAnnotation.IsAnnotated() && !type.IsValueType) || type.IsNativeIntegerWrapperType || (type.IsTupleType && !type.TupleElementNames.IsDefault)) { - diagnostics.Add(ErrorCode.ERR_AttrDependentTypeNotAllowed, errorLocation, type); + diagnostics.Add(ErrorCode.ERR_AttrDependentTypeNotAllowed, attributeName, type); return true; } if (type.IsUnboundGenericType() || type.Kind == SymbolKind.TypeParameter) { - diagnostics.Add(ErrorCode.ERR_AttrTypeArgCannotBeTypeVar, errorLocation, type); + diagnostics.Add(ErrorCode.ERR_AttrTypeArgCannotBeTypeVar, attributeName, type); return true; } return false; - }, typePredicate: null, arg: (errorLocation, diagnostics)); + }, typePredicate: null, arg: (attributeName, diagnostics)); } private BoundExpression BindSizeOf(SizeOfExpressionSyntax node, BindingDiagnosticBag diagnostics) @@ -1441,7 +1441,7 @@ internal static ConstantValue GetConstantSizeOf(TypeSymbol type) private BoundExpression BindDefaultExpression(DefaultExpressionSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureDefault.CheckFeatureAvailability(diagnostics, node, node.Keyword.GetLocation()); + MessageID.IDS_FeatureDefault.CheckFeatureAvailability(diagnostics, node.Keyword); TypeWithAnnotations typeWithAnnotations = this.BindType(node.Type, diagnostics, out AliasSymbol alias); var typeExpression = new BoundTypeExpression(node.Type, aliasOpt: alias, typeWithAnnotations); @@ -1776,7 +1776,7 @@ private BoundExpression SynthesizeMethodGroupReceiver(CSharpSyntaxNode syntax, A private bool IsBadLocalOrParameterCapture(Symbol symbol, TypeSymbol type, RefKind refKind) { - if (refKind != RefKind.None || type.IsRefLikeType) + if (refKind != RefKind.None || type.IsRestrictedType()) { var containingMethod = this.ContainingMemberOrLambda as MethodSymbol; if ((object)containingMethod != null && (object)symbol.ContainingSymbol != (object)containingMethod) @@ -1899,7 +1899,15 @@ private BoundExpression BindNonMethod(SimpleNameSyntax node, Symbol symbol, Bind if (IsBadLocalOrParameterCapture(localSymbol, type, localSymbol.RefKind)) { isError = true; - Error(diagnostics, ErrorCode.ERR_AnonDelegateCantUseLocal, node, localSymbol); + + if (localSymbol.RefKind == RefKind.None && type.IsRestrictedType(ignoreSpanLikeTypes: true)) + { + Error(diagnostics, ErrorCode.ERR_SpecialByRefInLambda, node, type); + } + else + { + Error(diagnostics, ErrorCode.ERR_AnonDelegateCantUseLocal, node, localSymbol); + } } } @@ -1931,7 +1939,20 @@ private BoundExpression BindNonMethod(SimpleNameSyntax node, Symbol symbol, Bind if (IsBadLocalOrParameterCapture(parameter, parameter.Type, parameter.RefKind)) { isError = true; - Error(diagnostics, parameter.Type.IsRefLikeType ? ErrorCode.ERR_AnonDelegateCantUseRefLike : ErrorCode.ERR_AnonDelegateCantUse, node, parameter.Name); + + if (parameter.RefKind != RefKind.None) + { + Error(diagnostics, ErrorCode.ERR_AnonDelegateCantUse, node, parameter.Name); + } + else if (parameter.Type.IsRestrictedType(ignoreSpanLikeTypes: true)) + { + Error(diagnostics, ErrorCode.ERR_SpecialByRefInLambda, node, parameter.Type); + } + else + { + Debug.Assert(parameter.Type.IsRefLikeType); + Error(diagnostics, ErrorCode.ERR_AnonDelegateCantUseRefLike, node, parameter.Name); + } } else if (primaryCtor is not null) { @@ -1939,10 +1960,22 @@ private BoundExpression BindNonMethod(SimpleNameSyntax node, Symbol symbol, Bind bool capture = (this.ContainingMember() is MethodSymbol containingMethod && (object)primaryCtor != containingMethod); if (capture && - (parameter.RefKind != RefKind.None || parameter.Type.IsRefLikeType) && + (parameter.RefKind != RefKind.None || parameter.Type.IsRestrictedType()) && !IsInsideNameof) { - Error(diagnostics, parameter.Type.IsRefLikeType ? ErrorCode.ERR_UnsupportedPrimaryConstructorParameterCapturingRefLike : ErrorCode.ERR_UnsupportedPrimaryConstructorParameterCapturingRef, node, parameter.Name); + if (parameter.RefKind != RefKind.None) + { + Error(diagnostics, ErrorCode.ERR_UnsupportedPrimaryConstructorParameterCapturingRef, node, parameter.Name); + } + else if (parameter.Type.IsRestrictedType(ignoreSpanLikeTypes: true)) + { + Error(diagnostics, ErrorCode.ERR_UnsupportedPrimaryConstructorParameterCapturingRefAny, node, parameter.Type); + } + else + { + Debug.Assert(parameter.Type.IsRefLikeType); + Error(diagnostics, ErrorCode.ERR_UnsupportedPrimaryConstructorParameterCapturingRefLike, node, parameter.Name); + } } else if (primaryCtor is { ThisParameter.RefKind: not RefKind.None } && this.ContainingMemberOrLambda is MethodSymbol { MethodKind: MethodKind.AnonymousFunction or MethodKind.LocalFunction } && @@ -2075,19 +2108,18 @@ private BoundExpression SynthesizeReceiver(SyntaxNode node, Symbol member, Bindi (currentType.IsInterface && (declaringType.IsObjectType() || currentType.AllInterfacesNoUseSiteDiagnostics.Contains(declaringType)))) { bool hasErrors = false; - if (EnclosingNameofArgument != node) + if (!IsInsideNameof || (EnclosingNameofArgument != node && !node.IsFeatureEnabled(MessageID.IDS_FeatureInstanceMemberInNameof))) { + DiagnosticInfo diagnosticInfoOpt = null; if (InFieldInitializer && !currentType.IsScriptClass) { //can't access "this" in field initializers - Error(diagnostics, ErrorCode.ERR_FieldInitRefNonstatic, node, member); - hasErrors = true; + diagnosticInfoOpt = new CSDiagnosticInfo(ErrorCode.ERR_FieldInitRefNonstatic, member); } else if (InConstructorInitializer || InAttributeArgument) { //can't access "this" in constructor initializers or attribute arguments - Error(diagnostics, ErrorCode.ERR_ObjectRequired, node, member); - hasErrors = true; + diagnosticInfoOpt = new CSDiagnosticInfo(ErrorCode.ERR_ObjectRequired, member); } else { @@ -2099,12 +2131,24 @@ private BoundExpression SynthesizeReceiver(SyntaxNode node, Symbol member, Bindi if (!locationIsInstanceMember) { // error CS0120: An object reference is required for the non-static field, method, or property '{0}' - Error(diagnostics, ErrorCode.ERR_ObjectRequired, node, member); - hasErrors = true; + diagnosticInfoOpt = new CSDiagnosticInfo(ErrorCode.ERR_ObjectRequired, member); } } - hasErrors = hasErrors || IsRefOrOutThisParameterCaptured(node, diagnostics); + diagnosticInfoOpt ??= GetDiagnosticIfRefOrOutThisParameterCaptured(); + hasErrors = diagnosticInfoOpt is not null; + + if (hasErrors) + { + if (IsInsideNameof) + { + CheckFeatureAvailability(node, MessageID.IDS_FeatureInstanceMemberInNameof, diagnostics); + } + else + { + Error(diagnostics, diagnosticInfoOpt, node); + } + } } return ThisReference(node, currentType, hasErrors, wasCompilerGenerated: true); @@ -2276,20 +2320,35 @@ private BoundThisReference ThisReference(SyntaxNode node, NamedTypeSymbol thisTy return new BoundThisReference(node, thisTypeOpt ?? CreateErrorType(), hasErrors) { WasCompilerGenerated = wasCompilerGenerated }; } +#nullable enable private bool IsRefOrOutThisParameterCaptured(SyntaxNodeOrToken thisOrBaseToken, BindingDiagnosticBag diagnostics) { - ParameterSymbol thisSymbol = this.ContainingMemberOrLambda.EnclosingThisSymbol(); - // If there is no this parameter, then it is definitely not captured and - // any diagnostic would be cascading. - if ((object)thisSymbol != null && thisSymbol.ContainingSymbol != ContainingMemberOrLambda && thisSymbol.RefKind != RefKind.None) + if (GetDiagnosticIfRefOrOutThisParameterCaptured() is { } diagnosticInfo) { - Error(diagnostics, ErrorCode.ERR_ThisStructNotInAnonMeth, thisOrBaseToken); + var location = thisOrBaseToken.GetLocation(); + Debug.Assert(location is not null); + Error(diagnostics, diagnosticInfo, location); return true; } return false; } + private DiagnosticInfo? GetDiagnosticIfRefOrOutThisParameterCaptured() + { + Debug.Assert(this.ContainingMemberOrLambda is not null); + ParameterSymbol? thisSymbol = this.ContainingMemberOrLambda.EnclosingThisSymbol(); + // If there is no this parameter, then it is definitely not captured and + // any diagnostic would be cascading. + if (thisSymbol is not null && thisSymbol.ContainingSymbol != ContainingMemberOrLambda && thisSymbol.RefKind != RefKind.None) + { + return new CSDiagnosticInfo(ErrorCode.ERR_ThisStructNotInAnonMeth); + } + + return null; + } +#nullable disable + private BoundBaseReference BindBase(BaseExpressionSyntax node, BindingDiagnosticBag diagnostics) { bool hasErrors = false; @@ -2868,12 +2927,12 @@ private void BindArgumentAndName( private BoundExpression BindArgumentValue(BindingDiagnosticBag diagnostics, ArgumentSyntax argumentSyntax, bool allowArglist, RefKind refKind) { if (argumentSyntax.RefKindKeyword.IsKind(SyntaxKind.InKeyword)) - MessageID.IDS_FeatureReadOnlyReferences.CheckFeatureAvailability(diagnostics, argumentSyntax, argumentSyntax.RefKindKeyword.GetLocation()); + MessageID.IDS_FeatureReadOnlyReferences.CheckFeatureAvailability(diagnostics, argumentSyntax.RefKindKeyword); if (argumentSyntax.Expression.Kind() == SyntaxKind.DeclarationExpression) { if (argumentSyntax.RefKindKeyword.IsKind(SyntaxKind.OutKeyword)) - MessageID.IDS_FeatureOutVar.CheckFeatureAvailability(diagnostics, argumentSyntax, argumentSyntax.RefKindKeyword.GetLocation()); + MessageID.IDS_FeatureOutVar.CheckFeatureAvailability(diagnostics, argumentSyntax.RefKindKeyword); var declarationExpression = (DeclarationExpressionSyntax)argumentSyntax.Expression; if (declarationExpression.IsOutDeclaration()) @@ -3403,7 +3462,7 @@ private BoundExpression BindArrayDimension(ExpressionSyntax dimension, BindingDi private BoundExpression BindImplicitArrayCreationExpression(ImplicitArrayCreationExpressionSyntax node, BindingDiagnosticBag diagnostics) { // See BindArrayCreationExpression method above for implicitly typed array creation SPEC. - MessageID.IDS_FeatureImplicitArray.CheckFeatureAvailability(diagnostics, node, node.NewKeyword.GetLocation()); + MessageID.IDS_FeatureImplicitArray.CheckFeatureAvailability(diagnostics, node.NewKeyword); InitializerExpressionSyntax initializer = node.Initializer; int rank = node.Commas.Count + 1; @@ -3874,7 +3933,7 @@ private bool ReportBadStackAllocPosition(SyntaxNode node, BindingDiagnosticBag d inLegalPosition = (IsInMethodBody || IsLocalFunctionsScopeBinder) && node.IsLegalCSharp73SpanStackAllocPosition(); if (!inLegalPosition) { - MessageID.IDS_FeatureNestedStackalloc.CheckFeatureAvailability(diagnostics, node, node.GetFirstToken().GetLocation()); + MessageID.IDS_FeatureNestedStackalloc.CheckFeatureAvailability(diagnostics, node.GetFirstToken()); } } @@ -3956,7 +4015,7 @@ private BoundExpression BindStackAllocWithInitializer( { Debug.Assert(node.IsKind(SyntaxKind.ImplicitStackAllocArrayCreationExpression) || node.IsKind(SyntaxKind.StackAllocArrayCreationExpression)); - MessageID.IDS_FeatureStackAllocInitializer.CheckFeatureAvailability(diagnostics, node, stackAllocKeyword.GetLocation()); + MessageID.IDS_FeatureStackAllocInitializer.CheckFeatureAvailability(diagnostics, stackAllocKeyword); if (boundInitExprOpt.IsDefault) { @@ -4403,7 +4462,7 @@ constructor is not SynthesizedPrimaryConstructor && private BoundExpression BindImplicitObjectCreationExpression(ImplicitObjectCreationExpressionSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureImplicitObjectCreation.CheckFeatureAvailability(diagnostics, node, node.NewKeyword.GetLocation()); + MessageID.IDS_FeatureImplicitObjectCreation.CheckFeatureAvailability(diagnostics, node.NewKeyword); var arguments = AnalyzedArguments.GetInstance(); BindArgumentsAndNames(node.ArgumentList, diagnostics, arguments, allowArglist: true); @@ -4826,7 +4885,7 @@ private BoundObjectInitializerExpression BindObjectInitializerExpression( Debug.Assert((object)initializerType != null); if (initializerSyntax.Kind() == SyntaxKind.ObjectInitializerExpression) - MessageID.IDS_FeatureObjectInitializer.CheckFeatureAvailability(diagnostics, initializerSyntax, initializerSyntax.OpenBraceToken.GetLocation()); + MessageID.IDS_FeatureObjectInitializer.CheckFeatureAvailability(diagnostics, initializerSyntax.OpenBraceToken); // We use a location specific binder for binding object initializer field/property access to generate object initializer specific diagnostics: // 1) CS1914 (ERR_StaticMemberInObjectInitializer) @@ -4972,8 +5031,7 @@ private BoundExpression BindObjectInitializerMember( { var implicitIndexing = (ImplicitElementAccessSyntax)leftSyntax; - MessageID.IDS_FeatureDictionaryInitializer.CheckFeatureAvailability( - diagnostics, implicitIndexing, implicitIndexing.ArgumentList.OpenBracketToken.GetLocation()); + MessageID.IDS_FeatureDictionaryInitializer.CheckFeatureAvailability(diagnostics, implicitIndexing.ArgumentList.OpenBracketToken); boundMember = BindElementAccess(implicitIndexing, implicitReceiver, implicitIndexing.ArgumentList, diagnostics); @@ -5323,7 +5381,7 @@ private BoundCollectionInitializerExpression BindCollectionInitializerExpression Debug.Assert(initializerSyntax.Expressions.Any()); Debug.Assert((object)initializerType != null); - MessageID.IDS_FeatureCollectionInitializer.CheckFeatureAvailability(diagnostics, initializerSyntax, initializerSyntax.OpenBraceToken.GetLocation()); + MessageID.IDS_FeatureCollectionInitializer.CheckFeatureAvailability(diagnostics, initializerSyntax.OpenBraceToken); var initializerBuilder = ArrayBuilder.GetInstance(); @@ -6265,7 +6323,7 @@ private BoundLiteral BindLiteralConstant(LiteralExpressionSyntax node, BindingDi if (node.Token.Kind() is SyntaxKind.SingleLineRawStringLiteralToken or SyntaxKind.MultiLineRawStringLiteralToken) { - MessageID.IDS_FeatureRawStringLiterals.CheckFeatureAvailability(diagnostics, node, node.Location); + MessageID.IDS_FeatureRawStringLiterals.CheckFeatureAvailability(diagnostics, node); } return new BoundLiteral(node, cv, type); @@ -7715,10 +7773,17 @@ private bool CheckInstanceOrStatic( { if (instanceReceiver == true) { - ErrorCode errorCode = this.Flags.Includes(BinderFlags.ObjectInitializerMember) ? - ErrorCode.ERR_StaticMemberInObjectInitializer : - ErrorCode.ERR_ObjectProhibited; - Error(diagnostics, errorCode, node, symbol); + if (!IsInsideNameof) + { + ErrorCode errorCode = this.Flags.Includes(BinderFlags.ObjectInitializerMember) ? + ErrorCode.ERR_StaticMemberInObjectInitializer : + ErrorCode.ERR_ObjectProhibited; + Error(diagnostics, errorCode, node, symbol); + } + else if (CheckFeatureAvailability(node, MessageID.IDS_FeatureInstanceMemberInNameof, diagnostics)) + { + return false; + } resultKind = LookupResultKind.StaticInstanceMismatch; return true; } @@ -9335,7 +9400,7 @@ internal static bool ReportDelegateInvokeUseSiteDiagnostic(BindingDiagnosticBag private BoundConditionalAccess BindConditionalAccessExpression(ConditionalAccessExpressionSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureNullPropagatingOperator.CheckFeatureAvailability(diagnostics, node, node.OperatorToken.GetLocation()); + MessageID.IDS_FeatureNullPropagatingOperator.CheckFeatureAvailability(diagnostics, node.OperatorToken); BoundExpression receiver = BindConditionalAccessReceiver(node, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index ba818cfaa8f9a..b48887917afb4 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -1109,7 +1109,7 @@ private BoundCall BindInvocationExpressionContinued( bool hasBaseReceiver = receiver != null && receiver.Kind == BoundKind.BaseReference; ReportDiagnosticsIfObsolete(diagnostics, method, node, hasBaseReceiver); - ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, method, node.Location, isDelegateConversion: false); + ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, method, node, isDelegateConversion: false); // No use site errors, but there could be use site warnings. // If there are any use site warnings, they have already been reported by overload resolution. diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs index 5f818e6af0cb7..ed1cbf9c8f28f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs @@ -56,7 +56,7 @@ private UnboundLambda AnalyzeAnonymousFunction( if (syntax is LambdaExpressionSyntax lambdaSyntax) { - MessageID.IDS_FeatureLambda.CheckFeatureAvailability(diagnostics, syntax, lambdaSyntax.ArrowToken.GetLocation()); + MessageID.IDS_FeatureLambda.CheckFeatureAvailability(diagnostics, lambdaSyntax.ArrowToken); checkAttributes(syntax, lambdaSyntax.AttributeLists, diagnostics); } @@ -86,7 +86,7 @@ private UnboundLambda AnalyzeAnonymousFunction( // delegate (int x) { } // delegate { } var anon = (AnonymousMethodExpressionSyntax)syntax; - MessageID.IDS_FeatureAnonDelegates.CheckFeatureAvailability(diagnostics, anon, anon.DelegateKeyword.GetLocation()); + MessageID.IDS_FeatureAnonDelegates.CheckFeatureAvailability(diagnostics, anon.DelegateKeyword); hasSignature = anon.ParameterList != null; if (hasSignature) @@ -105,12 +105,12 @@ private UnboundLambda AnalyzeAnonymousFunction( { if (modifier.IsKind(SyntaxKind.AsyncKeyword)) { - MessageID.IDS_FeatureAsync.CheckFeatureAvailability(diagnostics, syntax, modifier.GetLocation()); + MessageID.IDS_FeatureAsync.CheckFeatureAvailability(diagnostics, modifier); isAsync = true; } else if (modifier.IsKind(SyntaxKind.StaticKeyword)) { - MessageID.IDS_FeatureStaticAnonymousFunction.CheckFeatureAvailability(diagnostics, syntax, modifier.GetLocation()); + MessageID.IDS_FeatureStaticAnonymousFunction.CheckFeatureAvailability(diagnostics, modifier); isStatic = true; } } @@ -155,7 +155,7 @@ private UnboundLambda AnalyzeAnonymousFunction( } else { - MessageID.IDS_FeatureLambdaOptionalParameters.CheckFeatureAvailability(diagnostics, syntax, p.Default.EqualsToken.GetLocation()); + MessageID.IDS_FeatureLambdaOptionalParameters.CheckFeatureAvailability(diagnostics, p.Default.EqualsToken); } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs index 6463199811307..d3cbe2574fbca 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Lookup.cs @@ -1304,7 +1304,7 @@ internal static ImmutableArray GetCandidateMembers(NamespaceOrTypeSymbol { return ImmutableArray.Empty; } - else if (nsOrType is SourceMemberContainerTypeSymbol { PrimaryConstructor: not null } sourceMemberContainerTypeSymbol) + else if (nsOrType is SourceMemberContainerTypeSymbol { HasPrimaryConstructor: true } sourceMemberContainerTypeSymbol) { return sourceMemberContainerTypeSymbol.GetCandidateMembersForLookup(name); } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index 1ac2909d7eec8..0653d993db96e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -2407,7 +2407,7 @@ private bool CheckConstraintLanguageVersionAndRuntimeSupportForOperator(SyntaxNo private BoundExpression BindSuppressNullableWarningExpression(PostfixUnaryExpressionSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureNullableReferenceTypes.CheckFeatureAvailability(diagnostics, node, node.OperatorToken.GetLocation()); + MessageID.IDS_FeatureNullableReferenceTypes.CheckFeatureAvailability(diagnostics, node.OperatorToken); var expr = BindExpression(node.Operand, diagnostics); switch (expr.Kind) @@ -4089,7 +4089,7 @@ private BoundExpression BindNullCoalescingOperator(BinaryExpressionSyntax node, private BoundExpression BindNullCoalescingAssignmentOperator(AssignmentExpressionSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureCoalesceAssignmentExpression.CheckFeatureAvailability(diagnostics, node, node.OperatorToken.GetLocation()); + MessageID.IDS_FeatureCoalesceAssignmentExpression.CheckFeatureAvailability(diagnostics, node.OperatorToken); BoundExpression leftOperand = BindValue(node.Left, diagnostics, BindValueKind.CompoundAssignment); ReportSuppressionIfNeeded(leftOperand, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index 9ffff3ffe995a..2e6332492a73e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -17,7 +17,7 @@ partial class Binder { private BoundExpression BindIsPatternExpression(IsPatternExpressionSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeaturePatternMatching.CheckFeatureAvailability(diagnostics, node, node.IsKeyword.GetLocation()); + MessageID.IDS_FeaturePatternMatching.CheckFeatureAvailability(diagnostics, node.IsKeyword); BoundExpression expression = BindRValueWithoutTargetType(node.Expression, diagnostics); bool hasErrors = IsOperandErrors(node, ref expression, diagnostics); @@ -144,7 +144,7 @@ private BoundExpression BindSwitchExpression(SwitchExpressionSyntax node, Bindin { RoslynDebug.Assert(node is not null); - MessageID.IDS_FeatureRecursivePatterns.CheckFeatureAvailability(diagnostics, node, node.SwitchKeyword.GetLocation()); + MessageID.IDS_FeatureRecursivePatterns.CheckFeatureAvailability(diagnostics, node.SwitchKeyword); Binder? switchBinder = this.GetBinder(node); RoslynDebug.Assert(switchBinder is { }); @@ -194,7 +194,7 @@ private BoundPattern BindParenthesizedPattern( BindingDiagnosticBag diagnostics, bool underIsPattern) { - MessageID.IDS_FeatureParenthesizedPattern.CheckFeatureAvailability(diagnostics, node, node.OpenParenToken.GetLocation()); + MessageID.IDS_FeatureParenthesizedPattern.CheckFeatureAvailability(diagnostics, node.OpenParenToken); return BindPattern(node.Pattern, inputType, permitDesignations, hasErrors, diagnostics, underIsPattern); } @@ -1088,7 +1088,7 @@ deconstructMethod is null && } else if (subPattern.ExpressionColon != null) { - MessageID.IDS_FeatureExtendedPropertyPatterns.CheckFeatureAvailability(diagnostics, subPattern, subPattern.ExpressionColon.ColonToken.GetLocation()); + MessageID.IDS_FeatureExtendedPropertyPatterns.CheckFeatureAvailability(diagnostics, subPattern.ExpressionColon.ColonToken); diagnostics.Add(ErrorCode.ERR_IdentifierExpected, subPattern.ExpressionColon.Expression.Location); } @@ -1466,7 +1466,7 @@ private ImmutableArray BindPropertyPatternClause( foreach (SubpatternSyntax p in node.Subpatterns) { if (p.ExpressionColon is ExpressionColonSyntax) - MessageID.IDS_FeatureExtendedPropertyPatterns.CheckFeatureAvailability(diagnostics, p, p.ExpressionColon.ColonToken.GetLocation()); + MessageID.IDS_FeatureExtendedPropertyPatterns.CheckFeatureAvailability(diagnostics, p.ExpressionColon.ColonToken); ExpressionSyntax? expr = p.ExpressionColon?.Expression; PatternSyntax pattern = p.Pattern; @@ -1626,7 +1626,7 @@ private BoundPattern BindRelationalPattern( bool hasErrors, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureRelationalPattern.CheckFeatureAvailability(diagnostics, node, node.OperatorToken.GetLocation()); + MessageID.IDS_FeatureRelationalPattern.CheckFeatureAvailability(diagnostics, node.OperatorToken); BoundExpression value = BindExpressionForPattern(inputType, node.Expression, ref hasErrors, diagnostics, out var constantValueOpt, out _, out Conversion patternConversion); ExpressionSyntax innerExpression = SkipParensAndNullSuppressions(node.Expression, diagnostics, ref hasErrors); @@ -1719,7 +1719,7 @@ private BoundPattern BindUnaryPattern( BindingDiagnosticBag diagnostics, bool underIsPattern) { - MessageID.IDS_FeatureNotPattern.CheckFeatureAvailability(diagnostics, node, node.OperatorToken.GetLocation()); + MessageID.IDS_FeatureNotPattern.CheckFeatureAvailability(diagnostics, node.OperatorToken); bool permitDesignations = underIsPattern; // prevent designators under 'not' except under an is-pattern var subPattern = BindPattern(node.Pattern, inputType, permitDesignations, hasErrors, diagnostics, underIsPattern); @@ -1736,7 +1736,7 @@ private BoundPattern BindBinaryPattern( bool isDisjunction = node.Kind() == SyntaxKind.OrPattern; if (isDisjunction) { - MessageID.IDS_FeatureOrPattern.CheckFeatureAvailability(diagnostics, node, node.OperatorToken.GetLocation()); + MessageID.IDS_FeatureOrPattern.CheckFeatureAvailability(diagnostics, node.OperatorToken); permitDesignations = false; // prevent designators under 'or' var left = BindPattern(node.Left, inputType, permitDesignations, hasErrors, diagnostics); @@ -1821,7 +1821,7 @@ static void collectCandidates(BoundPattern pat, ArrayBuilder candida } else { - MessageID.IDS_FeatureAndPattern.CheckFeatureAvailability(diagnostics, node, node.OperatorToken.GetLocation()); + MessageID.IDS_FeatureAndPattern.CheckFeatureAvailability(diagnostics, node.OperatorToken); var left = BindPattern(node.Left, inputType, permitDesignations, hasErrors, diagnostics); var right = BindPattern(node.Right, left.NarrowedType, permitDesignations, hasErrors, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs index be3bac2451952..8cb074eb80fd3 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs @@ -23,7 +23,7 @@ internal partial class Binder internal BoundExpression BindQuery(QueryExpressionSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureQueryExpression.CheckFeatureAvailability(diagnostics, node, node.FromClause.FromKeyword.GetLocation()); + MessageID.IDS_FeatureQueryExpression.CheckFeatureAvailability(diagnostics, node.FromClause.FromKeyword); var fromClause = node.FromClause; var boundFromExpression = BindLeftOfPotentialColorColorMemberAccess(fromClause.Expression, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 0c8f9d6d6b53f..ddc29ce3202bb 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -215,7 +215,7 @@ private BoundStatement BindFixedStatementParts(FixedStatementSyntax node, Bindin private void CheckRequiredLangVersionForIteratorMethods(YieldStatementSyntax statement, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureIterators.CheckFeatureAvailability(diagnostics, statement, statement.YieldKeyword.GetLocation()); + MessageID.IDS_FeatureIterators.CheckFeatureAvailability(diagnostics, statement.YieldKeyword); var method = (MethodSymbol)this.ContainingMemberOrLambda; if (method.IsAsync) @@ -550,7 +550,7 @@ private BoundStatement BindGoto(GotoStatementSyntax node, BindingDiagnosticBag d private BoundStatement BindLocalFunctionStatement(LocalFunctionStatementSyntax node, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureLocalFunctions.CheckFeatureAvailability(diagnostics, node, node.Identifier.GetLocation()); + MessageID.IDS_FeatureLocalFunctions.CheckFeatureAvailability(diagnostics, node.Identifier); // already defined symbol in containing block var localSymbol = this.LookupLocalFunction(node.Identifier); @@ -595,9 +595,9 @@ private BoundStatement BindLocalFunctionStatement(LocalFunctionStatementSyntax n foreach (var modifier in node.Modifiers) { if (modifier.IsKind(SyntaxKind.StaticKeyword)) - MessageID.IDS_FeatureStaticLocalFunctions.CheckFeatureAvailability(diagnostics, node, modifier.GetLocation()); + MessageID.IDS_FeatureStaticLocalFunctions.CheckFeatureAvailability(diagnostics, modifier); else if (modifier.IsKind(SyntaxKind.ExternKeyword)) - MessageID.IDS_FeatureExternLocalFunctions.CheckFeatureAvailability(diagnostics, node, modifier.GetLocation()); + MessageID.IDS_FeatureExternLocalFunctions.CheckFeatureAvailability(diagnostics, modifier); } return new BoundLocalFunctionStatement(node, localSymbol, blockBody, expressionBody, hasErrors); @@ -1426,7 +1426,7 @@ private BoundExpression BindAssignment(AssignmentExpressionSyntax node, BindingD var lhsKind = isRef ? BindValueKind.RefAssignable : BindValueKind.Assignable; if (isRef) - MessageID.IDS_FeatureRefReassignment.CheckFeatureAvailability(diagnostics, node.Right, node.Right.GetFirstToken().GetLocation()); + MessageID.IDS_FeatureRefReassignment.CheckFeatureAvailability(diagnostics, node.Right.GetFirstToken()); var op1 = BindValue(node.Left, diagnostics, lhsKind); ReportSuppressionIfNeeded(op1, diagnostics); @@ -3247,7 +3247,7 @@ private BoundCatchBlock BindCatchBlock(CatchClauseSyntax node, ArrayBuilder throw ExceptionUtilities.UnexpectedValue(expressionBody.Parent.Kind()), }; - messageId?.CheckFeatureAvailability(diagnostics, expressionBody, expressionBody.ArrowToken.GetLocation()); + messageId?.CheckFeatureAvailability(diagnostics, expressionBody.ArrowToken); Binder bodyBinder = this.GetBinder(expressionBody); Debug.Assert(bodyBinder != null); @@ -3652,7 +3652,7 @@ private BoundNode BindConstructorBody(ConstructorDeclarationSyntax constructor, constructor.Body == null ? diagnostics : BindingDiagnosticBag.Discarded)); bool hasPrimaryConstructor() => - ContainingType is SourceMemberContainerTypeSymbol { PrimaryConstructor: not null }; + ContainingType is SourceMemberContainerTypeSymbol { HasPrimaryConstructor: true }; bool isInstanceConstructor(out MethodSymbol constructorSymbol) { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index fb06a4e052467..8f668fc7684e4 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -444,7 +444,7 @@ internal NamespaceOrTypeOrAliasSymbolWithAnnotations BindNamespaceOrTypeOrAliasS case SyntaxKind.FunctionPointerType: var functionPointerTypeSyntax = (FunctionPointerTypeSyntax)syntax; - MessageID.IDS_FeatureFunctionPointers.CheckFeatureAvailability(diagnostics, syntax, functionPointerTypeSyntax.DelegateKeyword.GetLocation()); + MessageID.IDS_FeatureFunctionPointers.CheckFeatureAvailability(diagnostics, functionPointerTypeSyntax.DelegateKeyword); if (GetUnsafeDiagnosticInfo(sizeOfTypeOpt: null) is CSDiagnosticInfo info) { @@ -535,7 +535,7 @@ void reportNullableReferenceTypesIfNeeded(SyntaxToken questionToken, TypeWithAnn NamespaceOrTypeOrAliasSymbolWithAnnotations bindNullable() { var nullableSyntax = (NullableTypeSyntax)syntax; - MessageID.IDS_FeatureNullable.CheckFeatureAvailability(diagnostics, nullableSyntax, nullableSyntax.QuestionToken.GetLocation()); + MessageID.IDS_FeatureNullable.CheckFeatureAvailability(diagnostics, nullableSyntax.QuestionToken); TypeSyntax typeArgumentSyntax = nullableSyntax.ElementType; TypeWithAnnotations typeArgument = BindType(typeArgumentSyntax, diagnostics, basesBeingResolved); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_WithExpression.cs b/src/Compilers/CSharp/Portable/Binder/Binder_WithExpression.cs index 9bb42e8b61beb..1ff2935f76237 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_WithExpression.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_WithExpression.cs @@ -14,7 +14,7 @@ internal partial class Binder { private BoundExpression BindWithExpression(WithExpressionSyntax syntax, BindingDiagnosticBag diagnostics) { - MessageID.IDS_FeatureRecords.CheckFeatureAvailability(diagnostics, syntax, syntax.WithKeyword.GetLocation()); + MessageID.IDS_FeatureRecords.CheckFeatureAvailability(diagnostics, syntax.WithKeyword); var receiver = BindRValueWithoutTargetType(syntax.Expression, diagnostics); var receiverType = receiver.Type; diff --git a/src/Compilers/CSharp/Portable/Binder/BindingDiagnosticBag.cs b/src/Compilers/CSharp/Portable/Binder/BindingDiagnosticBag.cs index b1aab32456052..d69391916db66 100644 --- a/src/Compilers/CSharp/Portable/Binder/BindingDiagnosticBag.cs +++ b/src/Compilers/CSharp/Portable/Binder/BindingDiagnosticBag.cs @@ -160,6 +160,12 @@ internal CSDiagnosticInfo Add(ErrorCode code, Location location) return info; } + internal CSDiagnosticInfo Add(ErrorCode code, SyntaxNode syntax, params object[] args) + => Add(code, syntax.Location, args); + + internal CSDiagnosticInfo Add(ErrorCode code, SyntaxToken syntax, params object[] args) + => Add(code, syntax.GetLocation()!, args); + internal CSDiagnosticInfo Add(ErrorCode code, Location location, params object[] args) { var info = new CSDiagnosticInfo(code, args); diff --git a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs index 4a7f3b64ae7a4..a3e4566b820a9 100644 --- a/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs +++ b/src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs @@ -12,6 +12,7 @@ using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Collections; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp @@ -38,7 +39,7 @@ namespace Microsoft.CodeAnalysis.CSharp /// /// /// In order to build this automaton, we start (in - /// + /// /// by computing a description of the initial state in a , and then /// for each such state description we decide what the test or evaluation will be at /// that state, and compute the successor state descriptions. @@ -132,7 +133,11 @@ private BoundDecisionDag CreateDecisionDagForIsPattern( LabelSymbol whenTrueLabel) { var rootIdentifier = BoundDagTemp.ForOriginalInput(inputExpression); - return MakeBoundDecisionDag(syntax, ImmutableArray.Create(MakeTestsForPattern(index: 1, pattern.Syntax, rootIdentifier, pattern, whenClause: null, whenTrueLabel))); + var builder = ArrayBuilder.GetInstance(1); + builder.Add(MakeTestsForPattern(index: 1, pattern.Syntax, rootIdentifier, pattern, whenClause: null, whenTrueLabel)); + var result = MakeBoundDecisionDag(syntax, builder); + builder.Free(); + return result; } private BoundDecisionDag CreateDecisionDagForSwitchStatement( @@ -154,7 +159,9 @@ private BoundDecisionDag CreateDecisionDagForSwitchStatement( } } - return MakeBoundDecisionDag(syntax, builder.ToImmutableAndFree()); + var result = MakeBoundDecisionDag(syntax, builder); + builder.Free(); + return result; } /// @@ -171,7 +178,9 @@ private BoundDecisionDag CreateDecisionDagForSwitchExpression( foreach (BoundSwitchExpressionArm arm in switchArms) builder.Add(MakeTestsForPattern(++i, arm.Syntax, rootIdentifier, arm.Pattern, arm.WhenClause, arm.Label)); - return MakeBoundDecisionDag(syntax, builder.ToImmutableAndFree()); + var result = MakeBoundDecisionDag(syntax, builder); + builder.Free(); + return result; } /// @@ -704,10 +713,15 @@ private TypeSymbol ErrorType(string name = "") /// decision when no decision appears to match. This implementation is nonrecursive to avoid /// overflowing the compiler's evaluation stack when compiling a large switch statement. /// - private BoundDecisionDag MakeBoundDecisionDag(SyntaxNode syntax, ImmutableArray cases) + private BoundDecisionDag MakeBoundDecisionDag(SyntaxNode syntax, ArrayBuilder cases) { + // A work list of DagStates whose successors need to be computed + var workList = ArrayBuilder.GetInstance(); + // Build the state machine underlying the decision dag - DecisionDag decisionDag = MakeDecisionDag(cases); + DecisionDag decisionDag = MakeDecisionDag(cases, workList); + + workList.Free(); // Note: It is useful for debugging the dag state table construction to set a breakpoint // here and view `decisionDag.Dump()`. @@ -767,11 +781,10 @@ int tempIdentifier(BoundDagEvaluation e) /// Make a (state machine) starting with the given set of cases in the root node, /// and return the node for the root. /// - private DecisionDag MakeDecisionDag(ImmutableArray casesForRootNode) + private DecisionDag MakeDecisionDag( + ArrayBuilder casesForRootNode, + ArrayBuilder workList) { - // A work list of DagStates whose successors need to be computed - var workList = ArrayBuilder.GetInstance(); - // A mapping used to make each DagState unique (i.e. to de-dup identical states). var uniqueState = new Dictionary(DagStateEquivalence.Instance); @@ -781,7 +794,7 @@ private DecisionDag MakeDecisionDag(ImmutableArray casesForRootNod // so that it is processed only once. This object identity uniqueness will be important later when we // start mutating the DagState nodes to compute successors and BoundDecisionDagNodes // for each one. That is why we have to use an equivalence relation in the dictionary `uniqueState`. - DagState uniqifyState(ImmutableArray cases, ImmutableDictionary remainingValues) + DagState uniquifyState(ImmutableArray cases, ImmutableDictionary remainingValues) { var state = new DagState(cases, remainingValues); if (uniqueState.TryGetValue(state, out DagState? existingState)) @@ -821,7 +834,7 @@ DagState uniqifyState(ImmutableArray cases, ImmutableDictionary.GetInstance(casesForRootNode.Length); + var rewrittenCases = ArrayBuilder.GetInstance(casesForRootNode.Count); foreach (var state in casesForRootNode) { var rewrittenCase = state.RewriteNestedLengthTests(); @@ -832,7 +845,7 @@ DagState uniqifyState(ImmutableArray cases, ImmutableDictionary.Empty); + var initialState = uniquifyState(rewrittenCases.ToImmutableAndFree(), ImmutableDictionary.Empty); // Go through the worklist of DagState nodes for which we have not yet computed // successor states. @@ -867,7 +880,7 @@ DagState uniqifyState(ImmutableArray cases, ImmutableDictionary cases, ImmutableDictionary cases, ImmutableDictionary whenTrueValues, out ImmutableDictionary whenFalseValues, ref foundExplicitNullTest); - state.TrueBranch = uniqifyState(whenTrueDecisions, whenTrueValues); - state.FalseBranch = uniqifyState(whenFalseDecisions, whenFalseValues); + state.TrueBranch = uniquifyState(whenTrueDecisions, whenTrueValues); + state.FalseBranch = uniquifyState(whenFalseDecisions, whenFalseValues); if (foundExplicitNullTest && d is BoundDagNonNullTest { IsExplicitTest: false } t) { // Turn an "implicit" non-null test into an explicit one @@ -914,7 +927,6 @@ DagState uniqifyState(ImmutableArray cases, ImmutableDictionary /// A successor function used to topologically sort the DagState set. /// - private static ImmutableArray Successor(DagState state) + private static void AddSuccessor(ref TemporaryArray builder, DagState state) { - if (state.TrueBranch != null && state.FalseBranch != null) - { - return ImmutableArray.Create(state.FalseBranch, state.TrueBranch); - } - else if (state.TrueBranch != null) - { - return ImmutableArray.Create(state.TrueBranch); - } - else if (state.FalseBranch != null) - { - return ImmutableArray.Create(state.FalseBranch); - } - else - { - return ImmutableArray.Empty; - } + builder.AddIfNotNull(state.TrueBranch); + builder.AddIfNotNull(state.FalseBranch); } /// @@ -1602,7 +1600,7 @@ private static ImmutableArray Successor(DagState state) /// True if the graph was acyclic. public bool TryGetTopologicallySortedReachableStates(out ImmutableArray result) { - return TopologicalSort.TryIterativeSort(SpecializedCollections.SingletonEnumerable(this.RootNode), Successor, out result); + return TopologicalSort.TryIterativeSort(this.RootNode, AddSuccessor, out result); } #if DEBUG @@ -1808,7 +1806,7 @@ public bool Equals(DagState? x, DagState? y) { RoslynDebug.Assert(x is { }); RoslynDebug.Assert(y is { }); - return x == y || x.Cases.SequenceEqual(y.Cases, (a, b) => a.Equals(b)); + return x == y || x.Cases.SequenceEqual(y.Cases, static (a, b) => a.Equals(b)); } public int GetHashCode(DagState x) @@ -1821,7 +1819,7 @@ public int GetHashCode(DagState x) /// As part of the description of a node of the decision automaton, we keep track of what tests /// remain to be done for each case. /// - private sealed class StateForCase + private readonly struct StateForCase { /// /// A number that is distinct for each case and monotonically increasing from earlier to later cases. @@ -1875,9 +1873,7 @@ public bool Equals(StateForCase other) { // We do not include Syntax, Bindings, WhereClause, or CaseLabel // because once the Index is the same, those must be the same too. - return this == other || - other != null && - this.Index == other.Index && + return this.Index == other.Index && this.RemainingTests.Equals(other.RemainingTests); } diff --git a/src/Compilers/CSharp/Portable/Binder/ExpressionVariableFinder.cs b/src/Compilers/CSharp/Portable/Binder/ExpressionVariableFinder.cs index ecb5ea0a48ea8..3b02eec015aab 100644 --- a/src/Compilers/CSharp/Portable/Binder/ExpressionVariableFinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ExpressionVariableFinder.cs @@ -649,7 +649,7 @@ protected override Symbol MakePatternVariable(TypeSyntax type, SingleVariableDes { return designation == null ? null : GlobalExpressionVariable.Create( _containingType, _modifiers, type, - designation.Identifier.ValueText, designation, designation.GetLocation(), + designation.Identifier.ValueText, designation, designation.Span, _containingFieldOpt, nodeToBind); } @@ -657,7 +657,7 @@ protected override Symbol MakeDeclarationExpressionVariable(DeclarationExpressio { return GlobalExpressionVariable.Create( _containingType, _modifiers, node.Type, - designation.Identifier.ValueText, designation, designation.Identifier.GetLocation(), + designation.Identifier.ValueText, designation, designation.Identifier.Span, _containingFieldOpt, nodeToBind); } @@ -672,7 +672,7 @@ protected override Symbol MakeDeconstructionVariable( typeSyntax: closestTypeSyntax, name: designation.Identifier.ValueText, syntax: designation, - location: designation.Location, + locationSpan: designation.Span, containingFieldOpt: null, nodeToBind: deconstruction); } diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index ef254301c1296..6c22b41537c6f 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -432,7 +432,7 @@ private BoundForEachStatement BindForEachPartsWorker(BindingDiagnosticBag diagno var foreachKeyword = _syntax.ForEachKeyword; ReportDiagnosticsIfObsolete(diagnostics, getEnumeratorMethod, foreachKeyword, hasBaseReceiver: false); - ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, getEnumeratorMethod, foreachKeyword.GetLocation(), isDelegateConversion: false); + ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, getEnumeratorMethod, foreachKeyword, isDelegateConversion: false); // MoveNext is an instance method, so it does not need to have unmanaged callers only diagnostics reported. // Either a diagnostic was reported at the declaration of the method (for the invalid attribute), or MoveNext // is marked as not supported and we won't get here in the first place (for metadata import). diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs index 1592f2bac88c3..6f62adbf611d9 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs @@ -25,6 +25,13 @@ namespace Microsoft.CodeAnalysis.CSharp // in uncommon cases an instance of this class is attached to the conversion. private class UncommonData { + public static readonly UncommonData NoApplicableOperators = new UncommonData( + isExtensionMethod: false, + isArrayIndex: false, + conversionResult: UserDefinedConversionResult.NoApplicableOperators(ImmutableArray.Empty), + conversionMethod: null, + nestedConversions: default); + public UncommonData( bool isExtensionMethod, bool isArrayIndex, @@ -107,12 +114,14 @@ internal Conversion(UserDefinedConversionResult conversionResult, bool isImplici ? ConversionKind.NoConversion : isImplicit ? ConversionKind.ImplicitUserDefined : ConversionKind.ExplicitUserDefined; - _uncommonData = new UncommonData( - isExtensionMethod: false, - isArrayIndex: false, - conversionResult: conversionResult, - conversionMethod: null, - nestedConversions: default); + _uncommonData = conversionResult.Kind == UserDefinedConversionResultKind.NoApplicableOperators && conversionResult.Results.IsEmpty + ? UncommonData.NoApplicableOperators + : new UncommonData( + isExtensionMethod: false, + isArrayIndex: false, + conversionResult: conversionResult, + conversionMethod: null, + nestedConversions: default); } // For the method group, lambda and anonymous method conversions diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs index b8599bddf7e3f..e5aba45435135 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/BinaryOperatorOverloadResolution.cs @@ -475,9 +475,8 @@ private void GetEnumOperation(BinaryOperatorKind kind, TypeSymbol enumType, Boun Debug.Assert((object)underlying != null); Debug.Assert(underlying.SpecialType != SpecialType.None); - var nullable = Compilation.GetSpecialType(SpecialType.System_Nullable_T); - var nullableEnum = nullable.Construct(enumType); - var nullableUnderlying = nullable.Construct(underlying); + var nullableEnum = MakeNullable(enumType); + var nullableUnderlying = MakeNullable(underlying); switch (kind) { diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs index f849ec6edd41b..4c6c2656b402f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Operators/UnaryOperatorOverloadResolution.cs @@ -17,7 +17,7 @@ internal sealed partial class OverloadResolution { private NamedTypeSymbol MakeNullable(TypeSymbol type) { - return Compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(type); + return Compilation.GetOrCreateNullableType(type); } public void UnaryOperatorOverloadResolution(UnaryOperatorKind kind, bool isChecked, BoundExpression operand, UnaryOperatorOverloadResolutionResult result, ref CompoundUseSiteInfo useSiteInfo) diff --git a/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs index a70f367e3446b..3dcf824ff86db 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchBinder_Patterns.cs @@ -267,7 +267,7 @@ private BoundSwitchLabel BindSwitchSectionLabel( { var matchLabelSyntax = (CasePatternSwitchLabelSyntax)node; - MessageID.IDS_FeaturePatternMatching.CheckFeatureAvailability(diagnostics, node, node.Keyword.GetLocation()); + MessageID.IDS_FeaturePatternMatching.CheckFeatureAvailability(diagnostics, node.Keyword); BoundPattern pattern = sectionBinder.BindPattern( matchLabelSyntax.Pattern, SwitchGoverningType, permitDesignations: true, node.HasErrors, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs b/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs index 463109a589e10..73ff46d298ef9 100644 --- a/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/SwitchExpressionBinder.cs @@ -8,6 +8,7 @@ using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Collections; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp @@ -84,7 +85,7 @@ private bool CheckSwitchExpressionExhaustive( // We only report exhaustive warnings when the default label is reachable through some series of // tests that do not include a test in which the value is known to be null. Handling paths with // nulls is the job of the nullable walker. - bool wasAcyclic = TopologicalSort.TryIterativeSort(SpecializedCollections.SingletonEnumerable(decisionDag.RootNode), nonNullSuccessors, out var nodes); + bool wasAcyclic = TopologicalSort.TryIterativeSort(decisionDag.RootNode, addNonNullSuccessors, out var nodes); // Since decisionDag.RootNode is acyclic by construction, its subset of nodes sorted here cannot be cyclic Debug.Assert(wasAcyclic); foreach (var n in nodes) @@ -107,7 +108,7 @@ private bool CheckSwitchExpressionExhaustive( return false; - ImmutableArray nonNullSuccessors(BoundDecisionDagNode n) + static void addNonNullSuccessors(ref TemporaryArray builder, BoundDecisionDagNode n) { switch (n) { @@ -115,14 +116,18 @@ ImmutableArray nonNullSuccessors(BoundDecisionDagNode n) switch (p.Test) { case BoundDagNonNullTest t: // checks that the input is not null - return ImmutableArray.Create(p.WhenTrue); + builder.Add(p.WhenTrue); + return; case BoundDagExplicitNullTest t: // checks that the input is null - return ImmutableArray.Create(p.WhenFalse); + builder.Add(p.WhenFalse); + return; default: - return BoundDecisionDag.Successors(n); + BoundDecisionDag.AddSuccessors(ref builder, n); + return; } default: - return BoundDecisionDag.Successors(n); + BoundDecisionDag.AddSuccessors(ref builder, n); + return; } } } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundDecisionDag.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundDecisionDag.cs index eec713ae6fbbc..1deac891fe1d9 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundDecisionDag.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundDecisionDag.cs @@ -11,6 +11,7 @@ using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Collections; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp @@ -20,18 +21,23 @@ internal partial class BoundDecisionDag private ImmutableHashSet _reachableLabels; private ImmutableArray _topologicallySortedNodes; - internal static ImmutableArray Successors(BoundDecisionDagNode node) + internal static void AddSuccessors(ref TemporaryArray builder, BoundDecisionDagNode node) { switch (node) { case BoundEvaluationDecisionDagNode p: - return ImmutableArray.Create(p.Next); + builder.Add(p.Next); + return; case BoundTestDecisionDagNode p: - return ImmutableArray.Create(p.WhenFalse, p.WhenTrue); + builder.Add(p.WhenFalse); + builder.Add(p.WhenTrue); + return; case BoundLeafDecisionDagNode d: - return ImmutableArray.Empty; + return; case BoundWhenDecisionDagNode w: - return (w.WhenFalse != null) ? ImmutableArray.Create(w.WhenTrue, w.WhenFalse) : ImmutableArray.Create(w.WhenTrue); + builder.Add(w.WhenTrue); + builder.AddIfNotNull(w.WhenFalse); + return; default: throw ExceptionUtilities.UnexpectedValue(node.Kind); } @@ -69,7 +75,7 @@ public ImmutableArray TopologicallySortedNodes if (_topologicallySortedNodes.IsDefault) { // We use an iterative topological sort to avoid overflowing the compiler's runtime stack for a large switch statement. - bool wasAcyclic = TopologicalSort.TryIterativeSort(SpecializedCollections.SingletonEnumerable(this.RootNode), Successors, out _topologicallySortedNodes); + bool wasAcyclic = TopologicalSort.TryIterativeSort(this.RootNode, AddSuccessors, out _topologicallySortedNodes); // Since these nodes were constructed by an isomorphic mapping from a known acyclic graph, it cannot be cyclic Debug.Assert(wasAcyclic); diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index b36398e1d8b8e..d0db9d09556ab 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -5074,9 +5074,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Cannot return '{0}' by reference because it is a '{1}' - - Cannot return fields of '{0}' by reference because it is a '{1}' - A readonly field cannot be returned by writable reference @@ -6817,6 +6814,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Parameter is unread. Did you forget to use it to initialize the property with that name? + + instance member in 'nameof' + The primary constructor conflicts with the synthesized copy constructor. @@ -7508,4 +7508,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ A constant value of type '{0}' is expected + + Cannot use primary constructor parameter of type '{0}' inside an instance member + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Compilation/BuiltInOperators.cs b/src/Compilers/CSharp/Portable/Compilation/BuiltInOperators.cs index 8b4d330e83a70..0cc6a7bffb1f9 100644 --- a/src/Compilers/CSharp/Portable/Compilation/BuiltInOperators.cs +++ b/src/Compilers/CSharp/Portable/Compilation/BuiltInOperators.cs @@ -281,7 +281,7 @@ internal UnaryOperatorSignature GetSignature(UnaryOperatorKind kind) if (kind.IsLifted()) { - opType = _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(opType); + opType = _compilation.GetOrCreateNullableType(opType); } return new UnaryOperatorSignature(kind, opType, opType); @@ -723,7 +723,7 @@ internal BinaryOperatorSignature GetSignature(BinaryOperatorKind kind) TypeSymbol rightType = _compilation.GetSpecialType(SpecialType.System_Int32); if (kind.IsLifted()) { - rightType = _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(rightType); + rightType = _compilation.GetOrCreateNullableType(rightType); } return new BinaryOperatorSignature(kind, left, rightType, left); @@ -837,23 +837,22 @@ private TypeSymbol LiftedType(BinaryOperatorKind kind) { Debug.Assert(kind.IsLifted()); - var nullable = _compilation.GetSpecialType(SpecialType.System_Nullable_T); - - switch (kind.OperandTypes()) + var typeArgument = kind.OperandTypes() switch { - case BinaryOperatorKind.Int: return nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Int32)); - case BinaryOperatorKind.UInt: return nullable.Construct(_compilation.GetSpecialType(SpecialType.System_UInt32)); - case BinaryOperatorKind.Long: return nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Int64)); - case BinaryOperatorKind.ULong: return nullable.Construct(_compilation.GetSpecialType(SpecialType.System_UInt64)); - case BinaryOperatorKind.NInt: return nullable.Construct(_compilation.CreateNativeIntegerTypeSymbol(signed: true)); - case BinaryOperatorKind.NUInt: return nullable.Construct(_compilation.CreateNativeIntegerTypeSymbol(signed: false)); - case BinaryOperatorKind.Float: return nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Single)); - case BinaryOperatorKind.Double: return nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Double)); - case BinaryOperatorKind.Decimal: return nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Decimal)); - case BinaryOperatorKind.Bool: return nullable.Construct(_compilation.GetSpecialType(SpecialType.System_Boolean)); - } - Debug.Assert(false, "Bad operator kind in lifted type"); - return null; + BinaryOperatorKind.Int => _compilation.GetSpecialType(SpecialType.System_Int32), + BinaryOperatorKind.UInt => _compilation.GetSpecialType(SpecialType.System_UInt32), + BinaryOperatorKind.Long => _compilation.GetSpecialType(SpecialType.System_Int64), + BinaryOperatorKind.ULong => _compilation.GetSpecialType(SpecialType.System_UInt64), + BinaryOperatorKind.NInt => _compilation.CreateNativeIntegerTypeSymbol(signed: true), + BinaryOperatorKind.NUInt => _compilation.CreateNativeIntegerTypeSymbol(signed: false), + BinaryOperatorKind.Float => _compilation.GetSpecialType(SpecialType.System_Single), + BinaryOperatorKind.Double => _compilation.GetSpecialType(SpecialType.System_Double), + BinaryOperatorKind.Decimal => _compilation.GetSpecialType(SpecialType.System_Decimal), + BinaryOperatorKind.Bool => _compilation.GetSpecialType(SpecialType.System_Boolean), + var v => throw ExceptionUtilities.UnexpectedValue(v), + }; + + return _compilation.GetOrCreateNullableType(typeArgument); } internal static bool IsValidObjectEquality(Conversions Conversions, TypeSymbol leftType, bool leftIsNull, bool leftIsDefault, TypeSymbol rightType, bool rightIsNull, bool rightIsDefault, ref CompoundUseSiteInfo useSiteInfo) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index 078a52970ea02..d7eeb560d938f 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -16,6 +16,7 @@ using Microsoft.Cci; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeGen; +using Microsoft.CodeAnalysis.Collections; using Microsoft.CodeAnalysis.CSharp.Emit; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -1565,6 +1566,18 @@ internal AliasSymbol GlobalNamespaceAlias return result; } + /// + /// Cache of T to Nullable<T>. + /// + private ImmutableSegmentedDictionary _typeToNullableVersion = ImmutableSegmentedDictionary.Empty; + + internal NamedTypeSymbol GetOrCreateNullableType(TypeSymbol typeArgument) + => RoslynImmutableInterlocked.GetOrAdd( + ref _typeToNullableVersion, + typeArgument, + static (typeArgument, @this) => @this.GetSpecialType(SpecialType.System_Nullable_T).Construct(typeArgument), + this); + /// /// Get the symbol for the predefined type member from the COR Library referenced by this compilation. /// @@ -4744,7 +4757,7 @@ protected override bool ShouldCheckTypeForMembers(MergedTypeDeclaration current) { foreach (SingleTypeDeclaration typeDecl in current.Declarations) { - if (typeDecl.MemberNames.ContainsKey(_name)) + if (typeDecl.MemberNames.Contains(_name)) { return true; } diff --git a/src/Compilers/CSharp/Portable/Declarations/DeclarationTable.cs b/src/Compilers/CSharp/Portable/Declarations/DeclarationTable.cs index a811203ee22bf..dd991cf527d7d 100644 --- a/src/Compilers/CSharp/Portable/Declarations/DeclarationTable.cs +++ b/src/Compilers/CSharp/Portable/Declarations/DeclarationTable.cs @@ -281,7 +281,7 @@ public static bool ContainsName( mergedRoot, n => n == name, filter, - t => t.MemberNames.ContainsKey(name), + t => t.MemberNames.Contains(name), cancellationToken); } @@ -295,7 +295,7 @@ public static bool ContainsName( mergedRoot, predicate, filter, t => { - foreach (var (name, _) in t.MemberNames) + foreach (var name in t.MemberNames) { if (predicate(name)) { diff --git a/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs b/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs index 083f88688a0f8..dcffaa303c257 100644 --- a/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs +++ b/src/Compilers/CSharp/Portable/Declarations/DeclarationTreeBuilder.cs @@ -138,7 +138,7 @@ private ImmutableArray VisitNamespaceChildren( return childrenBuilder.ToImmutableAndFree(); } - private static SingleNamespaceOrTypeDeclaration CreateImplicitClass(ImmutableSegmentedDictionary memberNames, SyntaxReference container, SingleTypeDeclaration.TypeDeclarationFlags declFlags) + private static SingleNamespaceOrTypeDeclaration CreateImplicitClass(ImmutableSegmentedHashSet memberNames, SyntaxReference container, SingleTypeDeclaration.TypeDeclarationFlags declFlags) { return new SingleTypeDeclaration( kind: DeclarationKind.ImplicitClass, @@ -167,7 +167,7 @@ private static SingleNamespaceOrTypeDeclaration CreateSimpleProgram(GlobalStatem SingleTypeDeclaration.TypeDeclarationFlags.IsSimpleProgram, syntaxReference: firstGlobalStatement.SyntaxTree.GetReference(firstGlobalStatement.Parent), nameLocation: new SourceLocation(firstGlobalStatement.GetFirstToken()), - memberNames: ImmutableSegmentedDictionary.Empty, + memberNames: ImmutableSegmentedHashSet.Empty, children: ImmutableArray.Empty, diagnostics: diagnostics, quickAttributes: QuickAttributes.None); @@ -236,7 +236,7 @@ private static ImmutableArray GetReferenceDirectives(Compila private SingleNamespaceOrTypeDeclaration CreateScriptClass( CompilationUnitSyntax parent, ImmutableArray children, - ImmutableSegmentedDictionary memberNames, + ImmutableSegmentedHashSet memberNames, SingleTypeDeclaration.TypeDeclarationFlags declFlags) { Debug.Assert(parent.Kind() == SyntaxKind.CompilationUnit && _syntaxTree.Options.Kind != SourceCodeKind.Regular); @@ -613,8 +613,7 @@ private SingleNamespaceOrTypeDeclaration VisitTypeDeclaration(TypeDeclarationSyn Symbol.ReportErrorIfHasConstraints(node.ConstraintClauses, diagnostics); } - // A type with parameters at least has a primary constructor - var hasPrimaryCtor = node.ParameterList != null; + var hasPrimaryCtor = node.ParameterList != null && node is RecordDeclarationSyntax or ClassDeclarationSyntax or StructDeclarationSyntax; if (hasPrimaryCtor) { declFlags |= SingleTypeDeclaration.TypeDeclarationFlags.HasAnyNontypeMembers; @@ -729,7 +728,7 @@ public override SingleNamespaceOrTypeDeclaration VisitDelegateDeclaration(Delega declFlags: declFlags, syntaxReference: _syntaxTree.GetReference(node), nameLocation: new SourceLocation(node.Identifier), - memberNames: ImmutableSegmentedDictionary.Empty, + memberNames: ImmutableSegmentedHashSet.Empty, children: ImmutableArray.Empty, diagnostics: diagnostics.ToReadOnlyAndFree(), _nonGlobalAliasedQuickAttributes | quickAttributes); @@ -748,7 +747,7 @@ public override SingleNamespaceOrTypeDeclaration VisitEnumDeclaration(EnumDeclar declFlags |= SingleTypeDeclaration.TypeDeclarationFlags.HasBaseDeclarations; } - ImmutableSegmentedDictionary memberNames = GetEnumMemberNames(members, ref declFlags); + ImmutableSegmentedHashSet memberNames = GetEnumMemberNames(members, ref declFlags); var diagnostics = DiagnosticBag.GetInstance(); var modifiers = node.Modifiers.ToDeclarationModifiers(isForTypeDeclaration: true, diagnostics: diagnostics); @@ -787,10 +786,10 @@ private static QuickAttributes GetQuickAttributes(SyntaxList.Builder> s_memberNameBuilderPool = - new ObjectPool.Builder>(() => ImmutableSegmentedDictionary.CreateBuilder()); + private static readonly ObjectPool.Builder> s_memberNameBuilderPool = + new ObjectPool.Builder>(() => ImmutableSegmentedHashSet.CreateBuilder()); - private static ImmutableSegmentedDictionary ToImmutableAndFree(ImmutableSegmentedDictionary.Builder builder) + private static ImmutableSegmentedHashSet ToImmutableAndFree(ImmutableSegmentedHashSet.Builder builder) { var result = builder.ToImmutable(); builder.Clear(); @@ -798,7 +797,7 @@ private static ImmutableSegmentedDictionary ToImmutableAndFr return result; } - private static ImmutableSegmentedDictionary GetEnumMemberNames(SeparatedSyntaxList members, ref SingleTypeDeclaration.TypeDeclarationFlags declFlags) + private static ImmutableSegmentedHashSet GetEnumMemberNames(SeparatedSyntaxList members, ref SingleTypeDeclaration.TypeDeclarationFlags declFlags) { var cnt = members.Count; @@ -811,7 +810,7 @@ private static ImmutableSegmentedDictionary GetEnumMemberNam bool anyMemberHasAttributes = false; foreach (var member in members) { - memberNamesBuilder.TryAdd(member.Identifier.ValueText); + memberNamesBuilder.Add(member.Identifier.ValueText); if (!anyMemberHasAttributes && member.AttributeLists.Any()) { anyMemberHasAttributes = true; @@ -826,7 +825,7 @@ private static ImmutableSegmentedDictionary GetEnumMemberNam return ToImmutableAndFree(memberNamesBuilder); } - private static ImmutableSegmentedDictionary GetNonTypeMemberNames( + private static ImmutableSegmentedHashSet GetNonTypeMemberNames( CoreInternalSyntax.SyntaxList members, ref SingleTypeDeclaration.TypeDeclarationFlags declFlags, bool skipGlobalStatements = false, bool hasPrimaryCtor = false) { bool anyMethodHadExtensionSyntax = false; @@ -838,7 +837,7 @@ private static ImmutableSegmentedDictionary GetNonTypeMember if (hasPrimaryCtor) { - memberNameBuilder.TryAdd(WellKnownMemberNames.InstanceConstructorName); + memberNameBuilder.Add(WellKnownMemberNames.InstanceConstructorName); } foreach (var member in members) @@ -976,7 +975,7 @@ private static bool CheckMemberForAttributes(Syntax.InternalSyntax.CSharpSyntaxN } private static void AddNonTypeMemberNames( - Syntax.InternalSyntax.CSharpSyntaxNode member, ImmutableSegmentedDictionary.Builder set, ref bool anyNonTypeMembers, bool skipGlobalStatements) + Syntax.InternalSyntax.CSharpSyntaxNode member, ImmutableSegmentedHashSet.Builder set, ref bool anyNonTypeMembers, bool skipGlobalStatements) { switch (member.Kind) { @@ -987,7 +986,7 @@ private static void AddNonTypeMemberNames( int numFieldDeclarators = fieldDeclarators.Count; for (int i = 0; i < numFieldDeclarators; i++) { - set.TryAdd(fieldDeclarators[i].Identifier.ValueText); + set.Add(fieldDeclarators[i].Identifier.ValueText); } break; @@ -998,7 +997,7 @@ private static void AddNonTypeMemberNames( int numEventDeclarators = eventDeclarators.Count; for (int i = 0; i < numEventDeclarators; i++) { - set.TryAdd(eventDeclarators[i].Identifier.ValueText); + set.Add(eventDeclarators[i].Identifier.ValueText); } break; @@ -1011,7 +1010,7 @@ private static void AddNonTypeMemberNames( var methodDecl = (Syntax.InternalSyntax.MethodDeclarationSyntax)member; if (methodDecl.ExplicitInterfaceSpecifier == null) { - set.TryAdd(methodDecl.Identifier.ValueText); + set.Add(methodDecl.Identifier.ValueText); } break; @@ -1021,7 +1020,7 @@ private static void AddNonTypeMemberNames( var propertyDecl = (Syntax.InternalSyntax.PropertyDeclarationSyntax)member; if (propertyDecl.ExplicitInterfaceSpecifier == null) { - set.TryAdd(propertyDecl.Identifier.ValueText); + set.Add(propertyDecl.Identifier.ValueText); } break; @@ -1031,25 +1030,25 @@ private static void AddNonTypeMemberNames( var eventDecl = (Syntax.InternalSyntax.EventDeclarationSyntax)member; if (eventDecl.ExplicitInterfaceSpecifier == null) { - set.TryAdd(eventDecl.Identifier.ValueText); + set.Add(eventDecl.Identifier.ValueText); } break; case SyntaxKind.ConstructorDeclaration: anyNonTypeMembers = true; - set.TryAdd(((Syntax.InternalSyntax.ConstructorDeclarationSyntax)member).Modifiers.Any((int)SyntaxKind.StaticKeyword) + set.Add(((Syntax.InternalSyntax.ConstructorDeclarationSyntax)member).Modifiers.Any((int)SyntaxKind.StaticKeyword) ? WellKnownMemberNames.StaticConstructorName : WellKnownMemberNames.InstanceConstructorName); break; case SyntaxKind.DestructorDeclaration: anyNonTypeMembers = true; - set.TryAdd(WellKnownMemberNames.DestructorName); + set.Add(WellKnownMemberNames.DestructorName); break; case SyntaxKind.IndexerDeclaration: anyNonTypeMembers = true; - set.TryAdd(WellKnownMemberNames.Indexer); + set.Add(WellKnownMemberNames.Indexer); break; case SyntaxKind.OperatorDeclaration: @@ -1062,7 +1061,7 @@ private static void AddNonTypeMemberNames( if (opDecl.ExplicitInterfaceSpecifier == null) { var name = OperatorFacts.OperatorNameFromDeclaration(opDecl); - set.TryAdd(name); + set.Add(name); } } break; @@ -1077,7 +1076,7 @@ private static void AddNonTypeMemberNames( if (opDecl.ExplicitInterfaceSpecifier == null) { var name = OperatorFacts.OperatorNameFromDeclaration(opDecl); - set.TryAdd(name); + set.Add(name); } } break; diff --git a/src/Compilers/CSharp/Portable/Declarations/MergedTypeDeclaration.cs b/src/Compilers/CSharp/Portable/Declarations/MergedTypeDeclaration.cs index 966cbae8d051f..e55a01689cfc3 100644 --- a/src/Compilers/CSharp/Portable/Declarations/MergedTypeDeclaration.cs +++ b/src/Compilers/CSharp/Portable/Declarations/MergedTypeDeclaration.cs @@ -243,7 +243,7 @@ public ICollection MemberNames { if (_lazyMemberNames == null) { - var names = UnionCollection.Create(this.Declarations, d => d.MemberNames.Keys); + var names = UnionCollection.Create(this.Declarations, d => d.MemberNames); Interlocked.CompareExchange(ref _lazyMemberNames, names, null); } diff --git a/src/Compilers/CSharp/Portable/Declarations/SingleTypeDeclaration.cs b/src/Compilers/CSharp/Portable/Declarations/SingleTypeDeclaration.cs index 1110313132338..3e3cb7f5c9ffa 100644 --- a/src/Compilers/CSharp/Portable/Declarations/SingleTypeDeclaration.cs +++ b/src/Compilers/CSharp/Portable/Declarations/SingleTypeDeclaration.cs @@ -70,7 +70,7 @@ internal SingleTypeDeclaration( TypeDeclarationFlags declFlags, SyntaxReference syntaxReference, SourceLocation nameLocation, - ImmutableSegmentedDictionary memberNames, + ImmutableSegmentedHashSet memberNames, ImmutableArray children, ImmutableArray diagnostics, QuickAttributes quickAttributes) @@ -119,7 +119,7 @@ public DeclarationModifiers Modifiers } } - public ImmutableSegmentedDictionary MemberNames { get; } + public ImmutableSegmentedHashSet MemberNames { get; } public bool AnyMemberHasExtensionMethodSyntax { @@ -245,7 +245,7 @@ public bool Equals(TypeDeclarationIdentity other) return false; } - if ((object)thisDecl.Location.SourceTree != otherDecl.Location.SourceTree + if ((object)thisDecl.SyntaxReference.SyntaxTree != otherDecl.SyntaxReference.SyntaxTree && ((thisDecl.Modifiers & DeclarationModifiers.File) != 0 || (otherDecl.Modifiers & DeclarationModifiers.File) != 0)) { diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 8423565e79902..ca89dd2af9635 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2190,6 +2190,7 @@ internal enum ErrorCode ERR_BadCaseInSwitchArm = 9134, ERR_ConstantValueOfTypeExpected = 9135, + ERR_UnsupportedPrimaryConstructorParameterCapturingRefAny = 9136, #endregion diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 3052e5e6cf4f6..beaf7cfeab2db 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -2310,6 +2310,7 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.ERR_BadStaticAfterUnsafe: case ErrorCode.ERR_BadCaseInSwitchArm: case ErrorCode.ERR_ConstantValueOfTypeExpected: + case ErrorCode.ERR_UnsupportedPrimaryConstructorParameterCapturingRefAny: return false; default: // NOTE: All error codes must be explicitly handled in this switch statement diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index e55e7ad573ffb..92e08d6ae0c10 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -265,6 +265,8 @@ internal enum MessageID IDS_FeaturePrimaryConstructors = MessageBase + 12833, IDS_FeatureUsingTypeAlias = MessageBase + 12834, + + IDS_FeatureInstanceMemberInNameof = MessageBase + 12835, } // Message IDs may refer to strings that need to be localized. @@ -314,31 +316,87 @@ public static LocalizableErrorArgument Localize(this MessageID id) } } + internal static bool CheckFeatureAvailability( + this MessageID feature, + DiagnosticBag diagnostics, + SyntaxNode syntax, + Location? location = null) + { + return CheckFeatureAvailability( + feature, + diagnostics, + syntax.SyntaxTree.Options, + static tuple => tuple.location ?? tuple.syntax.Location, + (syntax, location)); + } + + internal static bool CheckFeatureAvailability( + this MessageID feature, + DiagnosticBag diagnostics, + SyntaxToken syntax, + Location? location = null) + { + return CheckFeatureAvailability( + feature, + diagnostics, + syntax.SyntaxTree!.Options, + static tuple => tuple.location ?? tuple.syntax.GetLocation(), + (syntax, location)); + } + internal static bool CheckFeatureAvailability( this MessageID feature, BindingDiagnosticBag diagnostics, SyntaxNode syntax, Location? location = null) { - var diag = GetFeatureAvailabilityDiagnosticInfo(feature, (CSharpParseOptions)syntax.SyntaxTree.Options); - if (diag is object) + return CheckFeatureAvailability( + feature, + diagnostics, + syntax.SyntaxTree.Options, + static tuple => tuple.location ?? tuple.syntax.Location, + (syntax, location)); + } + + internal static bool CheckFeatureAvailability( + this MessageID feature, + BindingDiagnosticBag diagnostics, + SyntaxToken syntax, + Location? location = null) + { + return CheckFeatureAvailability( + feature, + diagnostics, + syntax.SyntaxTree!.Options, + static tuple => tuple.location ?? tuple.syntax.GetLocation(), + (syntax, location)); + } + + private static bool CheckFeatureAvailability( + this MessageID feature, + DiagnosticBag diagnostics, + ParseOptions parseOptions, + Func getLocation, + TData data) + { + if (GetFeatureAvailabilityDiagnosticInfo(feature, (CSharpParseOptions)parseOptions) is { } diagInfo) { - diagnostics.Add(diag, location ?? syntax.GetLocation()); + diagnostics.Add(diagInfo, getLocation(data)); return false; } return true; } - internal static bool CheckFeatureAvailability( + private static bool CheckFeatureAvailability( this MessageID feature, - DiagnosticBag diagnostics, - SyntaxNode syntax, - Location? location = null) + BindingDiagnosticBag diagnostics, + ParseOptions parseOptions, + Func getLocation, + TData data) { - var diag = GetFeatureAvailabilityDiagnosticInfo(feature, (CSharpParseOptions)syntax.SyntaxTree.Options); - if (diag is object) + if (GetFeatureAvailabilityDiagnosticInfo(feature, (CSharpParseOptions)parseOptions) is { } diagInfo) { - diagnostics.Add(diag, location ?? syntax.GetLocation()); + diagnostics.Add(diagInfo, getLocation(data)); return false; } return true; @@ -393,6 +451,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) case MessageID.IDS_FeatureLambdaParamsArray: // semantic check case MessageID.IDS_FeaturePrimaryConstructors: // declaration table check case MessageID.IDS_FeatureUsingTypeAlias: // semantic check + case MessageID.IDS_FeatureInstanceMemberInNameof: // semantic check return LanguageVersion.Preview; // C# 11.0 features. diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index 228e533fa7271..6139464e617a9 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -7920,7 +7920,7 @@ private TypeWithState VisitConversion( bool extensionMethodThisArgument = false, Optional stateForLambda = default, bool trackMembers = false, - Location? diagnosticLocationOpt = null, + Location? diagnosticLocation = null, ArrayBuilder? previousArgumentConversionResults = null) { Debug.Assert(!trackMembers || !IsConditionalState); @@ -7957,7 +7957,6 @@ private TypeWithState VisitConversion( NullableFlowState resultState = NullableFlowState.NotNull; bool canConvertNestedNullability = true; bool isSuppressed = false; - diagnosticLocationOpt ??= (conversionOpt ?? conversionOperand).Syntax.GetLocation(); if (conversionOperand.IsSuppressed == true) { @@ -7985,7 +7984,7 @@ private TypeWithState VisitConversion( } if (reportRemainingWarnings && invokeSignature != null) { - ReportNullabilityMismatchWithTargetDelegate(diagnosticLocationOpt, targetType, invokeSignature, method, conversion.IsExtensionMethod); + ReportNullabilityMismatchWithTargetDelegate(getDiagnosticLocation(), targetType, invokeSignature, method, conversion.IsExtensionMethod); } } resultState = NullableFlowState.NotNull; @@ -8007,7 +8006,7 @@ private TypeWithState VisitConversion( VisitLambda(lambda, delegateType, stateForLambda); if (reportRemainingWarnings && delegateType is not null) { - ReportNullabilityMismatchWithTargetDelegate(diagnosticLocationOpt, delegateType, lambda); + ReportNullabilityMismatchWithTargetDelegate(getDiagnosticLocation(), delegateType, lambda); } TrackAnalyzedNullabilityThroughConversionGroup(targetTypeWithNullability.ToTypeWithState(), conversionOpt, conversionOperand); @@ -8038,7 +8037,7 @@ private TypeWithState VisitConversion( case ConversionKind.ExplicitUserDefined: case ConversionKind.ImplicitUserDefined: - return VisitUserDefinedConversion(conversionOpt, conversionOperand, conversion, targetTypeWithNullability, operandType, useLegacyWarnings, assignmentKind, parameterOpt, reportTopLevelWarnings, reportRemainingWarnings, diagnosticLocationOpt); + return VisitUserDefinedConversion(conversionOpt, conversionOperand, conversion, targetTypeWithNullability, operandType, useLegacyWarnings, assignmentKind, parameterOpt, reportTopLevelWarnings, reportRemainingWarnings, getDiagnosticLocation()); case ConversionKind.ExplicitDynamic: case ConversionKind.ImplicitDynamic: @@ -8054,7 +8053,7 @@ private TypeWithState VisitConversion( { if (!operandType.IsNotNull && reportRemainingWarnings) { - ReportDiagnostic(ErrorCode.WRN_UnboxPossibleNull, diagnosticLocationOpt); + ReportDiagnostic(ErrorCode.WRN_UnboxPossibleNull, getDiagnosticLocation()); } LearnFromNonNullTest(conversionOperand, ref State); @@ -8148,7 +8147,7 @@ private TypeWithState VisitConversion( // Explicit conversion of Nullable to T is equivalent to Nullable.Value. if (reportTopLevelWarnings && operandType.MayBeNull) { - ReportDiagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, diagnosticLocationOpt); + ReportDiagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, getDiagnosticLocation()); } // Mark the value as not nullable, regardless of whether it was known to be nullable, @@ -8214,17 +8213,17 @@ private TypeWithState VisitConversion( // Need to report all warnings that apply since the warnings can be suppressed individually. if (reportTopLevelWarnings) { - ReportNullableAssignmentIfNecessary(conversionOperand, targetTypeWithNullability, resultType, useLegacyWarnings, assignmentKind, parameterOpt, diagnosticLocationOpt); + ReportNullableAssignmentIfNecessary(conversionOperand, targetTypeWithNullability, resultType, useLegacyWarnings, assignmentKind, parameterOpt, getDiagnosticLocation()); } if (reportRemainingWarnings && !canConvertNestedNullability) { if (assignmentKind == AssignmentKind.Argument) { - ReportNullabilityMismatchInArgument(diagnosticLocationOpt, operandType.Type, parameterOpt, targetType, forOutput: false); + ReportNullabilityMismatchInArgument(getDiagnosticLocation(), operandType.Type, parameterOpt, targetType, forOutput: false); } else { - ReportNullabilityMismatchInAssignment(diagnosticLocationOpt, GetTypeAsDiagnosticArgument(operandType.Type), targetType); + ReportNullabilityMismatchInAssignment(getDiagnosticLocation(), GetTypeAsDiagnosticArgument(operandType.Type), targetType); } } } @@ -8233,6 +8232,13 @@ private TypeWithState VisitConversion( return resultType; + // Avoid realizing the diagnostic location until needed. + Location getDiagnosticLocation() + { + diagnosticLocation ??= (conversionOpt ?? conversionOperand).Syntax.GetLocation(); + return diagnosticLocation; + } + #nullable enable static TypeWithState calculateResultType(TypeWithAnnotations targetTypeWithNullability, bool fromExplicitCast, NullableFlowState resultState, bool isSuppressed, TypeSymbol targetType) { @@ -8475,7 +8481,7 @@ private TypeWithState VisitUserDefinedConversion( parameterOpt, reportTopLevelWarnings, reportRemainingWarnings, - diagnosticLocationOpt: diagnosticLocation); + diagnosticLocation: diagnosticLocation); // Update method based on operandType: see https://github.com/dotnet/roslyn/issues/29605. // (see NullableReferenceTypesTests.ImplicitConversions_07). @@ -8688,7 +8694,7 @@ private TypeWithState ClassifyAndVisitConversion( parameterOpt, reportTopLevelWarnings: reportWarnings, reportRemainingWarnings: !fromExplicitCast && reportWarnings, - diagnosticLocationOpt: diagnosticLocation); + diagnosticLocation: diagnosticLocation); } public override BoundNode? VisitDelegateCreationExpression(BoundDelegateCreationExpression node) @@ -10221,7 +10227,7 @@ public override void VisitForEachIterationVariables(BoundForEachStatement node) AssignmentKind.ForEachIterationVariable, reportTopLevelWarnings: true, reportRemainingWarnings: true, - diagnosticLocationOpt: variableLocation); + diagnosticLocation: variableLocation); } // In non-error cases we'll only run this loop a single time. In error cases we'll set the nullability of the VariableType multiple times, but at least end up with something diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs index 55e82a0530129..0fd96f0aaf08d 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs @@ -1558,9 +1558,8 @@ private BoundExpression CaptureExpressionInTempIfNeeded( private BoundExpression MakeNewNullableBoolean(SyntaxNode syntax, bool? value) { - NamedTypeSymbol nullableType = _compilation.GetSpecialType(SpecialType.System_Nullable_T); TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean); - NamedTypeSymbol nullableBoolType = nullableType.Construct(boolType); + NamedTypeSymbol nullableBoolType = _compilation.GetOrCreateNullableType(boolType); if (value == null) { return new BoundDefaultExpression(syntax, nullableBoolType); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs index aefd2df3998c4..fb537a77a0ee1 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs @@ -734,7 +734,7 @@ private BoundExpression MakeBuiltInIncrementOperator(BoundIncrementOperator node if (binaryOperatorKind.IsLifted()) { - binaryOperandType = _compilation.GetSpecialType(SpecialType.System_Nullable_T).Construct(binaryOperandType); + binaryOperandType = _compilation.GetOrCreateNullableType(binaryOperandType); MethodSymbol ctor = UnsafeGetNullableMethod(node.Syntax, binaryOperandType, SpecialMember.System_Nullable_T__ctor); boundOne = new BoundObjectCreationExpression(node.Syntax, ctor, boundOne); } diff --git a/src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs index 487971e6bbae6..f39280f32a662 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs @@ -374,7 +374,7 @@ private NamespaceOrTypeSymbol ResolveAliasTarget( { if (usingDirective.UnsafeKeyword != default) { - MessageID.IDS_FeatureUsingTypeAlias.CheckFeatureAvailability(diagnostics, usingDirective, usingDirective.UnsafeKeyword.GetLocation()); + MessageID.IDS_FeatureUsingTypeAlias.CheckFeatureAvailability(diagnostics, usingDirective.UnsafeKeyword); } else if (usingDirective.NamespaceOrType is not NameSyntax) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs b/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs index 9898f52773941..64b9a72c48268 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/GlobalExpressionVariable.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.Symbols @@ -30,8 +31,8 @@ internal GlobalExpressionVariable( TypeSyntax typeSyntax, string name, SyntaxReference syntax, - Location location) - : base(containingType, modifiers, name, syntax, location) + TextSpan locationSpan) + : base(containingType, modifiers, name, syntax, locationSpan) { Debug.Assert(DeclaredAccessibility == Accessibility.Private); _typeSyntaxOpt = typeSyntax?.GetReference(); @@ -43,7 +44,7 @@ internal static GlobalExpressionVariable Create( TypeSyntax typeSyntax, string name, SyntaxNode syntax, - Location location, + TextSpan locationSpan, FieldSymbol containingFieldOpt, SyntaxNode nodeToBind) { @@ -51,8 +52,8 @@ internal static GlobalExpressionVariable Create( var syntaxReference = syntax.GetReference(); return (typeSyntax == null || typeSyntax.SkipScoped(out _).SkipRef().IsVar) - ? new InferrableGlobalExpressionVariable(containingType, modifiers, typeSyntax, name, syntaxReference, location, containingFieldOpt, nodeToBind) - : new GlobalExpressionVariable(containingType, modifiers, typeSyntax, name, syntaxReference, location); + ? new InferrableGlobalExpressionVariable(containingType, modifiers, typeSyntax, name, syntaxReference, locationSpan, containingFieldOpt, nodeToBind) + : new GlobalExpressionVariable(containingType, modifiers, typeSyntax, name, syntaxReference, locationSpan); } protected override SyntaxList AttributeDeclarationSyntaxList => default(SyntaxList); @@ -169,10 +170,10 @@ internal InferrableGlobalExpressionVariable( TypeSyntax typeSyntax, string name, SyntaxReference syntax, - Location location, + TextSpan locationSpan, FieldSymbol containingFieldOpt, SyntaxNode nodeToBind) - : base(containingType, modifiers, typeSyntax, name, syntax, location) + : base(containingType, modifiers, typeSyntax, name, syntax, locationSpan) { Debug.Assert(nodeToBind.Kind() == SyntaxKind.VariableDeclarator || nodeToBind is ExpressionSyntax); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs index 89a6dbdb8a619..e54c7008482b0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/LocalFunctionSymbol.cs @@ -303,6 +303,8 @@ public override bool IsExtensionMethod public override ImmutableArray Locations => ImmutableArray.Create(Syntax.Identifier.GetLocation()); + public override Location? TryGetFirstLocation() => Syntax.Identifier.GetLocation(); + internal override bool GenerateDebugInfo => true; public override ImmutableArray RefCustomModifiers => ImmutableArray.Empty; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs index 70a4c7370c399..efbe13b19594e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ModifierUtils.cs @@ -27,7 +27,7 @@ internal static DeclarationModifiers MakeAndCheckNonTypeMemberModifiers( var readonlyToken = modifiers.FirstOrDefault(SyntaxKind.ReadOnlyKeyword); if (readonlyToken.Parent is MethodDeclarationSyntax or AccessorDeclarationSyntax or BasePropertyDeclarationSyntax or EventDeclarationSyntax) - modifierErrors |= !MessageID.IDS_FeatureReadOnlyMembers.CheckFeatureAvailability(diagnostics, readonlyToken.Parent, readonlyToken.GetLocation()); + modifierErrors |= !MessageID.IDS_FeatureReadOnlyMembers.CheckFeatureAvailability(diagnostics, readonlyToken); if ((result & DeclarationModifiers.AccessibilityMask) == 0) result |= defaultAccess; @@ -401,7 +401,7 @@ public static DeclarationModifiers ToDeclarationModifiers( if (one == DeclarationModifiers.Partial) { var messageId = isForTypeDeclaration ? MessageID.IDS_FeaturePartialTypes : MessageID.IDS_FeaturePartialMethod; - messageId.CheckFeatureAvailability(diagnostics, modifier.Parent, modifier.GetLocation()); + messageId.CheckFeatureAvailability(diagnostics, modifier); // `partial` must always be the last modifier according to the language. However, there was a bug // where we allowed `partial async` at the end of modifiers on methods. We keep this behavior for diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs index 4c90e7e60b40e..613f0c478e7a9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/ParameterHelpers.cs @@ -531,7 +531,7 @@ internal static void CheckParameterModifiers( if (parsingLambdaParams) { - MessageID.IDS_FeatureLambdaParamsArray.CheckFeatureAvailability(diagnostics, parameter, modifier.GetLocation()); + MessageID.IDS_FeatureLambdaParamsArray.CheckFeatureAvailability(diagnostics, modifier); } break; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs index 4544f4714dc9f..e8f76dd887aea 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceComplexParameterSymbol.cs @@ -367,7 +367,7 @@ private ConstantValue MakeDefaultExpression(BindingDiagnosticBag diagnostics, ou return ConstantValue.NotAvailable; } - MessageID.IDS_FeatureOptionalParameter.CheckFeatureAvailability(diagnostics, defaultSyntax, defaultSyntax.EqualsToken.GetLocation()); + MessageID.IDS_FeatureOptionalParameter.CheckFeatureAvailability(diagnostics, defaultSyntax.EqualsToken); binder = GetDefaultParameterValueBinder(defaultSyntax); Binder binderForDefault = binder.CreateBinderForParameterDefaultValue(this, defaultSyntax); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs index 520b9aa43f75c..ff7adaaeafb06 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceConstructorSymbolBase.cs @@ -175,14 +175,6 @@ internal sealed override OneOrMany> GetReturnTyp return OneOrMany.Create(default(SyntaxList)); } - protected sealed override IAttributeTargetSymbol AttributeOwner - { - get - { - return base.AttributeOwner; - } - } - internal sealed override bool GenerateDebugInfo { get { return true; } @@ -252,7 +244,7 @@ internal sealed override int CalculateLocalSyntaxOffset(int position, SyntaxTree protected sealed override bool HasSetsRequiredMembersImpl => GetEarlyDecodedWellKnownAttributeData()?.HasSetsRequiredMembersAttribute == true; - internal sealed override (CSharpAttributeData?, BoundAttribute?) EarlyDecodeWellKnownAttribute(ref EarlyDecodeWellKnownAttributeArguments arguments) + internal override (CSharpAttributeData?, BoundAttribute?) EarlyDecodeWellKnownAttribute(ref EarlyDecodeWellKnownAttributeArguments arguments) { if (arguments.SymbolPart == AttributeLocation.None) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEnumConstantSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEnumConstantSymbol.cs index 45500e7957e5a..81b6d58b15362 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceEnumConstantSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceEnumConstantSymbol.cs @@ -46,7 +46,7 @@ public static SourceEnumConstantSymbol CreateImplicitValuedConstant( } protected SourceEnumConstantSymbol(SourceMemberContainerTypeSymbol containingEnum, EnumMemberDeclarationSyntax syntax, BindingDiagnosticBag diagnostics) - : base(containingEnum, syntax.Identifier.ValueText, syntax.GetReference(), syntax.Identifier.GetLocation()) + : base(containingEnum, syntax.Identifier.ValueText, syntax.GetReference(), syntax.Identifier.Span) { if (this.Name == WellKnownMemberNames.EnumBackingFieldName) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldSymbol.cs index e97a17ce96331..04279e41aaf83 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceFieldSymbol.cs @@ -11,6 +11,7 @@ using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Text; namespace Microsoft.CodeAnalysis.CSharp.Symbols { @@ -166,7 +167,7 @@ internal sealed override bool HasRuntimeSpecialName internal abstract class SourceFieldSymbolWithSyntaxReference : SourceFieldSymbol { private readonly string _name; - private readonly Location _location; + private readonly TextSpan _locationSpan; private readonly SyntaxReference _syntaxReference; private string _lazyDocComment; @@ -174,16 +175,15 @@ internal abstract class SourceFieldSymbolWithSyntaxReference : SourceFieldSymbol private ConstantValue _lazyConstantEarlyDecodingValue = Microsoft.CodeAnalysis.ConstantValue.Unset; private ConstantValue _lazyConstantValue = Microsoft.CodeAnalysis.ConstantValue.Unset; - protected SourceFieldSymbolWithSyntaxReference(SourceMemberContainerTypeSymbol containingType, string name, SyntaxReference syntax, Location location) + protected SourceFieldSymbolWithSyntaxReference(SourceMemberContainerTypeSymbol containingType, string name, SyntaxReference syntax, TextSpan locationSpan) : base(containingType) { Debug.Assert(name != null); Debug.Assert(syntax != null); - Debug.Assert(location != null); _name = name; _syntaxReference = syntax; - _location = location; + _locationSpan = locationSpan; } public SyntaxTree SyntaxTree @@ -211,33 +211,22 @@ public sealed override string Name } internal override LexicalSortKey GetLexicalSortKey() - { - return new LexicalSortKey(_location, this.DeclaringCompilation); - } + => new LexicalSortKey(_syntaxReference, this.DeclaringCompilation); + + public override Location TryGetFirstLocation() + => _syntaxReference.SyntaxTree.GetLocation(_locationSpan); public sealed override ImmutableArray Locations - { - get - { - return ImmutableArray.Create(_location); - } - } + => ImmutableArray.Create(GetFirstLocation()); internal sealed override Location ErrorLocation - { - get - { - return _location; - } - } + => GetFirstLocation(); public sealed override ImmutableArray DeclaringSyntaxReferences - { - get - { - return ImmutableArray.Create(_syntaxReference); - } - } + => ImmutableArray.Create(_syntaxReference); + + public override bool IsDefinedInSourceTree(SyntaxTree tree, TextSpan? definedWithinSpan, CancellationToken cancellationToken = default) + => IsDefinedInSourceTree(_syntaxReference, tree, definedWithinSpan); public sealed override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken)) { @@ -341,7 +330,7 @@ private void BindConstantValueIfNecessary(bool earlyDecodingWellKnownAttributes, var diagnostics = BindingDiagnosticBag.GetInstance(); if (startsCycle) { - diagnostics.Add(ErrorCode.ERR_CircConstValue, _location, this); + diagnostics.Add(ErrorCode.ERR_CircConstValue, GetFirstLocation(), this); } var value = MakeConstantValue(builder, earlyDecodingWellKnownAttributes, diagnostics); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs index ccbacc8bf13bd..746a884447a8e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs @@ -8,11 +8,14 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; -using System.Runtime.CompilerServices; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Syntax; using Roslyn.Utilities; +#if DEBUG +using System.Runtime.CompilerServices; +#endif + namespace Microsoft.CodeAnalysis.CSharp.Symbols { /// @@ -28,7 +31,6 @@ internal class SourceLocalSymbol : LocalSymbol private readonly Symbol _containingSymbol; private readonly SyntaxToken _identifierToken; - private readonly ImmutableArray _locations; private readonly TypeSyntax _typeSyntax; private readonly RefKind _refKind; private readonly LocalDeclarationKind _declarationKind; @@ -69,9 +71,6 @@ private SourceLocalSymbol( : isScoped ? ScopedKind.ScopedValue : ScopedKind.None; this._declarationKind = declarationKind; - - // create this eagerly as it will always be needed for the EnsureSingleDefinition - _locations = ImmutableArray.Create(identifierToken.GetLocation()); } /// @@ -404,18 +403,16 @@ internal void SetTypeWithAnnotations(TypeWithAnnotations newType) } } + public override Location TryGetFirstLocation() + => _identifierToken.GetLocation(); + /// /// Gets the locations where the local symbol was originally defined in source. /// There should not be local symbols from metadata, and there should be only one local variable declared. /// TODO: check if there are multiple same name local variables - error symbol or local symbol? /// public override ImmutableArray Locations - { - get - { - return _locations; - } - } + => ImmutableArray.Create(GetFirstLocation()); internal sealed override SyntaxNode GetDeclaratorSyntax() { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs index cdb108c1d88b5..589b61065b511 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberContainerSymbol.cs @@ -252,6 +252,7 @@ internal SourceMemberContainerTypeSymbol( : SpecialType.None; _flags = new Flags(specialType, typeKind, declaration.HasPrimaryConstructor); + Debug.Assert(typeKind is TypeKind.Struct or TypeKind.Class || !HasPrimaryConstructor); var containingType = this.ContainingType; if (containingType?.IsSealed == true && this.DeclaredAccessibility.HasProtected()) @@ -2654,6 +2655,14 @@ private void CheckSequentialOnPartialType(BindingDiagnosticBag diagnostics) } } } + + if (whereFoundField != null && + PrimaryConstructor is { } primaryConstructor && primaryConstructor.GetCapturedParameters().Any() && + (primaryConstructor.SyntaxRef.SyntaxTree != whereFoundField.SyntaxTree || primaryConstructor.SyntaxRef.Span != whereFoundField.Span)) + { + diagnostics.Add(ErrorCode.WRN_SequentialOnPartialClass, GetFirstLocation(), this); + return; + } } private static bool HasInstanceData(MemberDeclarationSyntax m) @@ -3200,20 +3209,28 @@ ImmutableArray buildSimpleProgramEntry } } + internal bool HasPrimaryConstructor => this._flags.HasPrimaryConstructor; + internal SynthesizedPrimaryConstructor? PrimaryConstructor { get { - if (!this._flags.HasPrimaryConstructor) + if (!HasPrimaryConstructor) return null; var declared = Volatile.Read(ref _lazyDeclaredMembersAndInitializers); + SynthesizedPrimaryConstructor? result; if (declared is not null && declared != DeclaredMembersAndInitializers.UninitializedSentinel) { - return declared.PrimaryConstructor; + result = declared.PrimaryConstructor; + } + else + { + result = GetMembersAndInitializers().PrimaryConstructor; } - return GetMembersAndInitializers().PrimaryConstructor; + Debug.Assert(result is object); + return result; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs index 3c8930fb110f2..c053192dea854 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberFieldSymbol.cs @@ -26,8 +26,8 @@ internal SourceMemberFieldSymbol( DeclarationModifiers modifiers, string name, SyntaxReference syntax, - Location location) - : base(containingType, name, syntax, location) + TextSpan locationSpan) + : base(containingType, name, syntax, locationSpan) { _modifiers = modifiers; } @@ -196,7 +196,7 @@ internal static DeclarationModifiers MakeModifiers(NamedTypeSymbol containingTyp foreach (var modifier in modifiers) { if (modifier.IsKind(SyntaxKind.FixedKeyword)) - MessageID.IDS_FeatureFixedBuffer.CheckFeatureAvailability(diagnostics, modifier.Parent, modifier.GetLocation()); + MessageID.IDS_FeatureFixedBuffer.CheckFeatureAvailability(diagnostics, modifier); } reportBadMemberFlagIfAny(result, DeclarationModifiers.Static, diagnostics, errorLocation); @@ -338,7 +338,7 @@ internal SourceMemberFieldSymbolFromDeclarator( DeclarationModifiers modifiers, bool modifierErrors, BindingDiagnosticBag diagnostics) - : base(containingType, modifiers, declarator.Identifier.ValueText, declarator.GetReference(), declarator.Identifier.GetLocation()) + : base(containingType, modifiers, declarator.Identifier.ValueText, declarator.GetReference(), declarator.Identifier.Span) { _hasInitializer = declarator.Initializer != null; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs index 893d1d7a431d6..79f805093c5a8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMethodSymbolWithAttributes.cs @@ -128,6 +128,11 @@ protected virtual IAttributeTargetSymbol AttributeOwner get { return this; } } + protected virtual AttributeLocation AttributeLocationForLoadAndValidateAttributes + { + get { return AttributeLocation.None; } + } + IAttributeTargetSymbol IAttributeTargetSymbol.AttributesOwner { get { return this.AttributeOwner; } @@ -280,7 +285,7 @@ private CustomAttributesBag GetAttributesBag(ref CustomAttr { var (declarations, symbolPart) = forReturnType ? (GetReturnTypeAttributeDeclarations(), AttributeLocation.Return) - : (GetAttributeDeclarations(), AttributeLocation.None); + : (GetAttributeDeclarations(), AttributeLocationForLoadAndValidateAttributes); bagCreatedOnThisThread = LoadAndValidateAttributes( declarations, ref lazyCustomAttributesBag, @@ -476,7 +481,7 @@ internal sealed override ImmutableArray GetAppliedConditionalSymbols() return data != null ? data.ConditionalSymbols : ImmutableArray.Empty; } - protected sealed override void DecodeWellKnownAttributeImpl(ref DecodeWellKnownAttributeArguments arguments) + protected override void DecodeWellKnownAttributeImpl(ref DecodeWellKnownAttributeArguments arguments) { Debug.Assert(!arguments.Attribute.HasErrors); Debug.Assert(arguments.SymbolPart == AttributeLocation.None || arguments.SymbolPart == AttributeLocation.Return); @@ -1015,7 +1020,7 @@ static UnmanagedCallersOnlyAttributeData DecodeUnmanagedCallersOnlyAttributeData } } - internal sealed override void PostDecodeWellKnownAttributes(ImmutableArray boundAttributes, ImmutableArray allAttributeSyntaxNodes, BindingDiagnosticBag diagnostics, AttributeLocation symbolPart, WellKnownAttributeData decodedData) + internal override void PostDecodeWellKnownAttributes(ImmutableArray boundAttributes, ImmutableArray allAttributeSyntaxNodes, BindingDiagnosticBag diagnostics, AttributeLocation symbolPart, WellKnownAttributeData decodedData) { Debug.Assert(!boundAttributes.IsDefault); Debug.Assert(!allAttributeSyntaxNodes.IsDefault); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs index ce3cf12ddc9e7..01950c5021b6d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs @@ -181,7 +181,7 @@ private ImmutableArray MakeTypeParameters(BindingDiagnostic throw ExceptionUtilities.UnexpectedValue(typeDecl.Kind()); } - MessageID.IDS_FeatureGenerics.CheckFeatureAvailability(diagnostics, tpl, tpl.LessThanToken.GetLocation()); + MessageID.IDS_FeatureGenerics.CheckFeatureAvailability(diagnostics, tpl.LessThanToken); bool isInterfaceOrDelegate = typeKind == SyntaxKind.InterfaceDeclaration || typeKind == SyntaxKind.DelegateDeclaration; var parameterBuilder = new List(); @@ -197,7 +197,7 @@ private ImmutableArray MakeTypeParameters(BindingDiagnostic } else { - MessageID.IDS_FeatureTypeVariance.CheckFeatureAvailability(diagnostics, tp, tp.VarianceKeyword.GetLocation()); + MessageID.IDS_FeatureTypeVariance.CheckFeatureAvailability(diagnostics, tp.VarianceKeyword); } } @@ -821,7 +821,7 @@ AttributeLocation IAttributeTargetSymbol.AllowedAttributeLocations case TypeKind.Struct: case TypeKind.Class: - return AttributeLocation.Type; + return AttributeLocation.Type | (HasPrimaryConstructor ? AttributeLocation.Method : 0); default: return AttributeLocation.None; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs index 9236831630301..0ae85749547cc 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodOrUserDefinedOperatorSymbol.cs @@ -228,11 +228,10 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, { base.AfterAddingTypeMembersChecks(conversions, diagnostics); - var location = ReturnTypeLocation; + // Defer computing location to avoid unnecessary allocations in most cases. + Location? returnTypeLocation = null; var compilation = DeclaringCompilation; - Debug.Assert(location != null); - // Check constraints on return type and parameters. Note: Dev10 uses the // method name location for any such errors. We'll do the same for return // type errors but for parameter errors, we'll use the parameter location. @@ -249,14 +248,14 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, if (RefKind == RefKind.RefReadOnly) { - compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: true); + compilation.EnsureIsReadOnlyAttributeExists(diagnostics, getReturnTypeLocation(), modifyCompilation: true); } ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); if (compilation.ShouldEmitNativeIntegerAttributes(ReturnType)) { - compilation.EnsureNativeIntegerAttributeExists(diagnostics, location, modifyCompilation: true); + compilation.EnsureNativeIntegerAttributeExists(diagnostics, getReturnTypeLocation(), modifyCompilation: true); } ParameterHelpers.EnsureNativeIntegerAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); @@ -265,10 +264,16 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, if (compilation.ShouldEmitNullableAttributes(this) && ReturnTypeWithAnnotations.NeedsNullableAttribute()) { - compilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); + compilation.EnsureNullableAttributeExists(diagnostics, getReturnTypeLocation(), modifyCompilation: true); } ParameterHelpers.EnsureNullableAttributeExists(compilation, this, Parameters, diagnostics, modifyCompilation: true); + + Location getReturnTypeLocation() + { + returnTypeLocation ??= this.ReturnTypeLocation; + return returnTypeLocation; + } } protected abstract void CheckConstraintsForExplicitInterfaceType(ConversionsBase conversions, BindingDiagnosticBag diagnostics); diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs index aadde6cc87875..49ea558801938 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs @@ -533,7 +533,7 @@ private ImmutableArray MakeTypeParameters(MethodDeclaration { Debug.Assert(syntax.TypeParameterList != null); - MessageID.IDS_FeatureGenerics.CheckFeatureAvailability(diagnostics, syntax.TypeParameterList, syntax.TypeParameterList.LessThanToken.GetLocation()); + MessageID.IDS_FeatureGenerics.CheckFeatureAvailability(diagnostics, syntax.TypeParameterList.LessThanToken); OverriddenMethodTypeParameterMapBase typeMap = null; if (this.IsOverride) @@ -620,8 +620,9 @@ public override bool IsDefinedInSourceTree( { // Since only the declaring (and not the implementing) part of a partial method appears in the member // list, we need to ensure we complete the implementation part when needed. + Debug.Assert(this.DeclaringSyntaxReferences.Length == 1); return - base.IsDefinedInSourceTree(tree, definedWithinSpan, cancellationToken) || + IsDefinedInSourceTree(this.SyntaxRef, tree, definedWithinSpan) || this.SourcePartialImplementation?.IsDefinedInSourceTree(tree, definedWithinSpan, cancellationToken) == true; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs index 9bf97d566b7ad..322b183327a77 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs @@ -235,7 +235,7 @@ protected SourcePropertyAccessorSymbol( } if (modifiers.Count > 0) - MessageID.IDS_FeaturePropertyAccessorMods.CheckFeatureAvailability(diagnostics, syntax, modifiers[0].GetLocation()); + MessageID.IDS_FeaturePropertyAccessorMods.CheckFeatureAvailability(diagnostics, modifiers[0]); } #nullable disable diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs index a2247924b101e..af93d12c934ff 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs @@ -133,7 +133,7 @@ private SourcePropertySymbol( diagnostics); if (syntax is PropertyDeclarationSyntax { Initializer: { } initializer }) - MessageID.IDS_FeatureAutoPropertyInitializer.CheckFeatureAvailability(diagnostics, syntax, initializer.EqualsToken.GetLocation()); + MessageID.IDS_FeatureAutoPropertyInitializer.CheckFeatureAvailability(diagnostics, initializer.EqualsToken); } private TypeSyntax GetTypeSyntax(SyntaxNode syntax) => ((BasePropertyDeclarationSyntax)syntax).Type; diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs index 136872cc5bb72..0e9bac249c720 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedConversionSymbol.cs @@ -27,7 +27,7 @@ public static SourceUserDefinedConversionSymbol CreateUserDefinedConversionSymbo if (name == WellKnownMemberNames.CheckedExplicitConversionName) { - MessageID.IDS_FeatureCheckedUserDefinedOperators.CheckFeatureAvailability(diagnostics, syntax, syntax.CheckedKeyword.GetLocation()); + MessageID.IDS_FeatureCheckedUserDefinedOperators.CheckFeatureAvailability(diagnostics, syntax.CheckedKeyword); } else if (syntax.CheckedKeyword.IsKind(SyntaxKind.CheckedKeyword)) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs index b150708e7de10..4a9c89330c33f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceUserDefinedOperatorSymbol.cs @@ -27,7 +27,7 @@ public static SourceUserDefinedOperatorSymbol CreateUserDefinedOperatorSymbol( if (SyntaxFacts.IsCheckedOperator(name)) { - MessageID.IDS_FeatureCheckedUserDefinedOperators.CheckFeatureAvailability(diagnostics, syntax, syntax.CheckedKeyword.GetLocation()); + MessageID.IDS_FeatureCheckedUserDefinedOperators.CheckFeatureAvailability(diagnostics, syntax.CheckedKeyword); } else if (!syntax.OperatorToken.IsMissing && syntax.CheckedKeyword.IsKind(SyntaxKind.CheckedKeyword)) { @@ -36,7 +36,7 @@ public static SourceUserDefinedOperatorSymbol CreateUserDefinedOperatorSymbol( if (name == WellKnownMemberNames.UnsignedRightShiftOperatorName) { - MessageID.IDS_FeatureUnsignedRightShift.CheckFeatureAvailability(diagnostics, syntax, syntax.OperatorToken.GetLocation()); + MessageID.IDS_FeatureUnsignedRightShift.CheckFeatureAvailability(diagnostics, syntax.OperatorToken); } var interfaceSpecifier = syntax.ExplicitInterfaceSpecifier; diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs index 734dba9e066e3..8f2c451aa7a85 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Symbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Symbol.cs @@ -829,8 +829,7 @@ internal bool Dangerous_IsFromSomeCompilation { cancellationToken.ThrowIfCancellationRequested(); - if (syntaxRef.SyntaxTree == tree && - (!definedWithinSpan.HasValue || syntaxRef.Span.IntersectsWith(definedWithinSpan.Value))) + if (IsDefinedInSourceTree(syntaxRef, tree, definedWithinSpan)) { return true; } @@ -839,6 +838,10 @@ internal bool Dangerous_IsFromSomeCompilation return false; } + protected static bool IsDefinedInSourceTree(SyntaxReference syntaxRef, SyntaxTree tree, TextSpan? definedWithinSpan) + => syntaxRef.SyntaxTree == tree && + (!definedWithinSpan.HasValue || syntaxRef.Span.IntersectsWith(definedWithinSpan.Value)); + internal static void ForceCompleteMemberByLocation(SourceLocation locationOpt, Symbol member, CancellationToken cancellationToken) { if (locationOpt == null || member.IsDefinedInSourceTree(locationOpt.SourceTree, locationOpt.SourceSpan, cancellationToken)) diff --git a/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs b/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs index 9b50cfc1d6b7f..f8f464d31506e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs @@ -590,7 +590,8 @@ private ImmutableArray GetAttributesToBind( foreach (var attributeDeclarationSyntax in attributeDeclarationSyntaxList) { // We bind the attribute only if it has a matching target for the given ownerSymbol and attributeLocation. - if (MatchAttributeTarget(attributeTarget, symbolPart, attributeDeclarationSyntax.Target, diagnostics)) + if (MatchAttributeTarget(attributeTarget, symbolPart, attributeDeclarationSyntax.Target, diagnostics) && + ShouldBindAttributes(attributeDeclarationSyntax, diagnostics)) { if (syntaxBuilder == null) { @@ -644,6 +645,11 @@ private ImmutableArray GetAttributesToBind( } } + protected virtual bool ShouldBindAttributes(AttributeListSyntax attributeDeclarationSyntax, BindingDiagnosticBag diagnostics) + { + return true; + } + #nullable enable private Binder GetAttributeBinder(SyntaxList attributeDeclarationSyntaxList, CSharpCompilation compilation, Binder? rootBinder = null) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedPrimaryConstructor.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedPrimaryConstructor.cs index f737464c83c8c..2703847c97b27 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedPrimaryConstructor.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/Records/SynthesizedPrimaryConstructor.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Threading; @@ -21,6 +22,9 @@ public SynthesizedPrimaryConstructor( base(containingType, syntax.Identifier.GetLocation(), syntax, isIterator: false) { Debug.Assert(syntax.Kind() is SyntaxKind.RecordDeclaration or SyntaxKind.RecordStructDeclaration or SyntaxKind.ClassDeclaration or SyntaxKind.StructDeclaration); + Debug.Assert(containingType.HasPrimaryConstructor); + Debug.Assert(containingType is SourceNamedTypeSymbol); + Debug.Assert(containingType is IAttributeTargetSymbol); this.MakeFlags( MethodKind.Constructor, @@ -36,6 +40,21 @@ internal TypeDeclarationSyntax GetSyntax() return (TypeDeclarationSyntax)syntaxReferenceOpt.GetSyntax(); } + protected override IAttributeTargetSymbol AttributeOwner + { + get { return (IAttributeTargetSymbol)ContainingType; } + } + + protected override AttributeLocation AttributeLocationForLoadAndValidateAttributes + { + get { return AttributeLocation.Method; } + } + + internal override OneOrMany> GetAttributeDeclarations() + { + return new OneOrMany>(((SourceNamedTypeSymbol)ContainingType).GetAttributeDeclarations()); + } + protected override ParameterListSyntax GetParameterList() { return GetSyntax().ParameterList!; @@ -99,5 +118,55 @@ public IReadOnlyDictionary GetCapturedParameters() Interlocked.CompareExchange(ref _capturedParameters, Binder.CapturedParametersFinder.GetCapturedParameters(this), null); return _capturedParameters; } + + internal override (CSharpAttributeData?, BoundAttribute?) EarlyDecodeWellKnownAttribute(ref EarlyDecodeWellKnownAttributeArguments arguments) + { + Debug.Assert(arguments.SymbolPart == AttributeLocation.Method); + arguments.SymbolPart = AttributeLocation.None; + var result = base.EarlyDecodeWellKnownAttribute(ref arguments); + arguments.SymbolPart = AttributeLocation.Method; + return result; + } + + protected override void DecodeWellKnownAttributeImpl(ref DecodeWellKnownAttributeArguments arguments) + { + Debug.Assert(arguments.SymbolPart == AttributeLocation.Method); + arguments.SymbolPart = AttributeLocation.None; + base.DecodeWellKnownAttributeImpl(ref arguments); + arguments.SymbolPart = AttributeLocation.Method; + } + + internal override void PostDecodeWellKnownAttributes(ImmutableArray boundAttributes, ImmutableArray allAttributeSyntaxNodes, BindingDiagnosticBag diagnostics, AttributeLocation symbolPart, WellKnownAttributeData decodedData) + { + Debug.Assert(symbolPart is AttributeLocation.Method or AttributeLocation.Return); + base.PostDecodeWellKnownAttributes(boundAttributes, allAttributeSyntaxNodes, diagnostics, symbolPart is AttributeLocation.Method ? AttributeLocation.None : symbolPart, decodedData); + } + + protected override bool ShouldBindAttributes(AttributeListSyntax attributeDeclarationSyntax, BindingDiagnosticBag diagnostics) + { + Debug.Assert(attributeDeclarationSyntax.Target is object); + + if (!base.ShouldBindAttributes(attributeDeclarationSyntax, diagnostics)) + { + return false; + } + + if (attributeDeclarationSyntax.SyntaxTree == SyntaxRef.SyntaxTree && + GetSyntax().AttributeLists.Contains(attributeDeclarationSyntax)) + { + if (ContainingType is { IsRecord: true } or { IsRecordStruct: true }) + { + MessageID.IDS_FeaturePrimaryConstructors.CheckFeatureAvailability(diagnostics, attributeDeclarationSyntax, attributeDeclarationSyntax.Target.Identifier.GetLocation()); + } + + return true; + } + + SyntaxToken target = attributeDeclarationSyntax.Target.Identifier; + diagnostics.Add(ErrorCode.WRN_AttributeLocationOnBadDeclaration, + target.GetLocation(), target.ToString(), (AttributeOwner.AllowedAttributeLocations & ~AttributeLocation.Method).ToDisplayString()); + + return false; + } } } diff --git a/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs b/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs index c181404184893..f315f0a24792a 100644 --- a/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs +++ b/src/Compilers/CSharp/Portable/Syntax/SyntaxNodeExtensions.cs @@ -271,10 +271,10 @@ private static TypeSyntax SkipRefWorker(TypeSyntax syntax, BindingDiagnosticBag? (current.Parent is VariableDeclarationSyntax { Parent: LocalDeclarationStatementSyntax } variableDeclaration && variableDeclaration.Type == current)); #endif - MessageID.IDS_FeatureRefLocalsReturns.CheckFeatureAvailability(diagnostics, refType, refType.RefKeyword.GetLocation()); + MessageID.IDS_FeatureRefLocalsReturns.CheckFeatureAvailability(diagnostics, refType.RefKeyword); if (refType.ReadOnlyKeyword != default) - MessageID.IDS_FeatureReadOnlyReferences.CheckFeatureAvailability(diagnostics, refType, refType.ReadOnlyKeyword.GetLocation()); + MessageID.IDS_FeatureReadOnlyReferences.CheckFeatureAvailability(diagnostics, refType.ReadOnlyKeyword); } return refType.Type; @@ -325,8 +325,7 @@ internal static SyntaxNode ModifyingScopedOrRefTypeOrSelf(this SyntaxNode syntax return syntax; } - MessageID.IDS_FeatureRefLocalsReturns.CheckFeatureAvailability( - diagnostics, refExpression, refExpression.RefKeyword.GetLocation()); + MessageID.IDS_FeatureRefLocalsReturns.CheckFeatureAvailability(diagnostics, refExpression.RefKeyword); refKind = RefKind.Ref; expression.CheckDeconstructionCompatibleArgument(diagnostics); diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 08b608bf72086..cf2d5091ce7e3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -1777,6 +1777,11 @@ Uvnitř členu instance nejde použít parametr primárního konstruktoru {0} typu odkaz, výstup nebo vstup. + + Cannot use primary constructor parameter of type '{0}' inside an instance member + Cannot use primary constructor parameter of type '{0}' inside an instance member + + Cannot use primary constructor parameter '{0}' that has ref-like type inside an instance member Nejde použít parametr primárního konstruktoru {0}, který má uvnitř členu instance typ podobný odkazu. @@ -1902,6 +1907,11 @@ odvozený typ delegáta + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes atributy lambda @@ -11672,11 +11682,6 @@ Pokud chcete odstranit toto varování, můžete místo toho použít /reference {0} nejde vrátit pomocí odkazu, protože je to {1}. - - Cannot return fields of '{0}' by reference because it is a '{1}' - Pole elementu {0} nejde vrátit pomocí odkazu, protože je to {1}. - - A readonly field cannot be returned by writable reference Pole jen pro čtení nejde vrátit zapisovatelným odkazem. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 1d6e11204d64a..d1aa50feea941 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -1777,6 +1777,11 @@ Der ref-, out- oder in-primäre Konstruktorparameter „{0}“ kann nicht innerhalb eines Instanzmembers verwendet werden. + + Cannot use primary constructor parameter of type '{0}' inside an instance member + Cannot use primary constructor parameter of type '{0}' inside an instance member + + Cannot use primary constructor parameter '{0}' that has ref-like type inside an instance member Der primäre Konstruktorparameter „{0}“, der einen ref-ähnlichen Typ innerhalb eines Instanzmembers aufweist, kann nicht verwendet werden. @@ -1902,6 +1907,11 @@ abgeleiteter Delegattyp + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes Lambdaattribute @@ -11672,11 +11682,6 @@ Um die Warnung zu beheben, können Sie stattdessen /reference verwenden (Einbett "{0}" kann nicht als Verweis zurückgegeben werden, weil es sich um ein {1}-Element handelt. - - Cannot return fields of '{0}' by reference because it is a '{1}' - Felder von "{0}" können nicht als Verweis zurückgegeben werden, weil es sich um ein {1}-Element handelt. - - A readonly field cannot be returned by writable reference Ein schreibgeschütztes Feld kann nicht als schreibbarer Verweis zurückgegeben werden. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 4f8f403b1ec52..1d7eae3b86136 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -1777,6 +1777,11 @@ No se puede usar ref, out o en el parámetro de constructor principal '{0}' dentro de un miembro de instancia + + Cannot use primary constructor parameter of type '{0}' inside an instance member + Cannot use primary constructor parameter of type '{0}' inside an instance member + + Cannot use primary constructor parameter '{0}' that has ref-like type inside an instance member No se puede usar el parámetro de constructor principal '{0}' que tiene un tipo ref-like dentro de un miembro de instancia @@ -1902,6 +1907,11 @@ tipo de delegado inferido + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes atributos de lambda @@ -11672,11 +11682,6 @@ Para eliminar la advertencia puede usar /reference (establezca la propiedad Embe '{0}' no se puede devolver por referencia porque es un '{1}'. - - Cannot return fields of '{0}' by reference because it is a '{1}' - Los campos de '{0}' no se pueden devolver por referencia porque es un '{1}'. - - A readonly field cannot be returned by writable reference No se puede devolver un campo de solo lectura por referencia grabable. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 67db681971e65..d9515827f9d61 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -1777,6 +1777,11 @@ Impossible d’utiliser le paramètre de constructeur principal '{0}' avec ref, out ou in à l’intérieur d’un membre d’instance. + + Cannot use primary constructor parameter of type '{0}' inside an instance member + Cannot use primary constructor parameter of type '{0}' inside an instance member + + Cannot use primary constructor parameter '{0}' that has ref-like type inside an instance member Nous n’avons pas pu utiliser le paramètre de constructeur principal '{0}' qui a un type de référence similaire à l’intérieur d’un membre d’instance @@ -1902,6 +1907,11 @@ type délégué déduit + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes attributs lambda @@ -11672,11 +11682,6 @@ Pour supprimer l'avertissement, vous pouvez utiliser la commande /reference (dé Impossible de retourner '{0}' par référence, car il s'agit d'un '{1}' - - Cannot return fields of '{0}' by reference because it is a '{1}' - Impossible de retourner les champs de '{0}' par référence, car il s'agit d'un '{1}' - - A readonly field cannot be returned by writable reference Impossible de retourner un champ readonly par référence accessible en écriture diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index fb63a0510b8cb..b9599ffc38273 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -1777,6 +1777,11 @@ Non è possibile usare il parametro ref, out o in del costruttore primario '{0}' all'interno di un membro di istanza + + Cannot use primary constructor parameter of type '{0}' inside an instance member + Cannot use primary constructor parameter of type '{0}' inside an instance member + + Cannot use primary constructor parameter '{0}' that has ref-like type inside an instance member Non è possibile usare il parametro del costruttore primario '{0}' con un tipo simile a ref all'interno di un membro di istanza @@ -1902,6 +1907,11 @@ tipo di delegato dedotto + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes attributi lambda @@ -11672,11 +11682,6 @@ Per rimuovere l'avviso, è invece possibile usare /reference (impostare la propr Non è possibile restituire '{0}' per riferimento perché è '{1}' - - Cannot return fields of '{0}' by reference because it is a '{1}' - Non è possibile restituire i campi di '{0}' per riferimento perché è '{1}' - - A readonly field cannot be returned by writable reference Un campo di sola lettura non può restituito per riferimento scrivibile diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index e8beb6a35d68e..edc79dc35b41a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -1777,6 +1777,11 @@ インスタンス メンバー内のプライマリ コンストラクター パラメーター '{0}' では ref、out、in を使用できません + + Cannot use primary constructor parameter of type '{0}' inside an instance member + Cannot use primary constructor parameter of type '{0}' inside an instance member + + Cannot use primary constructor parameter '{0}' that has ref-like type inside an instance member インスタンス メンバー内に ref に似た型を持つプライマリ コンストラクター パラメーター '{0}' を使用することはできません @@ -1902,6 +1907,11 @@ 推論されたデリゲート型 + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes ラムダ属性 @@ -11672,11 +11682,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ '{1}' であるため、'{0}' を参照渡しで返すことはできません - - Cannot return fields of '{0}' by reference because it is a '{1}' - '{1}' であるため、'{0}' のフィールドを参照渡しで返すことはできません - - A readonly field cannot be returned by writable reference 読み取り専用フィールドを書き込み可能な参照渡しで返すことはできません diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index c480f2f4d0adc..8ba51bf19c702 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -1777,6 +1777,11 @@ 인스턴스 멤버 내에서 ref, out 또는 기본 생성자 '{0}' 매개 변수를 사용할 수 없습니다. + + Cannot use primary constructor parameter of type '{0}' inside an instance member + Cannot use primary constructor parameter of type '{0}' inside an instance member + + Cannot use primary constructor parameter '{0}' that has ref-like type inside an instance member 인스턴스 멤버 내에 ref 유사 형식이 있는 기본 생성자 '{0}' 매개 변수를 사용할 수 없습니다. @@ -1902,6 +1907,11 @@ 유추된 대리자 형식 + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes 람다 특성 @@ -11671,11 +11681,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ '{0}'은(는) '{1}'이므로 참조로 반환할 수 없습니다. - - Cannot return fields of '{0}' by reference because it is a '{1}' - '{0}'의 필드는 '{1}'이므로 참조로 반환할 수 없습니다. - - A readonly field cannot be returned by writable reference 읽기 전용 필드는 쓰기 가능 참조로 반환될 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 3306e602d4082..d50d0d8303084 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -1777,6 +1777,11 @@ Nie można użyć ref, out lub w podstawowym parametrze konstruktora '{0}' wewnątrz elementu członkowskiego wystąpienia + + Cannot use primary constructor parameter of type '{0}' inside an instance member + Cannot use primary constructor parameter of type '{0}' inside an instance member + + Cannot use primary constructor parameter '{0}' that has ref-like type inside an instance member Nie można użyć podstawowego parametru konstruktora '{0}', który ma typ przypominający odwołanie wewnątrz składowej wystąpienia @@ -1902,6 +1907,11 @@ Typ delegowania wywnioskowanego + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes atrybuty lambda @@ -11672,11 +11682,6 @@ Aby usunąć ostrzeżenie, możesz zamiast tego użyć opcji /reference (ustaw w Nie można zwrócić elementu „{0}” przez referencję, ponieważ to jest element „{1}” - - Cannot return fields of '{0}' by reference because it is a '{1}' - Nie można zwrócić pól elementu „{0}” przez referencję, ponieważ to jest element „{1}” - - A readonly field cannot be returned by writable reference Nie można zwrócić pola tylko do odczytu przez zapisywalne odwołanie diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index c0e4438db70d8..6b2d4acb4bb83 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -1777,6 +1777,11 @@ Não é possível usar ref, out ou no parâmetro de construtor primário "{0}" dentro de um membro da instância + + Cannot use primary constructor parameter of type '{0}' inside an instance member + Cannot use primary constructor parameter of type '{0}' inside an instance member + + Cannot use primary constructor parameter '{0}' that has ref-like type inside an instance member Não é possível usar o parâmetro de construtor primário "{0}" que tem o tipo ref-like dentro de um membro de instância @@ -1902,6 +1907,11 @@ tipo representante inferido + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes lambda attributes @@ -11672,11 +11682,6 @@ Para incorporar informações de tipo de interoperabilidade para os dois assembl Não é possível retornar '{0}' por referência, porque ele é um '{1}' - - Cannot return fields of '{0}' by reference because it is a '{1}' - Não é possível retornar campos de '{0}' por referência, porque ele é um '{1}' - - A readonly field cannot be returned by writable reference Um campo somente leitura não pode ser retornado por referência gravável diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 1ef0d19b1ac13..d1362cd391fc4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -1777,6 +1777,11 @@ Невозможно использовать параметр основного конструктора "{0}" с модификаторами ref, out или in внутри элемента экземпляра + + Cannot use primary constructor parameter of type '{0}' inside an instance member + Cannot use primary constructor parameter of type '{0}' inside an instance member + + Cannot use primary constructor parameter '{0}' that has ref-like type inside an instance member Невозможно использовать параметр основного конструктора "{0}" типа ref-like внутри элемента экземпляра @@ -1902,6 +1907,11 @@ выводимый тип делегата + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes лямбда-атрибуты @@ -11672,11 +11682,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Невозможно вернуть "{0}" по ссылке, так как это "{1}" - - Cannot return fields of '{0}' by reference because it is a '{1}' - Невозможно вернуть поля "{0}" по ссылке, так как это "{1}" - - A readonly field cannot be returned by writable reference Поле, доступное только для чтения, невозможно вернуть по ссылке, доступной для записи diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 23c0e98db66e9..63258ad292658 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -1777,6 +1777,11 @@ Bir örnek üye içinde ref, out veya in '{0}' birincil oluşturucu parametresi kullanılamaz + + Cannot use primary constructor parameter of type '{0}' inside an instance member + Cannot use primary constructor parameter of type '{0}' inside an instance member + + Cannot use primary constructor parameter '{0}' that has ref-like type inside an instance member Bir örnek üye içinde ref benzeri türe sahip '{0}' birincil oluşturucu parametresi kullanılamaz @@ -1902,6 +1907,11 @@ çıkarsanan temsilci türü + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes lambda öznitelikleri @@ -11672,11 +11682,6 @@ Uyarıyı kaldırmak için, /reference kullanabilirsiniz (Birlikte Çalışma T '{0}' öğesi bir '{1}' olduğundan başvuru ile döndürülemez - - Cannot return fields of '{0}' by reference because it is a '{1}' - '{0}' bir '{1}' olduğundan alanları başvuru ile döndürülemez - - A readonly field cannot be returned by writable reference Salt okunur bir alan, yazılabilir başvuru ile döndürülemez diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index cefa521ea09ed..80a8f77bb0f6b 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -1777,6 +1777,11 @@ 无法在实例成员内的主构造函数参数 “{0}” 中使用 ref、out 或 + + Cannot use primary constructor parameter of type '{0}' inside an instance member + Cannot use primary constructor parameter of type '{0}' inside an instance member + + Cannot use primary constructor parameter '{0}' that has ref-like type inside an instance member 无法使用实例成员内具有 ref-like 类型的主构造函数参数 “{0}” @@ -1902,6 +1907,11 @@ 推断的委托类型 + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes Lambda 属性 @@ -11677,11 +11687,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ “{0}”是一个“{1}”,无法通过引用返回 - - Cannot return fields of '{0}' by reference because it is a '{1}' - “{0}”是一个“{1}”,无法通过引用返回其字段 - - A readonly field cannot be returned by writable reference 只读字段无法通过可写的引用返回 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 3d3bdc4e84ed4..04a3ec570e8e7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -1777,6 +1777,11 @@ 無法在執行個體成員內使用 ref、out 或 in 主要建立建構函式參數 '{0}' + + Cannot use primary constructor parameter of type '{0}' inside an instance member + Cannot use primary constructor parameter of type '{0}' inside an instance member + + Cannot use primary constructor parameter '{0}' that has ref-like type inside an instance member 無法使用執行個體成員內具有類似參考類型的主要建立函式參數 '{0}' @@ -1902,6 +1907,11 @@ 推斷委派類型 + + instance member in 'nameof' + instance member in 'nameof' + + lambda attributes Lambda 屬性 @@ -11672,11 +11682,6 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ 無法藉傳址方式傳回 '{0}',因其為 '{1}' - - Cannot return fields of '{0}' by reference because it is a '{1}' - 無法藉傳址方式傳回 '{0}' 欄位,因其為 '{1}' - - A readonly field cannot be returned by writable reference 無法以可寫入傳址方式傳回唯讀欄位 diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index 8731f0293ac95..cf28edcd5f19a 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -8635,7 +8635,7 @@ public void FileShareDeleteCompatibility_Windows() fsDll.Dispose(); fsPdb.Dispose(); - AssertEx.Equal(new[] { "Lib.cs", "Lib.dll", "Lib.pdb" }, Roslyn.Utilities.EnumerableExtensions.Order(Directory.GetFiles(dir.Path).Select(p => Path.GetFileName(p)))); + AssertEx.Equal(new[] { "Lib.cs", "Lib.dll", "Lib.pdb" }, Directory.GetFiles(dir.Path).Select(p => Path.GetFileName(p)).Order()); } /// @@ -8692,7 +8692,7 @@ public void FileShareDeleteCompatibility_Xplat() peDll.Dispose(); pePdb.Dispose(); - AssertEx.Equal(new[] { "Lib.cs", "Lib.dll", "Lib.pdb" }, Roslyn.Utilities.EnumerableExtensions.Order(Directory.GetFiles(dir.Path).Select(p => Path.GetFileName(p)))); + AssertEx.Equal(new[] { "Lib.cs", "Lib.dll", "Lib.pdb" }, Directory.GetFiles(dir.Path).Select(p => Path.GetFileName(p)).Order()); // files can be deleted now: File.Delete(libSrc.Path); @@ -8733,7 +8733,7 @@ public void FileShareDeleteCompatibility_ReadOnlyFiles() fsDll.Dispose(); - AssertEx.Equal(new[] { "Lib.cs", "Lib.dll" }, Roslyn.Utilities.EnumerableExtensions.Order(Directory.GetFiles(dir.Path).Select(p => Path.GetFileName(p)))); + AssertEx.Equal(new[] { "Lib.cs", "Lib.dll" }, Directory.GetFiles(dir.Path).Select(p => Path.GetFileName(p)).Order()); } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticSuppressorTests.cs b/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticSuppressorTests.cs index b2808db170bf5..9460fa67eaeb2 100644 --- a/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticSuppressorTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Diagnostics/DiagnosticSuppressorTests.cs @@ -563,7 +563,7 @@ public void TestProgrammaticSuppressionInfo_DiagnosticSuppressor() Assert.Single(diagnostics); var programmaticSuppression = diagnostics.Select(d => d.ProgrammaticSuppressionInfo).Single(); Assert.Equal(2, programmaticSuppression.Suppressions.Count); - var orderedSuppressions = Roslyn.Utilities.EnumerableExtensions.Order(programmaticSuppression.Suppressions).ToImmutableArrayOrEmpty(); + var orderedSuppressions = programmaticSuppression.Suppressions.Order().ToImmutableArrayOrEmpty(); Assert.Equal(suppressionId, orderedSuppressions[0].Id); Assert.Equal(suppressor.SuppressionDescriptor.Justification, orderedSuppressions[0].Justification); Assert.Equal(suppressionId2, orderedSuppressions[1].Id); diff --git a/src/Compilers/CSharp/Test/Emit2/FlowAnalysis/FlowTests.cs b/src/Compilers/CSharp/Test/Emit2/FlowAnalysis/FlowTests.cs index a6474e1d6859e..f718c080bade6 100644 --- a/src/Compilers/CSharp/Test/Emit2/FlowAnalysis/FlowTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/FlowAnalysis/FlowTests.cs @@ -5912,5 +5912,31 @@ public static void Main() "; CreateCompilation(source).VerifyDiagnostics(); } + + [Fact] + public void NameOf_Nested() + { + var source = """ + System.Console.WriteLine(C.M()); + public class C + { + private C c; + public static string M() => nameof(c.c.c); + } + """; + + var expectedDiagnostic = + // (4,15): warning CS0649: Field 'C.c' is never assigned to, and will always have its default value null + // private C c; + Diagnostic(ErrorCode.WRN_UnassignedInternalField, "c").WithArguments("C.c", "null").WithLocation(4, 15); + + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "c").VerifyDiagnostics(expectedDiagnostic); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "c").VerifyDiagnostics(expectedDiagnostic); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + expectedDiagnostic, + // (5,40): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public static string M() => nameof(c.c.c); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "c").WithArguments("instance member in 'nameof'").WithLocation(5, 40)); + } } } diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_INameOfOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_INameOfOperation.cs index 2587442a62a18..4f7975c981bb2 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_INameOfOperation.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_INameOfOperation.cs @@ -223,5 +223,208 @@ void M() "; VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void NameOfFlow_InstanceMemberFromStatic_Flat() + { + var source = """ + public class C + { + public int Property { get; } + public int Field; + public event System.Action Event; + + public static string StaticMethod() + /**/{ + return nameof(Property) + + nameof(Field) + + nameof(Event); + }/**/ + } + """; + + var expectedFlowGraph = """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String, Constant: "PropertyFieldEvent") (Syntax: 'nameof(Prop ... meof(Event)') + Left: + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String, Constant: "PropertyField") (Syntax: 'nameof(Prop ... meof(Field)') + Left: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Property") (Syntax: 'nameof(Property)') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Field") (Syntax: 'nameof(Field)') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Event") (Syntax: 'nameof(Event)') + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, DiagnosticDescription.None); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void NameOfFlow_InstanceMemberFromStatic_Flat_MethodGroup() + { + var source = """ + public class C + { + public void Method1() { } + public void Method1(int i) { } + public void Method2() { } + public static void Method2(int i) { } + + public static string StaticMethod() + /**/{ + return nameof(Method1) + + nameof(Method2); + }/**/ + } + """; + + var expectedFlowGraph = """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String, Constant: "Method1Method2") (Syntax: 'nameof(Meth ... of(Method2)') + Left: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Method1") (Syntax: 'nameof(Method1)') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Method2") (Syntax: 'nameof(Method2)') + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, DiagnosticDescription.None); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67565")] + public void NameOfFlow_InstanceMemberFromStatic_Nested() + { + var source = """ + public class C + { + public C1 Property { get; } + public C1 Field; + + public static string StaticMethod() + /**/{ + return nameof(Property.Property) + + nameof(Property.Field) + + nameof(Property.Event) + + nameof(Field.Property) + + nameof(Field.Field) + + nameof(Field.Event); + }/**/ + } + + public class C1 + { + public int Property { get; } + public int Field; + public event System.Action Event; + } + """; + + var expectedFlowGraph = """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String, Constant: "PropertyFieldEventPropertyFieldEvent") (Syntax: 'nameof(Prop ... ield.Event)') + Left: + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String, Constant: "PropertyFieldEventPropertyField") (Syntax: 'nameof(Prop ... ield.Field)') + Left: + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String, Constant: "PropertyFieldEventProperty") (Syntax: 'nameof(Prop ... d.Property)') + Left: + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String, Constant: "PropertyFieldEvent") (Syntax: 'nameof(Prop ... erty.Event)') + Left: + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String, Constant: "PropertyField") (Syntax: 'nameof(Prop ... erty.Field)') + Left: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Property") (Syntax: 'nameof(Prop ... y.Property)') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Field") (Syntax: 'nameof(Property.Field)') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Event") (Syntax: 'nameof(Property.Event)') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Property") (Syntax: 'nameof(Field.Property)') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Field") (Syntax: 'nameof(Field.Field)') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Event") (Syntax: 'nameof(Field.Event)') + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, DiagnosticDescription.None); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67565")] + public void NameOfFlow_InstanceMemberFromStatic_Nested_MethodGroup() + { + var source = """ + public class C + { + public C1 Property { get; } + public C1 Field; + public event System.Action Event; + + public static string StaticMethod() + /**/{ + return nameof(Property.Method) + + nameof(Field.Method) + + nameof(Event.Invoke); + }/**/ + } + + public class C1 + { + public void Method() { } + public void Method(int i) { } + } + """; + + var expectedFlowGraph = """ + Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Block[B1] - Block + Predecessors: [B0] + Statements (0) + Next (Return) Block[B2] + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String, Constant: "MethodMethodInvoke") (Syntax: 'nameof(Prop ... ent.Invoke)') + Left: + IBinaryOperation (BinaryOperatorKind.Add) (OperationKind.Binary, Type: System.String, Constant: "MethodMethod") (Syntax: 'nameof(Prop ... eld.Method)') + Left: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Method") (Syntax: 'nameof(Property.Method)') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Method") (Syntax: 'nameof(Field.Method)') + Right: + ILiteralOperation (OperationKind.Literal, Type: System.String, Constant: "Invoke") (Syntax: 'nameof(Event.Invoke)') + Block[B2] - Exit + Predecessors: [B1] + Statements (0) + """; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, DiagnosticDescription.None); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs index 4699165bab2b7..98a9d1f8137a9 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NameOfTests.cs @@ -4,13 +4,15 @@ #nullable disable -using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Threading; +using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; -using System.Threading; -using System.Linq; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { @@ -954,6 +956,315 @@ private static void Use(object o) {} Assert.Equal(0, symbolInfo.CandidateSymbols.Length); } + [Fact] + public void SymbolInfo_InstanceMemberFromStatic_Flat() + { + var source = """ + public class C + { + public int Property { get; } + public int Field; + public event System.Action Event; + + public static string StaticField = + nameof(Property) + + nameof(Field) + + nameof(Event); + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics(); + + var cProperty = comp.GetMember("C.Property"); + var cField = comp.GetMember("C.Field"); + var cEvent = comp.GetMember("C.Event"); + + var tree = comp.SyntaxTrees.Single(); + var tree2 = SyntaxFactory.ParseSyntaxTree(source + " "); + + var initializer = tree2.GetRoot().DescendantNodes().OfType().Single(); + + var nameofCalls = getNameOfCalls(tree); + Assert.Equal(3, nameofCalls.Length); + var nameofCalls2 = getNameOfCalls(tree2); + Assert.Equal(3, nameofCalls2.Length); + + var model = comp.GetSemanticModel(tree); + + verify(0, "Property", cProperty); + verify(1, "Field", cField); + verify(2, "Event", cEvent); + + void verify(int index, string expression, Symbol symbol) + { + var argument = nameofCalls[index].ArgumentList.Arguments.Single().Expression; + Assert.Equal(expression, argument.ToString()); + + verifySymbolInfo(model.GetSymbolInfo(argument)); + + var argument2 = nameofCalls2[index].ArgumentList.Arguments.Single().Expression; + Assert.Equal(expression, argument2.ToString()); + + Assert.True(model.TryGetSpeculativeSemanticModel(initializer.Position, initializer, out var model2)); + + verifySymbolInfo(model2.GetSymbolInfo(argument2)); + + verifySymbolInfo(model.GetSpeculativeSymbolInfo(argument2.Position, argument2, SpeculativeBindingOption.BindAsExpression)); + + Assert.True(model.GetSpeculativeSymbolInfo(argument2.Position, argument2, SpeculativeBindingOption.BindAsTypeOrNamespace).IsEmpty); + + void verifySymbolInfo(SymbolInfo symbolInfo) + { + Assert.NotNull(symbolInfo.Symbol); + Assert.Same(symbol.GetPublicSymbol(), symbolInfo.Symbol); + } + } + + static ImmutableArray getNameOfCalls(SyntaxTree tree) + { + return tree.GetRoot().DescendantNodes().OfType() + .Where(e => e.Expression is IdentifierNameSyntax { Identifier.ValueText: "nameof" }) + .ToImmutableArray(); + } + } + + [Fact] + public void SymbolInfo_InstanceMemberFromStatic_Flat_MethodGroup() + { + var source = """ + public class C + { + public void Method1() { } + public void Method1(int i) { } + public void Method2() { } + public static void Method2(int i) { } + + public static string StaticField = + nameof(Method1) + + nameof(Method2); + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics(); + + var cMethods1 = comp.GetMembers("C.Method1"); + Assert.Equal(2, cMethods1.Length); + var cMethods2 = comp.GetMembers("C.Method2"); + Assert.Equal(2, cMethods2.Length); + + var tree = comp.SyntaxTrees.Single(); + var tree2 = SyntaxFactory.ParseSyntaxTree(source + " "); + + var initializer = tree2.GetRoot().DescendantNodes().OfType().Single(); + + var nameofCalls = getNameOfCalls(tree); + Assert.Equal(2, nameofCalls.Length); + var nameofCalls2 = getNameOfCalls(tree2); + Assert.Equal(2, nameofCalls2.Length); + + var model = comp.GetSemanticModel(tree); + + verify(0, "Method1", cMethods1); + verify(1, "Method2", cMethods2); + + void verify(int index, string expression, ImmutableArray symbols) + { + var argument = nameofCalls[index].ArgumentList.Arguments.Single().Expression; + Assert.Equal(expression, argument.ToString()); + + verifySymbolInfo(CandidateReason.MemberGroup, model.GetSymbolInfo(argument)); + + var argument2 = nameofCalls2[index].ArgumentList.Arguments.Single().Expression; + Assert.Equal(expression, argument2.ToString()); + + Assert.True(model.TryGetSpeculativeSemanticModel(initializer.Position, initializer, out var model2)); + + verifySymbolInfo(CandidateReason.MemberGroup, model2.GetSymbolInfo(argument2)); + + verifySymbolInfo(CandidateReason.OverloadResolutionFailure, model.GetSpeculativeSymbolInfo(argument2.Position, argument2, SpeculativeBindingOption.BindAsExpression)); + + Assert.True(model.GetSpeculativeSymbolInfo(argument2.Position, argument2, SpeculativeBindingOption.BindAsTypeOrNamespace).IsEmpty); + + void verifySymbolInfo(CandidateReason reason, SymbolInfo symbolInfo) + { + Assert.Equal(reason, symbolInfo.CandidateReason); + AssertEx.SetEqual( + symbols.Select(s => s.GetPublicSymbol()), + symbolInfo.CandidateSymbols, + Roslyn.Utilities.ReferenceEqualityComparer.Instance); + } + } + + static ImmutableArray getNameOfCalls(SyntaxTree tree) + { + return tree.GetRoot().DescendantNodes().OfType() + .Where(e => e.Expression is IdentifierNameSyntax { Identifier.ValueText: "nameof" }) + .ToImmutableArray(); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67565")] + public void SymbolInfo_InstanceMemberFromStatic_Nested() + { + var source = """ + public class C + { + public C1 Property { get; } + public C1 Field; + + public static string StaticField = + nameof(Property.Property) + + nameof(Property.Field) + + nameof(Property.Event) + + nameof(Field.Property) + + nameof(Field.Field) + + nameof(Field.Event); + } + + public class C1 + { + public int Property { get; } + public int Field; + public event System.Action Event; + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics(); + + var c1Property = comp.GetMember("C1.Property"); + var c1Field = comp.GetMember("C1.Field"); + var c1Event = comp.GetMember("C1.Event"); + + var tree = comp.SyntaxTrees.Single(); + var tree2 = SyntaxFactory.ParseSyntaxTree(source + " "); + + var initializer = tree2.GetRoot().DescendantNodes().OfType().Single(); + + var nameofCalls = getNameOfCalls(tree); + Assert.Equal(6, nameofCalls.Length); + var nameofCalls2 = getNameOfCalls(tree2); + Assert.Equal(6, nameofCalls2.Length); + + var model = comp.GetSemanticModel(tree); + + verify(0, "Property.Property", c1Property); + verify(1, "Property.Field", c1Field); + verify(2, "Property.Event", c1Event); + verify(3, "Field.Property", c1Property); + verify(4, "Field.Field", c1Field); + verify(5, "Field.Event", c1Event); + + void verify(int index, string expression, Symbol symbol) + { + var argument = nameofCalls[index].ArgumentList.Arguments.Single().Expression; + Assert.Equal(expression, argument.ToString()); + + verifySymbolInfo(model.GetSymbolInfo(argument)); + + var argument2 = nameofCalls2[index].ArgumentList.Arguments.Single().Expression; + Assert.Equal(expression, argument2.ToString()); + + Assert.True(model.TryGetSpeculativeSemanticModel(initializer.Position, initializer, out var model2)); + + verifySymbolInfo(model2.GetSymbolInfo(argument2)); + + verifySymbolInfo(model.GetSpeculativeSymbolInfo(argument2.Position, argument2, SpeculativeBindingOption.BindAsExpression)); + + Assert.True(model.GetSpeculativeSymbolInfo(argument2.Position, argument2, SpeculativeBindingOption.BindAsTypeOrNamespace).IsEmpty); + + void verifySymbolInfo(SymbolInfo symbolInfo) + { + Assert.NotNull(symbolInfo.Symbol); + Assert.Same(symbol.GetPublicSymbol(), symbolInfo.Symbol); + } + } + + static ImmutableArray getNameOfCalls(SyntaxTree tree) + { + return tree.GetRoot().DescendantNodes().OfType() + .Where(e => e.Expression is IdentifierNameSyntax { Identifier.ValueText: "nameof" }) + .ToImmutableArray(); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67565")] + public void SymbolInfo_InstanceMemberFromStatic_Nested_MethodGroup() + { + var source = """ + public class C + { + public C1 Property { get; } + public C1 Field; + public event System.Action Event; + + public static string StaticField = + nameof(Property.Method) + + nameof(Field.Method) + + nameof(Event.Invoke); + } + + public class C1 + { + public void Method() { } + public void Method(int i) { } + } + """; + var comp = CreateCompilation(source).VerifyDiagnostics(); + + var c1Methods = comp.GetMembers("C1.Method").ToArray(); + Assert.Equal(2, c1Methods.Length); + var c1Event = comp.GetMember("C1.Event"); + var actionInvoke = comp.GetWellKnownType(WellKnownType.System_Action).GetMember("Invoke"); + + var tree = comp.SyntaxTrees.Single(); + var tree2 = SyntaxFactory.ParseSyntaxTree(source + " "); + + var initializer = tree2.GetRoot().DescendantNodes().OfType().Single(); + + var nameofCalls = getNameOfCalls(tree); + Assert.Equal(3, nameofCalls.Length); + var nameofCalls2 = getNameOfCalls(tree2); + Assert.Equal(3, nameofCalls2.Length); + + var model = comp.GetSemanticModel(tree); + + verify(0, "Property.Method", c1Methods); + verify(1, "Field.Method", c1Methods); + verify(2, "Event.Invoke", actionInvoke); + + void verify(int index, string expression, params Symbol[] symbols) + { + var argument = nameofCalls[index].ArgumentList.Arguments.Single().Expression; + Assert.Equal(expression, argument.ToString()); + + verifySymbolInfo(CandidateReason.MemberGroup, model.GetSymbolInfo(argument)); + + var argument2 = nameofCalls2[index].ArgumentList.Arguments.Single().Expression; + Assert.Equal(expression, argument2.ToString()); + + Assert.True(model.TryGetSpeculativeSemanticModel(initializer.Position, initializer, out var model2)); + + verifySymbolInfo(CandidateReason.MemberGroup, model2.GetSymbolInfo(argument2)); + + verifySymbolInfo(CandidateReason.OverloadResolutionFailure, model.GetSpeculativeSymbolInfo(argument2.Position, argument2, SpeculativeBindingOption.BindAsExpression)); + + Assert.True(model.GetSpeculativeSymbolInfo(argument2.Position, argument2, SpeculativeBindingOption.BindAsTypeOrNamespace).IsEmpty); + + void verifySymbolInfo(CandidateReason reason, SymbolInfo symbolInfo) + { + Assert.Equal(reason, symbolInfo.CandidateReason); + AssertEx.SetEqual( + symbols.Select(s => s.GetPublicSymbol()), + symbolInfo.CandidateSymbols, + Roslyn.Utilities.ReferenceEqualityComparer.Instance); + } + } + + static ImmutableArray getNameOfCalls(SyntaxTree tree) + { + return tree.GetRoot().DescendantNodes().OfType() + .Where(e => e.Expression is IdentifierNameSyntax { Identifier.ValueText: "nameof" }) + .ToImmutableArray(); + } + } + [Fact] public void ExtensionMethodConstraintFailed() { @@ -1487,5 +1798,580 @@ public void nameof(string x) var option = TestOptions.ReleaseDll; CreateCompilation(source, options: option).VerifyDiagnostics(); } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceInstanceMembersFromStaticMemberInNameof_Flat() + { + var source = @" +System.Console.Write(C.M()); +public class C +{ + public object Property { get; } + public object Field; + public event System.Action Event; + public void M2() { } + public static string M() => nameof(Property) + + "","" + nameof(Field) + + "","" + nameof(Event) + + "","" + nameof(M2) + ; +}"; + var expectedOutput = "Property,Field,Event,M2"; + + CompileAndVerify(source, parseOptions: TestOptions.Regular11, expectedOutput: expectedOutput).VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: expectedOutput).VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: expectedOutput).VerifyDiagnostics(); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceInstanceMembersFromStaticMemberInNameof_Nested() + { + var source = @" +System.Console.Write(C.M()); +public class C +{ + public C1 Property { get; } + public C1 Field; + public event System.Action Event; + public static string M() => nameof(Property.Property) + + "","" + nameof(Property.Field) + + "","" + nameof(Property.Method) + + "","" + nameof(Property.Event) + + "","" + nameof(Field.Property) + + "","" + nameof(Field.Field) + + "","" + nameof(Field.Method) + + "","" + nameof(Field.Event) + + "","" + nameof(Event.Invoke) + ; +} + +public class C1 +{ + public int Property { get; } + public int Field; + public void Method(){} + public event System.Action Event; +}"; + var expectedOutput = "Property,Field,Method,Event,Property,Field,Method,Event,Invoke"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: expectedOutput).VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: expectedOutput).VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (8,40): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public static string M() => nameof(Property.Property) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Property").WithArguments("instance member in 'nameof'").WithLocation(8, 40), + // (9,24): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // + "," + nameof(Property.Field) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Property").WithArguments("instance member in 'nameof'").WithLocation(9, 24), + // (10,24): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // + "," + nameof(Property.Method) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Property").WithArguments("instance member in 'nameof'").WithLocation(10, 24), + // (11,24): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // + "," + nameof(Property.Event) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Property").WithArguments("instance member in 'nameof'").WithLocation(11, 24), + // (12,24): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // + "," + nameof(Field.Property) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Field").WithArguments("instance member in 'nameof'").WithLocation(12, 24), + // (13,24): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // + "," + nameof(Field.Field) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Field").WithArguments("instance member in 'nameof'").WithLocation(13, 24), + // (14,24): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // + "," + nameof(Field.Method) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Field").WithArguments("instance member in 'nameof'").WithLocation(14, 24), + // (15,24): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // + "," + nameof(Field.Event) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Field").WithArguments("instance member in 'nameof'").WithLocation(15, 24), + // (16,24): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // + "," + nameof(Event.Invoke) + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Event").WithArguments("instance member in 'nameof'").WithLocation(16, 24)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void InstanceFromStatic_Lambdas() + { + var source = """ + using System; + Console.Write(C.Names()); + public class C + { + public object Property { get; } + public object Field; + public event Action Event; + public void Method() { } + public static string Names() + { + var lambda1 = static () => nameof(Property); + var lambda2 = static (string f = nameof(Field)) => f; + var lambda3 = static () => nameof(Event.Invoke); + var lambda4 = static (string i = nameof(Event.Invoke)) => i; + return lambda1() + "," + lambda2() + "," + lambda3() + "," + lambda4(); + } + } + """; + var expectedOutput = "Property,Field,Invoke,Invoke"; + + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: expectedOutput).VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: expectedOutput).VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (12,40): error CS8652: The feature 'lambda optional parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var lambda2 = static (string f = nameof(Field)) => f; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "=").WithArguments("lambda optional parameters").WithLocation(12, 40), + // (13,43): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var lambda3 = static () => nameof(Event.Invoke); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Event").WithArguments("instance member in 'nameof'").WithLocation(13, 43), + // (14,40): error CS8652: The feature 'lambda optional parameters' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var lambda4 = static (string i = nameof(Event.Invoke)) => i; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "=").WithArguments("lambda optional parameters").WithLocation(14, 40), + // (14,49): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // var lambda4 = static (string i = nameof(Event.Invoke)) => i; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Event").WithArguments("instance member in 'nameof'").WithLocation(14, 49)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void InstanceFromStatic_LocalFunctions() + { + var source = """ + using System; + Console.Write(C.Names()); + public class C + { + public object Property { get; } + public object Field; + public event Action Event; + public void Method() { } + public static string Names() + { + static string local1() => nameof(Property); + static string local2(string f = nameof(Field)) => f; + static string local3() => nameof(Event.Invoke); + static string local4(string i = nameof(Event.Invoke)) => i; + return local1() + "," + local2() + "," + local3() + "," + local4(); + } + } + """; + var expectedOutput = "Property,Field,Invoke,Invoke"; + + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: expectedOutput).VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: expectedOutput).VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (13,42): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // static string local3() => nameof(Event.Invoke); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Event").WithArguments("instance member in 'nameof'").WithLocation(13, 42), + // (14,48): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // static string local4(string i = nameof(Event.Invoke)) => i; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Event").WithArguments("instance member in 'nameof'").WithLocation(14, 48)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceInstanceMembersFromFieldInitializerInNameof() + { + var source = @" +System.Console.Write(new C().S); +public class C +{ + public string S { get; } = nameof(S.Length); +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "Length").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "Length").VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (5,39): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public string S { get; } = nameof(S.Length); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "S").WithArguments("instance member in 'nameof'").WithLocation(5, 39)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceInstanceMembersFromAttributeInNameof() + { + var source = @" +var p = new C().P; // 1 +public class C +{ + [System.Obsolete(nameof(S.Length))] + public int P { get; } + public string S { get; } +}"; + var expectedDiagnostics = new[] + { + // (2,9): warning CS0618: 'C.P' is obsolete: 'Length' + // var p = new C().P; // 1 + Diagnostic(ErrorCode.WRN_DeprecatedSymbolStr, "new C().P").WithArguments("C.P", "Length").WithLocation(2, 9) + }; + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (5,29): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // [System.Obsolete(nameof(S.Length))] + Diagnostic(ErrorCode.ERR_FeatureInPreview, "S").WithArguments("instance member in 'nameof'").WithLocation(5, 29)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceInstanceMembersFromConstructorInitializersInNameof() + { + var source = @" +System.Console.WriteLine(new C().S); +public class C +{ + public C(string s){ S = s; } + public C() : this(nameof(S.Length)){} + public string S { get; } +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "Length").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "Length").VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (6,30): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public C() : this(nameof(S.Length)){} + Diagnostic(ErrorCode.ERR_FeatureInPreview, "S").WithArguments("instance member in 'nameof'").WithLocation(6, 30)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanAccessStructInstancePropertyInLambdaInNameof() + { + var source = @" +using System; + +string s = ""str""; +new S().M(ref s); + +public struct S +{ + public string P { get; } + public void M(ref string x) + { + Func func = () => nameof(P.Length); + Console.WriteLine(func()); + } +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "Length").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "Length").VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (12,42): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // Func func = () => nameof(P.Length); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "P").WithArguments("instance member in 'nameof'").WithLocation(12, 42)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceStaticMembersFromInstanceMemberInNameof1() + { + var source = @" +System.Console.WriteLine(new C().M()); +public class C +{ + public C Prop { get; } + public static int StaticProp { get; } + public string M() => nameof(Prop.StaticProp); +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "StaticProp").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "StaticProp").VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (7,33): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public string M() => nameof(Prop.StaticProp); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Prop.StaticProp").WithArguments("instance member in 'nameof'").WithLocation(7, 33)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceStaticMembersFromInstanceMemberInNameof2() + { + var source = @" +System.Console.WriteLine(C.M()); +public class C +{ + public C Prop { get; } + public static int StaticProp { get; } + public static string M() => nameof(Prop.StaticProp); +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "StaticProp").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "StaticProp").VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (7,40): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public static string M() => nameof(Prop.StaticProp); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Prop").WithArguments("instance member in 'nameof'").WithLocation(7, 40), + // (7,40): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public static string M() => nameof(Prop.StaticProp); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Prop.StaticProp").WithArguments("instance member in 'nameof'").WithLocation(7, 40)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceStaticMembersFromInstanceMemberInNameof3() + { + var source = @" +System.Console.WriteLine(C.M()); +public class C +{ + public C Prop { get; } + public static string M() => nameof(Prop.M); +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "M").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "M").VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (6,40): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public static string M() => nameof(Prop.M); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Prop").WithArguments("instance member in 'nameof'").WithLocation(6, 40)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceStaticMembersFromInstanceMemberInNameof4() + { + var source = @" +System.Console.WriteLine(new C().M()); +public class C +{ + public C Prop { get; } + public static void StaticMethod(){} + public string M() => nameof(Prop.StaticMethod); +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "StaticMethod").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "StaticMethod").VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics(); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCannotReferenceInstanceMembersFromStaticMemberInNameofInCSharp11() + { + var source = @" +public class C +{ + public string S { get; } + public static string M() => nameof(S.Length); +}"; + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (5,40): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public static string M() => nameof(S.Length); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "S").WithArguments("instance member in 'nameof'").WithLocation(5, 40)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCannotReferenceInstanceMembersFromFieldInitializerInNameofInCSharp11() + { + var source = @" +public class C +{ + public string S { get; } = nameof(S.Length); +}"; + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (4,39): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public string S { get; } = nameof(S.Length); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "S").WithArguments("instance member in 'nameof'").WithLocation(4, 39)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCannotReferenceInstanceMembersFromAttributeInNameofInCSharp11() + { + var source = @" +public class C +{ + [System.Obsolete(nameof(S.Length))] + public int P { get; } + public string S { get; } +}"; + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (4,29): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // [System.Obsolete(nameof(S.Length))] + Diagnostic(ErrorCode.ERR_FeatureInPreview, "S").WithArguments("instance member in 'nameof'").WithLocation(4, 29)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCannotReferenceInstanceMembersFromConstructorInitializersInNameofInCSharp11() + { + var source = @" +public class C +{ + public C(string s){} + public C() : this(nameof(S.Length)){} + public string S { get; } +}"; + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (5,30): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public C() : this(nameof(S.Length)){} + Diagnostic(ErrorCode.ERR_FeatureInPreview, "S").WithArguments("instance member in 'nameof'").WithLocation(5, 30)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCannotAccessStructInstancePropertyInLambdaInNameofInCSharp11() + { + var source = @" +using System; + +public struct S +{ + public string P { get; } + public void M(ref string x) + { + Func func = () => nameof(P.Length); + } +}"; + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (9,42): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // Func func = () => nameof(P.Length); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "P").WithArguments("instance member in 'nameof'").WithLocation(9, 42)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCannotReferenceStaticPropertyFromInstanceMemberInNameofInCSharp11() + { + var source = @" +public class C +{ + public C Prop { get; } + public static int StaticProp { get; } + public string M() => nameof(Prop.StaticProp); +}"; + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (6,33): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // public string M() => nameof(Prop.StaticProp); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Prop.StaticProp").WithArguments("instance member in 'nameof'").WithLocation(6, 33)); + } + + [Fact] + public void TestCanReferenceStaticMethodFromInstanceMemberInNameofInCSharp11() + { + var source = @" +System.Console.WriteLine(new C().M()); +public class C +{ + public C Prop { get; } + public static void StaticMethod(){} + public string M() => nameof(Prop.StaticMethod); +}"; + CompileAndVerify(source, parseOptions: TestOptions.Regular11, expectedOutput: "StaticMethod").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "StaticMethod").VerifyDiagnostics(); + } + + [Fact] + public void TestCanAccessRefParameterInLambdaInNameof() + { + var source = @" +using System; + +string s = ""str""; +new S().M(ref s); + +public struct S +{ + public void M(ref string x) + { + Func func = () => nameof(x.Length); + Console.WriteLine(func()); + } +}"; + CompileAndVerify(source, parseOptions: TestOptions.Regular11, expectedOutput: "Length").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "Length").VerifyDiagnostics(); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceStaticMembersFromInstanceMemberInNameofUsedRecursivelyInAttributes1() + { + var source = @" +using System; +using System.Reflection; +Console.WriteLine(typeof(C).GetProperty(""Prop"").GetCustomAttribute().S); +class C +{ + [Attr(nameof(Prop.StaticMethod))] + public C Prop { get; } + public static void StaticMethod(){} +} +class Attr : Attribute +{ + public readonly string S; + public Attr(string s) { S = s; } +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "StaticMethod").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "StaticMethod").VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (7,18): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // [Attr(nameof(Prop.StaticMethod))] + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Prop").WithArguments("instance member in 'nameof'").WithLocation(7, 18)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceStaticMembersFromInstanceMemberInNameofUsedRecursivelyInAttributes2() + { + var source = @" +using System; +using System.Reflection; +Console.WriteLine(typeof(C).GetProperty(""Prop"").GetCustomAttribute().S); +class C +{ + [Attr(nameof(Prop.Prop))] + public static C Prop { get; } +} +class Attr : Attribute +{ + public readonly string S; + public Attr(string s) { S = s; } +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "Prop").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "Prop").VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (7,18): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // [Attr(nameof(Prop.Prop))] + Diagnostic(ErrorCode.ERR_FeatureInPreview, "Prop.Prop").WithArguments("instance member in 'nameof'").WithLocation(7, 18)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestCanReferenceStaticMembersFromInstanceMemberInNameofUsedRecursivelyInAttributes3() + { + var source = @" +using System; +using System.Reflection; +Console.WriteLine(typeof(C).GetCustomAttribute().S); +[Attr(nameof(C.Prop.Prop))] +class C +{ + public static C Prop { get; } +} +class Attr : Attribute +{ + public readonly string S; + public Attr(string s) { S = s; } +}"; + CompileAndVerify(source, parseOptions: TestOptions.RegularNext, expectedOutput: "Prop").VerifyDiagnostics(); + CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: "Prop").VerifyDiagnostics(); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics( + // (5,14): error CS8652: The feature 'instance member in 'nameof'' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // [Attr(nameof(C.Prop.Prop))] + Diagnostic(ErrorCode.ERR_FeatureInPreview, "C.Prop.Prop").WithArguments("instance member in 'nameof'").WithLocation(5, 14)); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestInvalidRecursiveUsageOfNameofInAttributesDoesNotCrashCompiler1() + { + var source = @" +class C +{ + [Attr(nameof(Method().Method))] + T Method() where T : C => default; +} +class Attr : System.Attribute { public Attr(string s) {} }"; + var expectedDiagnostics = new[] + { + // (4,18): error CS0411: The type arguments for method 'C.Method()' cannot be inferred from the usage. Try specifying the type arguments explicitly. + // [Attr(nameof(Method().Method))] + Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Method").WithArguments("C.Method()").WithLocation(4, 18) + }; + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics(expectedDiagnostics); + } + + [Fact, WorkItem(40229, "https://github.com/dotnet/roslyn/issues/40229")] + public void TestInvalidRecursiveUsageOfNameofInAttributesDoesNotCrashCompiler2() + { + var source = @" +class C +{ + [Attr(nameof(Method().Method))] + T Method() where T : C => default; +} +class Attr : System.Attribute { public Attr(string s) {} }"; + var expectedDiagnostics = new[] + { + // (4,18): error CS8082: Sub-expression cannot be used in an argument to nameof. + // [Attr(nameof(Method().Method))] + Diagnostic(ErrorCode.ERR_SubexpressionNotInNameof, "Method()").WithLocation(4, 18) + }; + CreateCompilation(source, parseOptions: TestOptions.RegularNext).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); + CreateCompilation(source, parseOptions: TestOptions.Regular11).VerifyDiagnostics(expectedDiagnostics); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/ObjectAndCollectionInitializerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/ObjectAndCollectionInitializerTests.cs index 61075f1fb3320..764b90baddebe 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/ObjectAndCollectionInitializerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/ObjectAndCollectionInitializerTests.cs @@ -3510,7 +3510,7 @@ where node.IsKind(SyntaxKind.CollectionInitializerExpression) Assert.Equal(2, symbolInfo.CandidateSymbols.Length); Assert.Equal(new[] {"void X.Add(System.Collections.Generic.List x)", "void X.Add(X x)"}, - Roslyn.Utilities.EnumerableExtensions.Order(symbolInfo.CandidateSymbols.Select(s => s.ToTestDisplayString())).ToArray()); + symbolInfo.CandidateSymbols.Select(s => s.ToTestDisplayString()).Order().ToArray()); } [WorkItem(529787, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/529787")] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PrimaryConstructorTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PrimaryConstructorTests.cs index 28654bc28f2b7..e3905450d2eff 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PrimaryConstructorTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PrimaryConstructorTests.cs @@ -3064,6 +3064,370 @@ public static void Main() comp.VerifyDiagnostics(); } + [Theory] + [CombinatorialData] + public void AttributesOnPrimaryConstructor_01([CombinatorialValues("class", "struct", "record", "record class", "record struct")] string declaration) + { + string source = @" +[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = true) ] +public class A : System.Attribute +{ +} + +[method: A] +" + declaration + @" C + (); +"; + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview); + comp.VerifyDiagnostics(); + verify(comp); + + comp = CreateCompilation(source, parseOptions: TestOptions.RegularNext); + comp.VerifyDiagnostics(); + verify(comp); + + comp = CreateCompilation(source, parseOptions: TestOptions.Regular11); + + if (declaration is "class" or "struct") + { + comp.VerifyDiagnostics( + // (9,5): error CS8652: The feature 'primary constructors' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // (); + Diagnostic(ErrorCode.ERR_FeatureInPreview, "()").WithArguments("primary constructors").WithLocation(9, 5) + ); + } + else + { + comp.VerifyDiagnostics( + // (7,2): error CS8652: The feature 'primary constructors' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // [method: A] + Diagnostic(ErrorCode.ERR_FeatureInPreview, "method").WithArguments("primary constructors").WithLocation(7, 2) + ); + } + + verify(comp); + + static void verify(CSharpCompilation comp) + { + var c = (SourceNamedTypeSymbol)comp.GetTypeByMetadataName("C"); + Assert.Empty(c.GetAttributes()); + Assert.True(c.HasPrimaryConstructor); + Assert.Equal("A", c.PrimaryConstructor.GetAttributes().Single().ToString()); + Assert.True(c.Constructors.Where(ctor => ctor != c.PrimaryConstructor).All(ctor => ctor.GetAttributes().IsEmpty)); + } + } + + [Theory] + [CombinatorialData] + public void AttributesOnPrimaryConstructor_02([CombinatorialValues("class C();", "struct C();", "record C();", "record class C();", "record struct C();")] string declaration) + { + string source = @" +[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = true) ] +public class A : System.Attribute +{ +} + +[return: A] +" + declaration + @" +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,2): warning CS0657: 'return' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'type, method'. All attributes in this block will be ignored. + // [return: A] + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "return").WithArguments("return", "type, method").WithLocation(7, 2) + ); + + var c = comp.GetTypeByMetadataName("C"); + Assert.Empty(c.GetAttributes()); + Assert.True(c.Constructors.All(ctor => ctor.GetAttributes().IsEmpty)); + Assert.True(c.Constructors.All(ctor => ctor.GetReturnTypeAttributes().IsEmpty)); + } + + [Fact] + public void AttributesOnPrimaryConstructor_03() + { + string source = @" +[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = true) ] +public class A : System.Attribute +{ +} + +[method: A] +[return: A] +interface I(); +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,2): warning CS0657: 'method' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'type'. All attributes in this block will be ignored. + // [method: A] + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "method").WithArguments("method", "type").WithLocation(7, 2), + // (8,2): warning CS0657: 'return' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'type'. All attributes in this block will be ignored. + // [return: A] + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "return").WithArguments("return", "type").WithLocation(8, 2), + // (9,12): error CS9122: Unexpected parameter list. + // interface I(); + Diagnostic(ErrorCode.ERR_UnexpectedParameterList, "()").WithLocation(9, 12) + ); + + var i = (SourceNamedTypeSymbol)comp.GetTypeByMetadataName("I"); + Assert.Empty(i.GetAttributes()); + Assert.False(i.HasPrimaryConstructor); + Assert.Null(i.PrimaryConstructor); + Assert.Empty(i.Constructors); + } + + [Fact] + public void AttributesOnPrimaryConstructor_04() + { + string source = @" +[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = true) ] +public class A : System.Attribute +{ +} + +[method: A] +[return: A] +enum E(); +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,2): warning CS0657: 'method' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'type'. All attributes in this block will be ignored. + // [method: A] + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "method").WithArguments("method", "type").WithLocation(7, 2), + // (8,2): warning CS0657: 'return' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'type'. All attributes in this block will be ignored. + // [return: A] + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "return").WithArguments("return", "type").WithLocation(8, 2), + // (9,7): error CS1514: { expected + // enum E(); + Diagnostic(ErrorCode.ERR_LbraceExpected, "(").WithLocation(9, 7), + // (9,7): error CS1513: } expected + // enum E(); + Diagnostic(ErrorCode.ERR_RbraceExpected, "(").WithLocation(9, 7), + // (9,7): error CS8803: Top-level statements must precede namespace and type declarations. + // enum E(); + Diagnostic(ErrorCode.ERR_TopLevelStatementAfterNamespaceOrType, "();").WithLocation(9, 7), + // (9,8): error CS1525: Invalid expression term ')' + // enum E(); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(9, 8) + ); + + var e = (SourceNamedTypeSymbol)comp.GetTypeByMetadataName("E"); + Assert.Empty(e.GetAttributes()); + Assert.False(e.HasPrimaryConstructor); + Assert.Null(e.PrimaryConstructor); + Assert.True(e.Constructors.All(ctor => ctor.GetAttributes().IsEmpty)); + Assert.True(e.Constructors.All(ctor => ctor.GetReturnTypeAttributes().IsEmpty)); + } + + [Theory] + [CombinatorialData] + public void AttributesOnPrimaryConstructor_05([CombinatorialValues("class C;", "struct C;", "record C;", "record class C;", "record struct C;", "interface C;", "enum C;")] string declaration) + { + string source = @" +[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = true) ] +public class A : System.Attribute +{ +} + +[method: A] +[return: A] +" + declaration + @" +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (7,2): warning CS0657: 'method' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'type'. All attributes in this block will be ignored. + // [method: A] + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "method").WithArguments("method", "type").WithLocation(7, 2), + // (8,2): warning CS0657: 'return' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'type'. All attributes in this block will be ignored. + // [return: A] + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "return").WithArguments("return", "type").WithLocation(8, 2) + ); + + var c = (SourceNamedTypeSymbol)comp.GetTypeByMetadataName("C"); + Assert.Empty(c.GetAttributes()); + Assert.False(c.HasPrimaryConstructor); + Assert.Null(c.PrimaryConstructor); + Assert.True(c.Constructors.All(ctor => ctor.GetAttributes().IsEmpty)); + Assert.True(c.Constructors.All(ctor => ctor.GetReturnTypeAttributes().IsEmpty)); + } + + [Theory] + [CombinatorialData] + public void AttributesOnPrimaryConstructor_06([CombinatorialValues("class C();", "struct C();", "record C();", "record class C();", "record struct C();")] string declaration) + { + string source = @" +[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = true) ] +public class A : System.Attribute +{ +} + +[type: A] +" + declaration + @" +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var c = comp.GetTypeByMetadataName("C"); + Assert.Equal("A", c.GetAttributes().Single().ToString()); + Assert.True(c.Constructors.All(ctor => ctor.GetAttributes().IsEmpty)); + } + + [Theory] + [CombinatorialData] + public void AttributesOnPrimaryConstructor_07([CombinatorialValues("class C();", "struct C();", "record C();", "record class C();", "record struct C();")] string declaration) + { + string source = @" +[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = true) ] +public class A : System.Attribute +{ +} + +[A] +" + declaration + @" +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var c = comp.GetTypeByMetadataName("C"); + Assert.Equal("A", c.GetAttributes().Single().ToString()); + Assert.True(c.Constructors.All(ctor => ctor.GetAttributes().IsEmpty)); + } + + [Theory] + [CombinatorialData] + public void AttributesOnPrimaryConstructor_08([CombinatorialValues("class", "struct", "record", "record class", "record struct")] string declaration) + { + string source = @" +[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = true) ] +public class A : System.Attribute +{ +} + +[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = true) ] +public class B : System.Attribute +{ +} + +[method: A] +partial " + declaration + @" C1(); + +[method: B] +partial " + declaration + @" C1; + +[method: B] +partial " + declaration + @" C2; + +[method: A] +partial " + declaration + @" C2(); +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (15,2): warning CS0657: 'method' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'type'. All attributes in this block will be ignored. + // [method: B] + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "method").WithArguments("method", "type").WithLocation(15, 2), + // (18,2): warning CS0657: 'method' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'type'. All attributes in this block will be ignored. + // [method: B] + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "method").WithArguments("method", "type").WithLocation(18, 2) + ); + + var c1 = (SourceNamedTypeSymbol)comp.GetTypeByMetadataName("C1"); + Assert.Empty(c1.GetAttributes()); + Assert.True(c1.HasPrimaryConstructor); + Assert.Equal("A", c1.PrimaryConstructor.GetAttributes().Single().ToString()); + Assert.True(c1.Constructors.Where(ctor => ctor != c1.PrimaryConstructor).All(ctor => ctor.GetAttributes().IsEmpty)); + + var c2 = (SourceNamedTypeSymbol)comp.GetTypeByMetadataName("C2"); + Assert.Empty(c2.GetAttributes()); + Assert.True(c2.HasPrimaryConstructor); + Assert.Equal("A", c2.PrimaryConstructor.GetAttributes().Single().ToString()); + Assert.True(c2.Constructors.Where(ctor => ctor != c2.PrimaryConstructor).All(ctor => ctor.GetAttributes().IsEmpty)); + } + + [Theory] + [CombinatorialData] + public void AttributesOnPrimaryConstructor_09([CombinatorialValues("class", "struct", "record", "record class", "record struct")] string declaration) + { + string source = @" +[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = true) ] +public class A : System.Attribute +{ +} + +[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = true) ] +public class B : System.Attribute +{ +} + +[method: A] +partial " + declaration + @" C1(); + +#line 100 +[method: B] +partial " + declaration + @" C1 +#line 200 + (); + +[method: B] +partial " + declaration + @" C2(); + +#line 300 +[method: A] +partial " + declaration + @" C2 +#line 400 + (); +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // (100,2): warning CS0657: 'method' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'type'. All attributes in this block will be ignored. + // [method: B] + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "method").WithArguments("method", "type").WithLocation(100, 2), + // (200,5): error CS8863: Only a single partial type declaration may have a parameter list + // (); + Diagnostic(ErrorCode.ERR_MultipleRecordParameterLists, "()").WithLocation(200, 5), + // (300,2): warning CS0657: 'method' is not a valid attribute location for this declaration. Valid attribute locations for this declaration are 'type'. All attributes in this block will be ignored. + // [method: A] + Diagnostic(ErrorCode.WRN_AttributeLocationOnBadDeclaration, "method").WithArguments("method", "type").WithLocation(300, 2), + // (400,5): error CS8863: Only a single partial type declaration may have a parameter list + // (); + Diagnostic(ErrorCode.ERR_MultipleRecordParameterLists, "()").WithLocation(400, 5) + ); + + var c1 = (SourceNamedTypeSymbol)comp.GetTypeByMetadataName("C1"); + Assert.Empty(c1.GetAttributes()); + Assert.True(c1.HasPrimaryConstructor); + Assert.Equal("A", c1.PrimaryConstructor.GetAttributes().Single().ToString()); + Assert.True(c1.Constructors.Where(ctor => ctor != c1.PrimaryConstructor).All(ctor => ctor.GetAttributes().IsEmpty)); + + var c2 = (SourceNamedTypeSymbol)comp.GetTypeByMetadataName("C2"); + Assert.Empty(c2.GetAttributes()); + Assert.True(c2.HasPrimaryConstructor); + Assert.Equal("B", c2.PrimaryConstructor.GetAttributes().Single().ToString()); + Assert.True(c2.Constructors.Where(ctor => ctor != c2.PrimaryConstructor).All(ctor => ctor.GetAttributes().IsEmpty)); + } + + [Fact] + public void AttributesOnPrimaryConstructor_10_NameofParameter() + { + string source = @" +[System.AttributeUsage(System.AttributeTargets.All, AllowMultiple = true) ] +public class A : System.Attribute +{ + public A(string x){} +} + +[method: A(nameof(someParam))] +class C(int someParam) +{ + int X = someParam; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + var c = (SourceNamedTypeSymbol)comp.GetTypeByMetadataName("C"); + Assert.Equal(@"A(""someParam"")", c.PrimaryConstructor.GetAttributes().Single().ToString()); + } + [Fact] public void AnalyzerActions_01_Class() { @@ -15430,5 +15794,279 @@ public S3(object o) : this() Diagnostic(ErrorCode.ERR_RecordStructConstructorCallsDefaultConstructor, "this").WithLocation(6, 27) ); } + + [Fact] + public void StructLayout_01() + { + string source = @" +using System.Runtime.InteropServices; + +[StructLayout(LayoutKind.Explicit)] +struct S(int x, int y) +{ + int X = x; + int Y => y; + + [FieldOffset(8)] + int Z = 0; +} +"; + var comp = CreateCompilation(source); + comp.VerifyDiagnostics( + // We might want to adjust the warning depending on what we decide to do for + // https://github.com/dotnet/csharplang/blob/main/proposals/primary-constructors.md#field-targeting-attributes-for-captured-primary-constructor-parameters. + // + // If we decide to support attributes for capture fields, consider testing + // ERR_MarshalUnmanagedTypeNotValidForFields + // ERR_StructOffsetOnBadStruct + // ERR_DoNotUseFixedBufferAttr + + // (5,21): error CS0625: 'S.P': instance field in types marked with StructLayout(LayoutKind.Explicit) must have a FieldOffset attribute + // struct S(int x, int y) + Diagnostic(ErrorCode.ERR_MissingStructOffset, "y").WithArguments("S.P").WithLocation(5, 21), + // (7,9): error CS0625: 'S.X': instance field in types marked with StructLayout(LayoutKind.Explicit) must have a FieldOffset attribute + // int X = x; + Diagnostic(ErrorCode.ERR_MissingStructOffset, "X").WithArguments("S.X").WithLocation(7, 9) + ); + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/67162")] + public void StructLayout_02() + { + string source1 = @" +public partial struct S(int x) +{ + int X => x; +} +"; + string source2 = @" +public partial struct S +{ + public int Y; +} +"; + verify1(source1, source2, validate2); + verify1(source1 + source2, "", validate2); + verify1(source2, source1, validate3); + verify1(source2 + source1, "", validate3); + + void verify1(string source1, string source2, Action validator) + { + var comp = CreateCompilation(new[] { source1, source2 }); + CompileAndVerify(comp, symbolValidator: validator, sourceSymbolValidator: validator).VerifyDiagnostics( + // 0.cs(2,23): warning CS0282: There is no defined ordering between fields in multiple declarations of partial struct 'S'. To specify an ordering, all instance fields must be in the same declaration. + // public partial struct S(int x) + Diagnostic(ErrorCode.WRN_SequentialOnPartialClass, "S").WithArguments("S").WithLocation(2, 23) + ); + } + + void validate2(ModuleSymbol m) + { + var fields = m.GlobalNamespace.GetTypeMember("S").GetMembers().OfType().ToArray(); + Assert.Equal(2, fields.Length); + Assert.Equal("P", fields[0].Name); + Assert.Equal("Y", fields[1].Name); + } + + void validate3(ModuleSymbol m) + { + var fields = m.GlobalNamespace.GetTypeMember("S").GetMembers().OfType().ToArray(); + Assert.Equal(2, fields.Length); + Assert.Equal("Y", fields[0].Name); + Assert.Equal("P", fields[1].Name); + } + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/67162")] + public void StructLayout_03() + { + string source1 = @" +public partial struct S(int x) +{ +} +"; + string source2 = @" +public partial struct S +{ + int X = x; +} +"; + verify1(source1, source2, validate2); + verify1(source1 + source2, "", validate2); + verify1(source2, source1, validate2); + verify1(source2 + source1, "", validate2); + + void verify1(string source1, string source2, Action validator) + { + var comp = CreateCompilation(new[] { source1, source2 }); + CompileAndVerify(comp, symbolValidator: validator, sourceSymbolValidator: validator).VerifyDiagnostics(); + } + + void validate2(ModuleSymbol m) + { + Assert.Equal(1, m.GlobalNamespace.GetTypeMember("S").GetMembers().OfType().Count()); + } + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/67162")] + public void StructLayout_04() + { + string source1 = @" +public partial struct S(int x) +{ + int X => x; + public int Y; +} +"; + string source2 = @" +public partial struct S +{ +} +"; + verify1(source1, source2, validate2); + verify1(source1 + source2, "", validate2); + verify1(source2, source1, validate2); + verify1(source2 + source1, "", validate2); + + void verify1(string source1, string source2, Action validator) + { + var comp = CreateCompilation(new[] { source1, source2 }); + CompileAndVerify(comp, symbolValidator: validator, sourceSymbolValidator: validator).VerifyDiagnostics(); + } + + void validate2(ModuleSymbol m) + { + var fields = m.GlobalNamespace.GetTypeMember("S").GetMembers().OfType().ToArray(); + Assert.Equal(2, fields.Length); + Assert.Equal("P", fields[0].Name); + Assert.Equal("Y", fields[1].Name); + } + } + + [Fact] + [WorkItem("https://github.com/dotnet/roslyn/issues/67162")] + public void StructLayout_05() + { + string source1 = @" +public partial struct S(int x) +{ + int X => x; +} +"; + string source2 = @" +public partial struct S +{ + public int Y; +} +"; + string source3 = @" +public partial struct S +{ + public int Z; +} +"; + verify1(source1, source2, source3); + verify1(source1 + source2, source3, ""); + verify1(source1 + source2 + source3, "", ""); + + verify1(source1, source3, source2); + verify1(source1 + source3, source2, ""); + verify1(source1 + source3 + source2, "", ""); + + verify1(source2, source1, source3); + verify1(source2 + source1, source3, ""); + verify1(source2 + source1 + source3, "", ""); + + verify1(source2, source3, source1); + verify1(source2 + source3, source1, ""); + verify1(source2 + source3 + source1, "", ""); + + verify1(source3, source1, source2); + verify1(source3 + source1, source2, ""); + verify1(source3 + source1 + source2, "", ""); + + verify1(source3, source2, source1); + verify1(source3 + source2, source1, ""); + verify1(source3 + source2 + source1, "", ""); + + void verify1(string source1, string source2, string source3) + { + var comp = CreateCompilation(new[] { source1, source2, source3 }); + comp.VerifyDiagnostics( + // 0.cs(2,23): warning CS0282: There is no defined ordering between fields in multiple declarations of partial struct 'S'. To specify an ordering, all instance fields must be in the same declaration. + // public partial struct S(int x) + Diagnostic(ErrorCode.WRN_SequentialOnPartialClass, "S").WithArguments("S").WithLocation(2, 23) + ); + } + } + + [Theory] + [CombinatorialData] + public void RestrictedType_01([CombinatorialValues("class", "struct")] string declaration) + { + var src1 = @" +" + declaration + @" C1 +(System.ArgIterator a) +{ + void M() + { + _ = a; + } +} + +" + declaration + @" C2 +(System.ArgIterator b) +{ + void M() + { + System.Action d = () => _ = b; + } +} + +" + declaration + @" C3 +(System.ArgIterator c) +{ + System.Action d = () => _ = c; +} + +#pragma warning disable CS" + UnreadParameterWarning() + @" // Parameter 'z' is unread. +" + declaration + @" C4(System.ArgIterator z) +{ +} +"; + var comp = CreateCompilation(src1, targetFramework: TargetFramework.DesktopLatestExtended); + comp.VerifyDiagnostics( + // (7,13): error CS9136: Cannot use primary constructor parameter of type 'ArgIterator' inside an instance member + // _ = a; + Diagnostic(ErrorCode.ERR_UnsupportedPrimaryConstructorParameterCapturingRefAny, "a").WithArguments("System.ArgIterator").WithLocation(7, 13), + // (16,37): error CS4013: Instance of type 'ArgIterator' cannot be used inside a nested function, query expression, iterator block or async method + // System.Action d = () => _ = b; + Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "b").WithArguments("System.ArgIterator").WithLocation(16, 37), + // (23,33): error CS4013: Instance of type 'ArgIterator' cannot be used inside a nested function, query expression, iterator block or async method + // System.Action d = () => _ = c; + Diagnostic(ErrorCode.ERR_SpecialByRefInLambda, "c").WithArguments("System.ArgIterator").WithLocation(23, 33) + ); + } + + [Theory] + [CombinatorialData] + public void RestrictedType_02([CombinatorialValues("record", "record class", "record struct")] string keyword) + { + var src1 = @" +" + keyword + @" C1 +(System.ArgIterator x) +{ +} +"; + var comp = CreateCompilation(src1, targetFramework: TargetFramework.NetCoreApp); + comp.VerifyDiagnostics( + // (3,2): error CS0610: Field or property cannot be of type 'ArgIterator' + // (System.ArgIterator x) + Diagnostic(ErrorCode.ERR_FieldCantBeRefAny, "System.ArgIterator").WithArguments("System.ArgIterator").WithLocation(3, 2) + ); + } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index f35bd414f7716..fe6c672902c0a 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -13102,18 +13102,6 @@ static void MT2() where T : I1 targetFramework: _supportingFramework); compilation1.VerifyDiagnostics( - // (14,20): error CS0176: Member 'I1.P01' cannot be accessed with an instance reference; qualify it with a type name instead - // _ = nameof(this.P01); - Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.P01").WithArguments("I1.P01").WithLocation(14, 20), - // (15,20): error CS0176: Member 'I1.P04' cannot be accessed with an instance reference; qualify it with a type name instead - // _ = nameof(this.P04); - Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.P04").WithArguments("I1.P04").WithLocation(15, 20), - // (28,20): error CS0176: Member 'I1.P01' cannot be accessed with an instance reference; qualify it with a type name instead - // _ = nameof(x.P01); - Diagnostic(ErrorCode.ERR_ObjectProhibited, "x.P01").WithArguments("I1.P01").WithLocation(28, 20), - // (30,20): error CS0176: Member 'I1.P04' cannot be accessed with an instance reference; qualify it with a type name instead - // _ = nameof(x.P04); - Diagnostic(ErrorCode.ERR_ObjectProhibited, "x.P04").WithArguments("I1.P04").WithLocation(30, 20), // (35,20): error CS0704: Cannot do non-virtual member lookup in 'T' because it is a type parameter // _ = nameof(T.P03); Diagnostic(ErrorCode.ERR_LookupInTypeVariable, "T").WithArguments("T").WithLocation(35, 20), @@ -13984,18 +13972,6 @@ static void MT2() where T : I1 targetFramework: _supportingFramework); compilation1.VerifyDiagnostics( - // (14,20): error CS0176: Member 'I1.P01' cannot be accessed with an instance reference; qualify it with a type name instead - // _ = nameof(this.P01); - Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.P01").WithArguments("I1.P01").WithLocation(14, 20), - // (15,20): error CS0176: Member 'I1.P04' cannot be accessed with an instance reference; qualify it with a type name instead - // _ = nameof(this.P04); - Diagnostic(ErrorCode.ERR_ObjectProhibited, "this.P04").WithArguments("I1.P04").WithLocation(15, 20), - // (28,20): error CS0176: Member 'I1.P01' cannot be accessed with an instance reference; qualify it with a type name instead - // _ = nameof(x.P01); - Diagnostic(ErrorCode.ERR_ObjectProhibited, "x.P01").WithArguments("I1.P01").WithLocation(28, 20), - // (30,20): error CS0176: Member 'I1.P04' cannot be accessed with an instance reference; qualify it with a type name instead - // _ = nameof(x.P04); - Diagnostic(ErrorCode.ERR_ObjectProhibited, "x.P04").WithArguments("I1.P04").WithLocation(30, 20), // (35,20): error CS0704: Cannot do non-virtual member lookup in 'T' because it is a type parameter // _ = nameof(T.P03); Diagnostic(ErrorCode.ERR_LookupInTypeVariable, "T").WithArguments("T").WithLocation(35, 20), diff --git a/src/Compilers/CSharp/csc/AnyCpu/csc.csproj b/src/Compilers/CSharp/csc/AnyCpu/csc.csproj index f159446c495ec..621378e9e9089 100644 --- a/src/Compilers/CSharp/csc/AnyCpu/csc.csproj +++ b/src/Compilers/CSharp/csc/AnyCpu/csc.csproj @@ -3,7 +3,7 @@ Exe - $(SourceBuildTargetFrameworksNetFx) + $(SourceBuildToolsetTargetFrameworks);net472 false true diff --git a/src/Compilers/Core/CodeAnalysisTest/Collections/TopologicalSortTests.cs b/src/Compilers/Core/CodeAnalysisTest/Collections/TopologicalSortTests.cs index 8531686f0d9b0..f17755162b283 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Collections/TopologicalSortTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Collections/TopologicalSortTests.cs @@ -2,13 +2,12 @@ // 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; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Shared.Collections; using Roslyn.Test.Utilities; using Xunit; @@ -16,6 +15,12 @@ namespace Microsoft.CodeAnalysis.UnitTests.Collections { public class TopologicalSortTests { + private TopologicalSortAddSuccessors GetAddSuccessorsFunction(int[][] successors) + => GetAddSuccessorsFunction(successors, i => i); + + private static TopologicalSortAddSuccessors GetAddSuccessorsFunction(T[][] successors, Func toInt) + => (ref TemporaryArray builder, T value) => builder.AddRange(successors[toInt(value)].ToImmutableArray()); + [Fact] public void Test01() { @@ -29,8 +34,8 @@ public void Test01() /* 5 */ new int[] { 0, 2 }, }; - Func> succF = x => successors[x]; - var wasAcyclic = TopologicalSort.TryIterativeSort(new[] { 4, 5 }, i => succF(i).ToImmutableArray(), out var sorted); + var succF = GetAddSuccessorsFunction(successors); + var wasAcyclic = TopologicalSort.TryIterativeSort(new[] { 4, 5 }, succF, out var sorted); Assert.True(wasAcyclic); AssertTopologicallySorted(sorted, succF, "Test01"); Assert.Equal(6, sorted.Length); @@ -50,8 +55,8 @@ public void Test01b() /* 5 */ new string[] { "0", "2" }, }; - Func> succF = x => successors[int.Parse(x)]; - var wasAcyclic = TopologicalSort.TryIterativeSort(new[] { "4", "5" }, i => succF(i).ToImmutableArray(), out var sorted); + var succF = GetAddSuccessorsFunction(successors, x => int.Parse(x)); + var wasAcyclic = TopologicalSort.TryIterativeSort(new[] { "4", "5" }, succF, out var sorted); Assert.True(wasAcyclic); AssertTopologicallySorted(sorted, succF, "Test01"); Assert.Equal(6, sorted.Length); @@ -73,8 +78,8 @@ public void Test02() /* 7 */ new int[] { } }; - Func> succF = x => successors[x]; - var wasAcyclic = TopologicalSort.TryIterativeSort(new[] { 1, 6 }, i => succF(i).ToImmutableArray(), out var sorted); + var succF = GetAddSuccessorsFunction(successors); + var wasAcyclic = TopologicalSort.TryIterativeSort(new[] { 1, 6 }, succF, out var sorted); Assert.True(wasAcyclic); AssertTopologicallySorted(sorted, succF, "Test02"); Assert.Equal(7, sorted.Length); @@ -97,7 +102,8 @@ public void TestCycle() }; // 1 -> 4 -> 3 -> 5 -> 1 - var wasAcyclic = TopologicalSort.TryIterativeSort(new[] { 1 }, x => successors[x].ToImmutableArray(), out var sorted); + var succF = GetAddSuccessorsFunction(successors); + var wasAcyclic = TopologicalSort.TryIterativeSort(new[] { 1 }, succF, out var sorted); Assert.False(wasAcyclic); } @@ -140,8 +146,8 @@ public void TestRandom(int seed) } // Perform a topological sort and check it. - Func> succF = x => successors[x]; - var wasAcyclic = TopologicalSort.TryIterativeSort(Enumerable.Range(0, numberOfNodes).ToArray(), i => succF(i).ToImmutableArray(), out var sorted); + var succF = GetAddSuccessorsFunction(successors); + var wasAcyclic = TopologicalSort.TryIterativeSort(Enumerable.Range(0, numberOfNodes).ToArray(), succF, out var sorted); Assert.True(wasAcyclic); Assert.Equal(numberOfNodes, sorted.Length); AssertTopologicallySorted(sorted, succF, $"TestRandom(seed: {seed})"); @@ -155,7 +161,7 @@ public void TestRandom(int seed) // time. successors[possibleSort[0]] = successors[possibleSort[0]].Concat(new int[] { possibleSort[numberOfNodes - 1] }).ToArray(); - wasAcyclic = TopologicalSort.TryIterativeSort(Enumerable.Range(0, numberOfNodes).ToArray(), i => succF(i).ToImmutableArray(), out sorted); + wasAcyclic = TopologicalSort.TryIterativeSort(Enumerable.Range(0, numberOfNodes).ToArray(), succF, out sorted); Assert.False(wasAcyclic); // where @@ -203,13 +209,18 @@ public void TestLots() } } - private void AssertTopologicallySorted(ImmutableArray sorted, Func> successors, string message = null) + private void AssertTopologicallySorted(ImmutableArray sorted, TopologicalSortAddSuccessors addSuccessors, string? message = null) { var seen = new HashSet(); + using var successors = TemporaryArray.Empty; for (int i = sorted.Length - 1; i >= 0; i--) { var n = sorted[i]; - foreach (var succ in successors(n)) + + successors.Clear(); + addSuccessors(ref successors.AsRef(), n); + + foreach (var succ in successors) { Assert.True(seen.Contains(succ), message); } diff --git a/src/Compilers/Core/MSBuildTask/Microsoft.Build.Tasks.CodeAnalysis.csproj b/src/Compilers/Core/MSBuildTask/Microsoft.Build.Tasks.CodeAnalysis.csproj index b25c8696df1f3..76f8c63559e8e 100644 --- a/src/Compilers/Core/MSBuildTask/Microsoft.Build.Tasks.CodeAnalysis.csproj +++ b/src/Compilers/Core/MSBuildTask/Microsoft.Build.Tasks.CodeAnalysis.csproj @@ -6,7 +6,7 @@ Library Microsoft.CodeAnalysis.BuildTasks en-US - $(SourceBuildTargetFrameworksNetFx) + $(SourceBuildToolsetTargetFrameworks);net472 true - $(SourceBuildTargetFrameworksNetFx) + net472;$(SourceBuildToolsetTargetFramework) true Microsoft.Net.Compilers.Toolset @@ -51,10 +51,10 @@ <_File Include="@(DesktopCompilerArtifact)" TargetDir="tasks/net472"/> <_File Include="@(DesktopCompilerResourceArtifact)" TargetDir="tasks/net472"/> - <_File Include="@(CoreClrCompilerBuildArtifact)" TargetDir="tasks/$(TargetFramework)"/> - <_File Include="@(CoreClrCompilerToolsArtifact)" TargetDir="tasks/$(TargetFramework)"/> - <_File Include="@(CoreClrCompilerBinArtifact)" TargetDir="tasks/$(TargetFramework)/bincore"/> - <_File Include="@(CoreClrCompilerBinRuntimesArtifact)" TargetDir="tasks/$(TargetFramework)/bincore/runtimes"/> + <_File Include="@(CoreClrCompilerBuildArtifact)" TargetDir="tasks/netcore"/> + <_File Include="@(CoreClrCompilerToolsArtifact)" TargetDir="tasks/netcore"/> + <_File Include="@(CoreClrCompilerBinArtifact)" TargetDir="tasks/netcore/bincore"/> + <_File Include="@(CoreClrCompilerBinRuntimesArtifact)" TargetDir="tasks/netcore/bincore/runtimes"/> diff --git a/src/NuGet/Microsoft.Net.Compilers.Toolset/AnyCpu/build/Microsoft.Net.Compilers.Toolset.props b/src/NuGet/Microsoft.Net.Compilers.Toolset/AnyCpu/build/Microsoft.Net.Compilers.Toolset.props index 4b771afb964cf..8dc5f46a1ecd3 100644 --- a/src/NuGet/Microsoft.Net.Compilers.Toolset/AnyCpu/build/Microsoft.Net.Compilers.Toolset.props +++ b/src/NuGet/Microsoft.Net.Compilers.Toolset/AnyCpu/build/Microsoft.Net.Compilers.Toolset.props @@ -2,7 +2,7 @@ - <_RoslynTargetDirectoryName Condition="'$(MSBuildRuntimeType)' == 'Core'">net6.0 + <_RoslynTargetDirectoryName Condition="'$(MSBuildRuntimeType)' == 'Core'">netcore <_RoslynTargetDirectoryName Condition="'$(MSBuildRuntimeType)' != 'Core'">net472 <_RoslynTasksDirectory>$(MSBuildThisFileDirectory)..\tasks\$(_RoslynTargetDirectoryName)\ $(_RoslynTasksDirectory)Microsoft.Build.Tasks.CodeAnalysis.dll diff --git a/src/NuGet/Microsoft.Net.Compilers.Toolset/arm64/build/Microsoft.Net.Compilers.Toolset.Arm64.props b/src/NuGet/Microsoft.Net.Compilers.Toolset/arm64/build/Microsoft.Net.Compilers.Toolset.Arm64.props index 4b771afb964cf..ea3dd999eb8c4 100644 --- a/src/NuGet/Microsoft.Net.Compilers.Toolset/arm64/build/Microsoft.Net.Compilers.Toolset.Arm64.props +++ b/src/NuGet/Microsoft.Net.Compilers.Toolset/arm64/build/Microsoft.Net.Compilers.Toolset.Arm64.props @@ -2,9 +2,7 @@ - <_RoslynTargetDirectoryName Condition="'$(MSBuildRuntimeType)' == 'Core'">net6.0 - <_RoslynTargetDirectoryName Condition="'$(MSBuildRuntimeType)' != 'Core'">net472 - <_RoslynTasksDirectory>$(MSBuildThisFileDirectory)..\tasks\$(_RoslynTargetDirectoryName)\ + <_RoslynTasksDirectory>$(MSBuildThisFileDirectory)..\tasks\net472\ $(_RoslynTasksDirectory)Microsoft.Build.Tasks.CodeAnalysis.dll true $(_RoslynTasksDirectory)Microsoft.CSharp.Core.targets diff --git a/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs b/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs index 2c70bfbe51e47..4e2c621a86750 100644 --- a/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs +++ b/src/Tools/BuildBoss/CompilerNuGetCheckerUtil.cs @@ -138,16 +138,16 @@ private bool CheckPackages(TextWriter textWriter) allGood &= VerifyPackageCore( textWriter, FindNuGetPackage(Path.Combine(ArtifactsDirectory, "packages", Configuration, "Shipping"), "Microsoft.Net.Compilers.Toolset"), - excludeFunc: relativeFileName => relativeFileName.StartsWith(@"tasks\net6.0\bincore\Microsoft.DiaSymReader.Native", PathComparison), + excludeFunc: relativeFileName => relativeFileName.StartsWith(@"tasks\netcore\bincore\Microsoft.DiaSymReader.Native", PathComparison), (@"tasks\net472", GetProjectOutputDirectory("csc", "net472")), (@"tasks\net472", GetProjectOutputDirectory("vbc", "net472")), (@"tasks\net472", GetProjectOutputDirectory("csi", "net472")), (@"tasks\net472", GetProjectOutputDirectory("VBCSCompiler", "net472")), (@"tasks\net472", GetProjectOutputDirectory("Microsoft.Build.Tasks.CodeAnalysis", "net472")), - (@"tasks\net6.0\bincore", GetProjectPublishDirectory("csc", "net6.0")), - (@"tasks\net6.0\bincore", GetProjectPublishDirectory("vbc", "net6.0")), - (@"tasks\net6.0\bincore", GetProjectPublishDirectory("VBCSCompiler", "net6.0")), - (@"tasks\net6.0", GetProjectPublishDirectory("Microsoft.Build.Tasks.CodeAnalysis", "net6.0"))); + (@"tasks\netcore\bincore", GetProjectPublishDirectory("csc", "net6.0")), + (@"tasks\netcore\bincore", GetProjectPublishDirectory("vbc", "net6.0")), + (@"tasks\netcore\bincore", GetProjectPublishDirectory("VBCSCompiler", "net6.0")), + (@"tasks\netcore", GetProjectPublishDirectory("Microsoft.Build.Tasks.CodeAnalysis", "net6.0"))); foreach (var arch in new[] { "x86", "x64", "arm64" }) { diff --git a/src/VisualStudio/Core/Def/PackageRegistration.pkgdef b/src/VisualStudio/Core/Def/PackageRegistration.pkgdef index bb95c44a768af..8b73440802722 100644 --- a/src/VisualStudio/Core/Def/PackageRegistration.pkgdef +++ b/src/VisualStudio/Core/Def/PackageRegistration.pkgdef @@ -31,6 +31,12 @@ "Title"="Run C#/VB code analysis on .Net 6 (requires restart)" "PreviewPaneChannels"="IntPreview,int.main" +[$RootKey$\FeatureFlags\Roslyn\OOPServerGC] +"Description"="Run C#/VB out-of-process code analysis with ServerGC." +"Value"=dword:00000000 +"Title"="Run C#/VB code analysis with ServerGC (requires restart)" +"PreviewPaneChannels"="IntPreview,int.main" + // Corresponds to WellKnownExperimentNames.LspPullDiagnosticsFeatureFlag [$RootKey$\FeatureFlags\Lsp\PullDiagnostics] "Description"="Enables the LSP-powered diagnostics for managed .Net projects" diff --git a/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj b/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj index 1f2b37be84e72..075262492f489 100644 --- a/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj +++ b/src/Workspaces/Core/MSBuild/Microsoft.CodeAnalysis.Workspaces.MSBuild.csproj @@ -5,7 +5,7 @@ Library Microsoft.CodeAnalysis true - $(SourceBuildTargetFrameworksNetFx) + $(SourceBuildTargetFrameworks);net472 $(DefineConstants);WORKSPACE_MSBUILD true diff --git a/src/Workspaces/Core/Portable/PatternMatching/ContainerPatternMatcher.cs b/src/Workspaces/Core/Portable/PatternMatching/ContainerPatternMatcher.cs index da4f39eec73c7..eb12898fd5f53 100644 --- a/src/Workspaces/Core/Portable/PatternMatching/ContainerPatternMatcher.cs +++ b/src/Workspaces/Core/Portable/PatternMatching/ContainerPatternMatcher.cs @@ -2,12 +2,9 @@ // 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; using System.Globalization; using System.Linq; -using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Collections; namespace Microsoft.CodeAnalysis.PatternMatching @@ -21,9 +18,10 @@ private sealed partial class ContainerPatternMatcher : PatternMatcher public ContainerPatternMatcher( string[] patternParts, char[] containerSplitCharacters, - CultureInfo culture, + bool includeMatchedSpans, + CultureInfo? culture, bool allowFuzzyMatching = false) - : base(false, culture, allowFuzzyMatching) + : base(includeMatchedSpans, culture, allowFuzzyMatching) { _containerSplitCharacters = containerSplitCharacters; @@ -44,7 +42,7 @@ public override void Dispose() } } - public override bool AddMatches(string container, ref TemporaryArray matches) + public override bool AddMatches(string? container, ref TemporaryArray matches) { if (SkipMatch(container)) { diff --git a/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.cs b/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.cs index 36c33c465e7be..67153d122d0d2 100644 --- a/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.cs +++ b/src/Workspaces/Core/Portable/PatternMatching/PatternMatcher.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared; @@ -46,7 +47,7 @@ internal abstract partial class PatternMatcher : IDisposable /// Whether or not close matches should count as matches. protected PatternMatcher( bool includeMatchedSpans, - CultureInfo culture, + CultureInfo? culture, bool allowFuzzyMatching = false) { culture ??= CultureInfo.CurrentCulture; @@ -74,21 +75,23 @@ public static PatternMatcher CreatePatternMatcher( public static PatternMatcher CreateContainerPatternMatcher( string[] patternParts, char[] containerSplitCharacters, + bool includeMatchedSpans = false, CultureInfo? culture = null, bool allowFuzzyMatching = false) { return new ContainerPatternMatcher( - patternParts, containerSplitCharacters, culture, allowFuzzyMatching); + patternParts, containerSplitCharacters, includeMatchedSpans, culture, allowFuzzyMatching); } public static PatternMatcher CreateDotSeparatedContainerMatcher( string pattern, + bool includeMatchedSpans = false, CultureInfo? culture = null, bool allowFuzzyMatching = false) { return CreateContainerPatternMatcher( pattern.Split(s_dotCharacterArray, StringSplitOptions.RemoveEmptyEntries), - s_dotCharacterArray, culture, allowFuzzyMatching); + s_dotCharacterArray, includeMatchedSpans, culture, allowFuzzyMatching); } internal static (string name, string? containerOpt) GetNameAndContainer(string pattern) @@ -102,7 +105,7 @@ internal static (string name, string? containerOpt) GetNameAndContainer(string p public abstract bool AddMatches(string? candidate, ref TemporaryArray matches); - private bool SkipMatch(string? candidate) + private bool SkipMatch([NotNullWhen(false)] string? candidate) => _invalidPattern || string.IsNullOrWhiteSpace(candidate); private static bool ContainsUpperCaseLetter(string pattern) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs b/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs index c0ab70015feb1..9639bc617aa50 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/LoadableTextAndVersionSource.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; @@ -16,12 +17,27 @@ internal sealed class LoadableTextAndVersionSource : ITextAndVersionSource private sealed class LazyValueWithOptions { public readonly LoadableTextAndVersionSource Source; - public readonly AsyncLazy LazyValue; public readonly LoadTextOptions Options; + private readonly SemaphoreSlim _gate = new(initialCount: 1); + + /// + /// Strong reference to the loaded text and version. Only held onto once computed if . is . Once held onto, this will be returned from all calls to + /// , or . Once non-null will always + /// remain non-null. + /// + private TextAndVersion? _instance; + + /// + /// Weak reference to the loaded text and version that we create whenever the value is computed. We will + /// attempt to return from this if still alive when clients call back into this. If neither this, nor are available, the value will be reloaded. Once non-null, this will always be non-null. + /// + private WeakReference? _weakInstance; + public LazyValueWithOptions(LoadableTextAndVersionSource source, LoadTextOptions options) { - LazyValue = new AsyncLazy(LoadAsync, LoadSynchronously, source.CacheResult); Source = source; Options = options; } @@ -31,6 +47,67 @@ private Task LoadAsync(CancellationToken cancellationToken) private TextAndVersion LoadSynchronously(CancellationToken cancellationToken) => Source.Loader.LoadTextSynchronously(Options, cancellationToken); + + public bool TryGetValue([MaybeNullWhen(false)] out TextAndVersion value) + { + value = _instance; + if (value != null) + return true; + + return _weakInstance?.TryGetTarget(out value) == true && value != null; + } + + public TextAndVersion GetValue(CancellationToken cancellationToken) + { + if (!TryGetValue(out var textAndVersion)) + { + using (_gate.DisposableWait(cancellationToken)) + { + if (!TryGetValue(out textAndVersion)) + { + textAndVersion = LoadSynchronously(cancellationToken); + UpdateWeakAndStrongReferences_NoLock(textAndVersion); + } + } + } + + return textAndVersion; + } + + public async Task GetValueAsync(CancellationToken cancellationToken) + { + if (!TryGetValue(out var textAndVersion)) + { + using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) + { + if (!TryGetValue(out textAndVersion)) + { + textAndVersion = await LoadAsync(cancellationToken).ConfigureAwait(false); + UpdateWeakAndStrongReferences_NoLock(textAndVersion); + } + } + } + + return textAndVersion; + } + + private void UpdateWeakAndStrongReferences_NoLock(TextAndVersion textAndVersion) + { + Contract.ThrowIfTrue(_gate.CurrentCount != 0); + + if (this.Source.CacheResult) + { + // if our source wants us to hold onto the value strongly, do so. No need to involve the weak-refs as + // this will now hold onto the value forever. + _instance = textAndVersion; + } + else + { + // Update the weak ref, so we can return the same instance if anything else is holding onto it. + _weakInstance ??= new WeakReference(textAndVersion); + _weakInstance.SetTarget(textAndVersion); + } + } } public readonly TextLoader Loader; @@ -47,7 +124,7 @@ public LoadableTextAndVersionSource(TextLoader loader, bool cacheResult) public bool CanReloadText => Loader.CanReloadText; - private AsyncLazy GetLazyValue(LoadTextOptions options) + private LazyValueWithOptions GetLazyValue(LoadTextOptions options) { var lazy = _lazyValue; @@ -57,7 +134,7 @@ private AsyncLazy GetLazyValue(LoadTextOptions options) _lazyValue = lazy = new LazyValueWithOptions(this, options); } - return lazy.LazyValue; + return lazy; } public TextAndVersion GetValue(LoadTextOptions options, CancellationToken cancellationToken) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDependencyGraph.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDependencyGraph.cs index cf16b22f5fcb4..4593d8b9c5c70 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDependencyGraph.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDependencyGraph.cs @@ -128,6 +128,17 @@ internal ProjectDependencyGraph WithProjectReferences(ProjectId projectId, IRead { Contract.ThrowIfFalse(_projectIds.Contains(projectId)); + if (!_referencesMap.ContainsKey(projectId)) + { + // This project doesn't have any references currently, so we delegate to WithAdditionalProjectReferences + return WithAdditionalProjectReferences(projectId, projectReferences); + } + else if (projectReferences.Count == 0) + { + // We are removing all project references; do so directly + return WithAllProjectReferencesRemoved(projectId); + } + // This method we can't optimize very well: changing project references arbitrarily could invalidate pretty much anything. // The only thing we can reuse is our actual map of project references for all the other projects, so we'll do that. diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDependencyGraph_RemoveAllProjectReferences.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDependencyGraph_RemoveAllProjectReferences.cs new file mode 100644 index 0000000000000..22deeab440b73 --- /dev/null +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectDependencyGraph_RemoveAllProjectReferences.cs @@ -0,0 +1,124 @@ +// 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. + +using System.Collections.Immutable; +using System.Diagnostics; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis +{ + public partial class ProjectDependencyGraph + { + internal ProjectDependencyGraph WithAllProjectReferencesRemoved(ProjectId projectId) + { + Contract.ThrowIfFalse(_projectIds.Contains(projectId)); + + if (!_referencesMap.TryGetValue(projectId, out var referencedProjectIds)) + return this; + + // Removing a project reference doesn't change the set of projects + var projectIds = _projectIds; + + // Incrementally update the graph + var referencesMap = ComputeNewReferencesMapForRemovedAllProjectReferences(_referencesMap, projectId); + var reverseReferencesMap = ComputeNewReverseReferencesMapForRemovedAllProjectReferences(_lazyReverseReferencesMap, projectId, referencedProjectIds); + var transitiveReferencesMap = ComputeNewTransitiveReferencesMapForRemovedAllProjectReferences(_transitiveReferencesMap, projectId, referencedProjectIds); + var reverseTransitiveReferencesMap = ComputeNewReverseTransitiveReferencesMapForRemovedAllProjectReferences(_reverseTransitiveReferencesMap, projectId); + + return new ProjectDependencyGraph( + projectIds, + referencesMap, + reverseReferencesMap, + transitiveReferencesMap, + reverseTransitiveReferencesMap, + topologicallySortedProjects: default, + dependencySets: default); + } + + private static ImmutableDictionary> ComputeNewReferencesMapForRemovedAllProjectReferences( + ImmutableDictionary> existingForwardReferencesMap, + ProjectId projectId) + { + // Projects with no references do not have an entry in the forward references map + return existingForwardReferencesMap.Remove(projectId); + } + + /// + /// Computes a new for the removal of all project references from a + /// project. + /// + /// The prior to the removal, + /// or if the reverse references map was not computed for the prior graph. + /// The project ID from which a project reference is being removed. + /// The targets of the project references which are being removed. + /// The updated (complete) reverse references map, or if the reverse references + /// map could not be incrementally updated. + private static ImmutableDictionary>? ComputeNewReverseReferencesMapForRemovedAllProjectReferences( + ImmutableDictionary>? existingReverseReferencesMap, + ProjectId projectId, + ImmutableHashSet referencedProjectIds) + { + if (existingReverseReferencesMap is null) + { + return null; + } + + var builder = existingReverseReferencesMap.ToBuilder(); + foreach (var referencedProjectId in referencedProjectIds) + { + builder.MultiRemove(referencedProjectId, projectId); + } + + return builder.ToImmutable(); + } + + private static ImmutableDictionary> ComputeNewTransitiveReferencesMapForRemovedAllProjectReferences( + ImmutableDictionary> existingTransitiveReferencesMap, + ProjectId projectId, + ImmutableHashSet referencedProjectIds) + { + var builder = existingTransitiveReferencesMap.ToBuilder(); + + // Invalidate the transitive references from every project referencing the changed project (transitively) + foreach (var (project, references) in existingTransitiveReferencesMap) + { + if (!references.Contains(projectId)) + { + // This is the forward-references-equivalent of the optimization in the update of reverse transitive + // references. + continue; + } + + Debug.Assert(references.IsSupersetOf(referencedProjectIds)); + builder.Remove(project); + } + + // Invalidate the transitive references from the changed project + builder.Remove(projectId); + + return builder.ToImmutable(); + } + + private static ImmutableDictionary> ComputeNewReverseTransitiveReferencesMapForRemovedAllProjectReferences( + ImmutableDictionary> existingReverseTransitiveReferencesMap, + ProjectId projectId) + { + var builder = existingReverseTransitiveReferencesMap.ToBuilder(); + + // Invalidate the transitive reverse references from every project previously referenced by the original + // project (transitively). + foreach (var (project, references) in existingReverseTransitiveReferencesMap) + { + if (!references.Contains(projectId)) + { + continue; + } + + builder.Remove(project); + } + + return builder.ToImmutable(); + } + } +} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs index b9695d23e8e1a..879b628a01346 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Extensions/ListExtensions.cs @@ -78,5 +78,14 @@ public static int IndexOf(this IList list, Func predicate) return -1; } + + public static void AddRangeWhere(this List list, List collection, Func predicate) + { + foreach (var element in collection) + { + if (predicate(element)) + list.Add(element); + } + } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.AnalysisData.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.AnalysisData.cs index af6ff0d545911..c0ad85b57b3b1 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.AnalysisData.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.AnalysisData.cs @@ -185,11 +185,13 @@ public void OnReadReferenceFound(ISymbol symbol) // Mark all the current reaching writes of symbol as read. if (SymbolsWriteBuilder.Count != 0) { - var currentWrites = CurrentBlockAnalysisData.GetCurrentWrites(symbol); - foreach (var write in currentWrites) - { - SymbolsWriteBuilder[(symbol, write)] = true; - } + CurrentBlockAnalysisData.ForEachCurrentWrite( + symbol, + static (write, arg) => + { + arg.self.SymbolsWriteBuilder[(arg.symbol, write)] = true; + }, + (symbol, self: this)); } // Mark the current symbol as read. diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.BasicBlockAnalysisData.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.BasicBlockAnalysisData.cs index 7b0f5f5c7a2d4..45c98a5326b28 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.BasicBlockAnalysisData.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.BasicBlockAnalysisData.cs @@ -73,15 +73,30 @@ public void Clear(ISymbol symbol) /// /// Gets the currently reachable writes for the given symbol. /// - public IEnumerable GetCurrentWrites(ISymbol symbol) + public void ForEachCurrentWrite(ISymbol symbol, Action action, TArg arg) + { + ForEachCurrentWrite( + symbol, + static (write, arg) => + { + arg.action(write, arg.arg); + return true; + }, + (action, arg)); + } + + public bool ForEachCurrentWrite(ISymbol symbol, Func action, TArg arg) { if (_reachingWrites.TryGetValue(symbol, out var values)) { foreach (var value in values) { - yield return value; + if (!action(value, arg)) + return false; } } + + return true; } /// @@ -137,6 +152,11 @@ public bool Equals(BasicBlockAnalysisData other) /// private static bool SetEquals(HashSet set1, HashSet set2) { +#if NET8_0_OR_GREATER + // 📝 PERF: The boxed enumerator allocation that appears in some traces was fixed in .NET 8: + // https://github.com/dotnet/runtime/pull/78613 + return set1.SetEquals(set2); +#else // same logic as https://github.com/dotnet/runtime/blob/62d6a8fe599ea3a77ef7af3c7660d398d692f062/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/HashSet.cs#L1192 if (set1.Count != set2.Count) @@ -149,6 +169,7 @@ private static bool SetEquals(HashSet set1, HashSet set2) } return true; +#endif } private bool IsEmpty => _reachingWrites.Count == 0; @@ -214,7 +235,7 @@ private static void AddEntries(Dictionary> re } #if NET - values.EnsureCapacity(operations.Count); + values.EnsureCapacity(values.Count + operations.Count); #endif values.AddRange(operations); } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.DataFlowAnalyzer.FlowGraphAnalysisData.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.DataFlowAnalyzer.FlowGraphAnalysisData.cs index 9acddc816678a..fa713fa175768 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.DataFlowAnalyzer.FlowGraphAnalysisData.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/FlowAnalysis/SymbolUsageAnalysis/SymbolUsageAnalysis.DataFlowAnalyzer.FlowGraphAnalysisData.cs @@ -440,14 +440,16 @@ public void SetAnalysisDataOnMethodExit() { if (parameter.RefKind is RefKind.Ref or RefKind.Out) { - var currentWrites = CurrentBlockAnalysisData.GetCurrentWrites(parameter); - foreach (var write in currentWrites) - { - if (write != null) + CurrentBlockAnalysisData.ForEachCurrentWrite( + parameter, + static (write, arg) => { - SymbolsWriteBuilder[(parameter, write)] = true; - } - } + if (write != null) + { + arg.self.SymbolsWriteBuilder[(arg.parameter, write)] = true; + } + }, + (parameter, self: this)); } } } @@ -519,28 +521,40 @@ public override void SetTargetsFromSymbolForDelegate(IOperation write, ISymbol s // Action y = x; // var targetsBuilder = PooledHashSet.GetInstance(); - foreach (var symbolWrite in CurrentBlockAnalysisData.GetCurrentWrites(symbol)) - { - if (symbolWrite == null) + var completedVisit = CurrentBlockAnalysisData.ForEachCurrentWrite( + symbol, + static (symbolWrite, arg) => { - continue; - } + if (symbolWrite == null) + { + // Continue with the iteration + return true; + } - if (!_reachingDelegateCreationTargets.TryGetValue(symbolWrite, out var targetsBuilderForSymbolWrite)) - { - // Unable to find delegate creation targets for this symbol write. - // Bail out without setting targets. - targetsBuilder.Free(); - return; - } - else - { - foreach (var target in targetsBuilderForSymbolWrite) + if (!arg.self._reachingDelegateCreationTargets.TryGetValue(symbolWrite, out var targetsBuilderForSymbolWrite)) { - targetsBuilder.Add(target); + // Unable to find delegate creation targets for this symbol write. + // Bail out without setting targets. + arg.targetsBuilder.Free(); + + // Stop iterating here, even if early + return false; } - } - } + else + { + foreach (var target in targetsBuilderForSymbolWrite) + { + arg.targetsBuilder.Add(target); + } + } + + // Continue with the iteration + return true; + }, + (targetsBuilder, self: this)); + + if (!completedVisit) + return; _reachingDelegateCreationTargets[write] = targetsBuilder; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs index 96256475649f9..056326d26eaa5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/AbstractFormatEngine.cs @@ -4,12 +4,13 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Threading; using Microsoft.CodeAnalysis.Collections; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Formatting.Rules; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.LanguageService; +using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; @@ -28,6 +29,11 @@ namespace Microsoft.CodeAnalysis.Formatting // that would create too big graph. key for this approach is how to reduce size of graph. internal abstract partial class AbstractFormatEngine { + // Intentionally do not trim the capacities of these collections down. We will repeatedly try to format large + // files as we edit them and this will produce a lot of garbage as we free the internal array backing the list + // over and over again. + private static readonly ObjectPool> s_tokenPairListPool = new(() => new(), trimOnFree: false); + private readonly ChainedFormattingRules _formattingRules; private readonly SyntaxNode _commonRoot; @@ -88,7 +94,8 @@ public AbstractFormattingResult Format(CancellationToken cancellationToken) var nodeOperations = CreateNodeOperations(cancellationToken); var tokenStream = new TokenStream(this.TreeData, Options, this.SpanToFormat, CreateTriviaFactory()); - var tokenOperation = CreateTokenOperation(tokenStream, cancellationToken); + using var tokenOperations = s_tokenPairListPool.GetPooledObject(); + AddTokenOperations(tokenStream, tokenOperations.Object, cancellationToken); // initialize context var context = CreateFormattingContext(tokenStream, cancellationToken); @@ -101,8 +108,7 @@ public AbstractFormattingResult Format(CancellationToken cancellationToken) ApplyBeginningOfTreeTriviaOperation(context, cancellationToken); - ApplyTokenOperations(context, nodeOperations, - tokenOperation, cancellationToken); + ApplyTokenOperations(context, nodeOperations, tokenOperations.Object, cancellationToken); ApplyTriviaOperations(context, cancellationToken); @@ -125,88 +131,56 @@ protected virtual NodeOperations CreateNodeOperations(CancellationToken cancella { cancellationToken.ThrowIfCancellationRequested(); - // iterating tree is very expensive. do it once and cache it to list - SegmentedList nodeIterator; - using (Logger.LogBlock(FunctionId.Formatting_IterateNodes, cancellationToken)) - { - const int magicLengthToNodesRatio = 5; - var result = new SegmentedList(Math.Max(this.SpanToFormat.Length / magicLengthToNodesRatio, 4)); + var nodeOperations = new NodeOperations(); - foreach (var node in _commonRoot.DescendantNodesAndSelf(this.SpanToFormat)) - { - cancellationToken.ThrowIfCancellationRequested(); - result.Add(node); - } + var indentBlockOperation = new List(); + var suppressOperation = new List(); + var alignmentOperation = new List(); + var anchorIndentationOperations = new List(); - nodeIterator = result; - } + // Cache delegates out here to avoid allocation overhead. - // iterate through each operation using index to not create any unnecessary object - cancellationToken.ThrowIfCancellationRequested(); - List indentBlockOperation; - using (Logger.LogBlock(FunctionId.Formatting_CollectIndentBlock, cancellationToken)) - { - indentBlockOperation = AddOperations(nodeIterator, _formattingRules.AddIndentBlockOperations, cancellationToken); - } + var addIndentBlockOperations = _formattingRules.AddIndentBlockOperations; + var addSuppressOperation = _formattingRules.AddSuppressOperations; + var addAlignTokensOperations = _formattingRules.AddAlignTokensOperations; + var addAnchorIndentationOperations = _formattingRules.AddAnchorIndentationOperations; - cancellationToken.ThrowIfCancellationRequested(); - List suppressOperation; - using (Logger.LogBlock(FunctionId.Formatting_CollectSuppressOperation, cancellationToken)) + // iterating tree is very expensive. only do it once. + foreach (var node in _commonRoot.DescendantNodesAndSelf(this.SpanToFormat)) { - suppressOperation = AddOperations(nodeIterator, _formattingRules.AddSuppressOperations, cancellationToken); - } - - cancellationToken.ThrowIfCancellationRequested(); - List alignmentOperation; - using (Logger.LogBlock(FunctionId.Formatting_CollectAlignOperation, cancellationToken)) - { - var operations = AddOperations(nodeIterator, _formattingRules.AddAlignTokensOperations, cancellationToken); - - // make sure we order align operation from left to right - operations.Sort((o1, o2) => o1.BaseToken.Span.CompareTo(o2.BaseToken.Span)); + cancellationToken.ThrowIfCancellationRequested(); - alignmentOperation = operations; + AddOperations(nodeOperations.IndentBlockOperation, indentBlockOperation, node, addIndentBlockOperations); + AddOperations(nodeOperations.SuppressOperation, suppressOperation, node, addSuppressOperation); + AddOperations(nodeOperations.AlignmentOperation, alignmentOperation, node, addAlignTokensOperations); + AddOperations(nodeOperations.AnchorIndentationOperations, anchorIndentationOperations, node, addAnchorIndentationOperations); } - cancellationToken.ThrowIfCancellationRequested(); - List anchorIndentationOperations; - using (Logger.LogBlock(FunctionId.Formatting_CollectAnchorOperation, cancellationToken)) - { - anchorIndentationOperations = AddOperations(nodeIterator, _formattingRules.AddAnchorIndentationOperations, cancellationToken); - } + // make sure we order align operation from left to right + alignmentOperation.Sort(static (o1, o2) => o1.BaseToken.Span.CompareTo(o2.BaseToken.Span)); - return new NodeOperations(indentBlockOperation, suppressOperation, anchorIndentationOperations, alignmentOperation); + return nodeOperations; } - private static List AddOperations(SegmentedList nodes, Action, SyntaxNode> addOperations, CancellationToken cancellationToken) + private static void AddOperations(List operations, List scratch, SyntaxNode node, Action, SyntaxNode> addOperations) { - var operations = new List(); - var list = new List(); - - foreach (var n in nodes) - { - cancellationToken.ThrowIfCancellationRequested(); - addOperations(list, n); + Debug.Assert(scratch.Count == 0); - list.RemoveAll(item => item == null); - operations.AddRange(list); - list.Clear(); - } + addOperations(scratch, node); - return operations; + operations.AddRangeWhere(scratch, static item => item is not null); + scratch.Clear(); } - private SegmentedArray CreateTokenOperation( + private void AddTokenOperations( TokenStream tokenStream, + SegmentedList list, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); using (Logger.LogBlock(FunctionId.Formatting_CollectTokenOperation, cancellationToken)) { - // pre-allocate list once. this is cheaper than re-adjusting list as items are added. - var list = new SegmentedArray(tokenStream.TokenCount - 1); - foreach (var (index, currentToken, nextToken) in tokenStream.TokenIterator) { cancellationToken.ThrowIfCancellationRequested(); @@ -214,17 +188,15 @@ private SegmentedArray CreateTokenOperation( var spaceOperation = _formattingRules.GetAdjustSpacesOperation(currentToken, nextToken); var lineOperation = _formattingRules.GetAdjustNewLinesOperation(currentToken, nextToken); - list[index] = new TokenPairWithOperations(tokenStream, index, spaceOperation, lineOperation); + list.Add(new TokenPairWithOperations(tokenStream, index, spaceOperation, lineOperation)); } - - return list; } } private void ApplyTokenOperations( FormattingContext context, NodeOperations nodeOperations, - SegmentedArray tokenOperations, + SegmentedList tokenOperations, CancellationToken cancellationToken) { var applier = new OperationApplier(context, _formattingRules); @@ -326,7 +298,7 @@ private static void ApplySpecialOperations( FormattingContext context, NodeOperations nodeOperationsCollector, OperationApplier applier, CancellationToken cancellationToken) { // apply alignment operation - using (Logger.LogBlock(FunctionId.Formatting_CollectAlignOperation, cancellationToken)) + using (Logger.LogBlock(FunctionId.Formatting_ApplyAlignOperation, cancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); @@ -352,7 +324,7 @@ private static void ApplySpecialOperations( private static void ApplyAnchorOperations( FormattingContext context, - SegmentedArray tokenOperations, + SegmentedList tokenOperations, OperationApplier applier, CancellationToken cancellationToken) { @@ -364,9 +336,7 @@ private static void ApplyAnchorOperations( { cancellationToken.ThrowIfCancellationRequested(); if (!AnchorOperationCandidate(p)) - { continue; - } var pairIndex = p.PairIndex; applier.ApplyAnchorIndentation(pairIndex, previousChangesMap, cancellationToken); @@ -409,7 +379,7 @@ private static SyntaxToken FindCorrectBaseTokenOfRelativeIndentBlockOperation(In private static void ApplySpaceAndWrappingOperations( FormattingContext context, - SegmentedArray tokenOperations, + SegmentedList tokenOperations, OperationApplier applier, CancellationToken cancellationToken) { @@ -417,9 +387,7 @@ private static void ApplySpaceAndWrappingOperations( { // go through each token pairs and apply operations foreach (var operationPair in tokenOperations) - { ApplySpaceAndWrappingOperationsBody(context, operationPair, applier, cancellationToken); - } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/NodeOperations.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/NodeOperations.cs index 7a3fbbaa4328c..d76367193f62d 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/NodeOperations.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/NodeOperations.cs @@ -14,25 +14,9 @@ internal class NodeOperations { public static NodeOperations Empty = new(); - public List IndentBlockOperation { get; } - public List SuppressOperation { get; } - public List AlignmentOperation { get; } - public List AnchorIndentationOperations { get; } - - public NodeOperations(List indentBlockOperation, List suppressOperation, List anchorIndentationOperations, List alignmentOperation) - { - this.IndentBlockOperation = indentBlockOperation; - this.SuppressOperation = suppressOperation; - this.AlignmentOperation = alignmentOperation; - this.AnchorIndentationOperations = anchorIndentationOperations; - } - - private NodeOperations() - { - this.IndentBlockOperation = new List(); - this.SuppressOperation = new List(); - this.AlignmentOperation = new List(); - this.AnchorIndentationOperations = new List(); - } + public List IndentBlockOperation { get; } = new(); + public List SuppressOperation { get; } = new(); + public List AlignmentOperation { get; } = new(); + public List AnchorIndentationOperations { get; } = new(); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenStream.Iterator.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenStream.Iterator.cs index 79efdb4985940..fd8fc491ce50a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenStream.Iterator.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenStream.Iterator.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; using Microsoft.CodeAnalysis.Collections; namespace Microsoft.CodeAnalysis.Formatting @@ -12,20 +10,17 @@ internal partial class TokenStream { // gain of having hand written iterator seems about 50-100ms over auto generated one. // not sure whether it is worth it. but I already wrote it to test, so going to just keep it. - private class Iterator : IEnumerable<(int index, SyntaxToken currentToken, SyntaxToken nextToken)> + public readonly struct Iterator { private readonly SegmentedList _tokensIncludingZeroWidth; public Iterator(SegmentedList tokensIncludingZeroWidth) => _tokensIncludingZeroWidth = tokensIncludingZeroWidth; - public IEnumerator<(int index, SyntaxToken currentToken, SyntaxToken nextToken)> GetEnumerator() - => new Enumerator(_tokensIncludingZeroWidth); + public Enumerator GetEnumerator() + => new(_tokensIncludingZeroWidth); - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - => GetEnumerator(); - - private struct Enumerator : IEnumerator<(int index, SyntaxToken currentToken, SyntaxToken nextToken)> + public struct Enumerator { private readonly SegmentedList _tokensIncludingZeroWidth; private readonly int _maxCount; @@ -42,10 +37,6 @@ public Enumerator(SegmentedList tokensIncludingZeroWidth) _current = default; } - public readonly void Dispose() - { - } - public bool MoveNext() { if (_index < _maxCount) @@ -66,25 +57,6 @@ private bool MoveNextRare() } public readonly (int index, SyntaxToken currentToken, SyntaxToken nextToken) Current => _current; - - readonly object System.Collections.IEnumerator.Current - { - get - { - if (_index == 0 || _index == _maxCount + 1) - { - throw new InvalidOperationException(); - } - - return Current; - } - } - - void System.Collections.IEnumerator.Reset() - { - _index = 0; - _current = new ValueTuple(); - } } } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenStream.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenStream.cs index adb3cef214c0c..f1062caa03050 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenStream.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Formatting/Engine/TokenStream.cs @@ -550,13 +550,8 @@ private int GetTokenIndexInStream(SyntaxToken token) return -1; } - public IEnumerable<(int index, SyntaxToken currentToken, SyntaxToken nextToken)> TokenIterator - { - get - { - return new Iterator(_tokens); - } - } + public Iterator TokenIterator + => new(_tokens); private sealed class TokenOrderComparer : IComparer { diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs index c0d57e6ac0ee4..fb265dad84e82 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs @@ -111,11 +111,11 @@ internal enum FunctionId Formatting_ContextInitialization = 85, Formatting_Format = 86, Formatting_ApplyResultToBuffer = 87, - Formatting_IterateNodes = 88, - Formatting_CollectIndentBlock = 89, - Formatting_CollectSuppressOperation = 90, - Formatting_CollectAlignOperation = 91, - Formatting_CollectAnchorOperation = 92, + // obsolete: Formatting_IterateNodes = 88, + // obsolete: Formatting_CollectIndentBlock = 89, + // obsolete: Formatting_CollectSuppressOperation = 90, + // obsolete: Formatting_CollectAlignOperation = 91, + // obsolete: Formatting_CollectAnchorOperation = 92, Formatting_CollectTokenOperation = 93, Formatting_BuildContext = 94, Formatting_ApplySpaceAndLine = 95, diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/Extensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/Extensions.cs index a009bbcb30b39..f41c902d9fbc5 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/Extensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/Extensions.cs @@ -268,7 +268,7 @@ public static void ClearAndFree(this ObjectPool(this ObjectPool> pool, List list) + public static void ClearAndFree(this ObjectPool> pool, List list, bool trim = true) { if (list == null) { @@ -277,7 +277,7 @@ public static void ClearAndFree(this ObjectPool> pool, List list) list.Clear(); - if (list.Capacity > Threshold) + if (trim && list.Capacity > Threshold) { list.Capacity = Threshold; } @@ -285,7 +285,7 @@ public static void ClearAndFree(this ObjectPool> pool, List list) pool.Free(list); } - public static void ClearAndFree(this ObjectPool> pool, SegmentedList list) + public static void ClearAndFree(this ObjectPool> pool, SegmentedList list, bool trim = true) { if (list == null) { @@ -294,7 +294,7 @@ public static void ClearAndFree(this ObjectPool> pool, Segme list.Clear(); - if (list.Capacity > Threshold) + if (trim && list.Capacity > Threshold) { list.Capacity = Threshold; } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledObject.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledObject.cs index 98ad09b8e3c8b..9078f5f0fd50c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledObject.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/ObjectPools/PooledObject.cs @@ -134,13 +134,13 @@ private static List Allocator(ObjectPool> pool) => pool.AllocateAndClear(); private static void Releaser(ObjectPool> pool, List obj) - => pool.ClearAndFree(obj); + => pool.ClearAndFree(obj, pool.TrimOnFree); private static SegmentedList Allocator(ObjectPool> pool) => pool.AllocateAndClear(); private static void Releaser(ObjectPool> pool, SegmentedList obj) - => pool.ClearAndFree(obj); + => pool.ClearAndFree(obj, pool.TrimOnFree); #endregion } }