From 2e23352c904a10f30b3bbb9eadda5e8a9b16f6dd Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Thu, 25 Jun 2020 13:10:15 -0700 Subject: [PATCH] Move SourceOrdinaryMethodSymbol functionality that is not syntax specific into a new base class. (#45424) This work is related to #45296. --- .../Source/SourceMemberMethodSymbol.cs | 2 +- .../Source/SourceOrdinaryMethodSymbol.cs | 706 ++---------------- .../Source/SourceOrdinaryMethodSymbolBase.cs | 668 +++++++++++++++++ .../Source/SourcePropertyAccessorSymbol.cs | 4 +- 4 files changed, 754 insertions(+), 626 deletions(-) create mode 100644 src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs index ed14cf49046..a8992e242a0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs @@ -923,7 +923,7 @@ internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, r /// Checks to see if a body is legal given the current modifiers. /// If it is not, a diagnostic is added with the current type. /// - protected void CheckModifiersForBody(SyntaxNode declarationSyntax, Location location, DiagnosticBag diagnostics) + protected void CheckModifiersForBody(Location location, DiagnosticBag diagnostics) { if (IsExtern && !IsAbstract) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs index befe03e53ce..78a02d9b379 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbol.cs @@ -9,7 +9,6 @@ using System.Linq; using System.Threading; using Microsoft.CodeAnalysis.CSharp.Emit; -using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Text; @@ -17,19 +16,12 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols { - internal sealed class SourceOrdinaryMethodSymbol : SourceMemberMethodSymbol + internal sealed class SourceOrdinaryMethodSymbol : SourceOrdinaryMethodSymbolBase { - private readonly ImmutableArray _typeParameters; private readonly TypeSymbol _explicitInterfaceType; - private readonly string _name; private readonly bool _isExpressionBodied; private readonly bool _hasAnyBody; private readonly RefKind _refKind; - - private ImmutableArray _lazyExplicitInterfaceImplementations; - private ImmutableArray _lazyRefCustomModifiers; - private ImmutableArray _lazyParameters; - private TypeWithAnnotations _lazyReturnType; private bool _lazyIsVararg; /// @@ -76,79 +68,48 @@ internal sealed class SourceOrdinaryMethodSymbol : SourceMemberMethodSymbol MethodKind methodKind, DiagnosticBag diagnostics) : base(containingType, - syntax.GetReference(), + name, location, - isIterator: SyntaxFacts.HasYieldOperations(syntax.Body)) + syntax, + methodKind, + isIterator: SyntaxFacts.HasYieldOperations(syntax.Body), + isExtensionMethod: syntax.ParameterList.Parameters.FirstOrDefault() is ParameterSyntax firstParam && + !firstParam.IsArgList && + firstParam.Modifiers.Any(SyntaxKind.ThisKeyword), + isPartial: syntax.Modifiers.IndexOf(SyntaxKind.PartialKeyword) < 0, + hasBody: syntax.Body != null || syntax.ExpressionBody != null, + diagnostics) { - _name = name; _explicitInterfaceType = explicitInterfaceType; - SyntaxTokenList modifiers = syntax.Modifiers; - - // The following two values are used to compute and store the initial value of the flags - // However, these two components are placeholders; the correct value will be - // computed lazily later and then the flags will be fixed up. - const bool returnsVoid = false; - - var firstParam = syntax.ParameterList.Parameters.FirstOrDefault(); - bool isExtensionMethod = firstParam != null && - !firstParam.IsArgList && - firstParam.Modifiers.Any(SyntaxKind.ThisKeyword); - bool hasBlockBody = syntax.Body != null; _isExpressionBodied = !hasBlockBody && syntax.ExpressionBody != null; bool hasBody = hasBlockBody || _isExpressionBodied; _hasAnyBody = hasBody; - - bool modifierErrors; - DeclarationModifiers declarationModifiers; - (declarationModifiers, HasExplicitAccessModifier, modifierErrors) = this.MakeModifiers(modifiers, methodKind, hasBody, location, diagnostics); - - var isMetadataVirtualIgnoringModifiers = (object)explicitInterfaceType != null; //explicit impls must be marked metadata virtual - - this.MakeFlags(methodKind, declarationModifiers, returnsVoid, isExtensionMethod, isMetadataVirtualIgnoringModifiers); - - if (syntax.Arity == 0) - { - _typeParameters = ImmutableArray.Empty; - ReportErrorIfHasConstraints(syntax.ConstraintClauses, diagnostics); - } - else - { - _typeParameters = MakeTypeParameters(syntax, diagnostics); - } - _refKind = syntax.ReturnType.GetRefKind(); - CheckFeatureAvailabilityAndRuntimeSupport(syntax, location, hasBody, diagnostics); - - if (hasBody) - { - CheckModifiersForBody(syntax, location, diagnostics); - } - - var info = ModifierUtils.CheckAccessibility(this.DeclarationModifiers, this, isExplicitInterfaceImplementation: methodKind == MethodKind.ExplicitInterfaceImplementation); - if (info != null) - { - diagnostics.Add(info, location); - } - CheckForBlockAndExpressionBody( syntax.Body, syntax.ExpressionBody, syntax, diagnostics); } - public override bool ReturnsVoid + protected override ImmutableArray MakeTypeParameters(CSharpSyntaxNode node, DiagnosticBag diagnostics) { - get + var syntax = (MethodDeclarationSyntax)node; + if (syntax.Arity == 0) { - LazyMethodChecks(); - return base.ReturnsVoid; + ReportErrorIfHasConstraints(syntax.ConstraintClauses, diagnostics); + return ImmutableArray.Empty; + } + else + { + return MakeTypeParameters(syntax, diagnostics); } } - private void MethodChecks(MethodDeclarationSyntax syntax, Binder withTypeParamsBinder, DiagnosticBag diagnostics) + protected override (TypeWithAnnotations ReturnType, ImmutableArray Parameters, bool IsVararg, ImmutableArray declaredConstraintsForOverrideOrImplement) MakeParametersAndBindReturnType(DiagnosticBag diagnostics) { - Debug.Assert(this.MethodKind != MethodKind.UserDefinedOperator, "SourceUserDefinedOperatorSymbolBase overrides this"); + var syntax = GetSyntax(); + var withTypeParamsBinder = this.DeclaringCompilation.GetBinderFactory(syntax.SyntaxTree).GetBinder(syntax.ReturnType, syntax, this); SyntaxToken arglistToken; @@ -159,7 +120,7 @@ private void MethodChecks(MethodDeclarationSyntax syntax, Binder withTypeParamsB // instance). Constraints are checked in AfterAddingTypeMembersChecks. var signatureBinder = withTypeParamsBinder.WithAdditionalFlagsAndContainingMemberOrLambda(BinderFlags.SuppressConstraintChecks, this); - _lazyParameters = ParameterHelpers.MakeParameters( + ImmutableArray parameters = ParameterHelpers.MakeParameters( signatureBinder, this, syntax.ParameterList, out arglistToken, allowRefOrOut: true, allowThis: true, @@ -169,12 +130,12 @@ private void MethodChecks(MethodDeclarationSyntax syntax, Binder withTypeParamsB _lazyIsVararg = (arglistToken.Kind() == SyntaxKind.ArgListKeyword); RefKind refKind; var returnTypeSyntax = syntax.ReturnType.SkipRef(out refKind); - _lazyReturnType = signatureBinder.BindType(returnTypeSyntax, diagnostics); + TypeWithAnnotations returnType = signatureBinder.BindType(returnTypeSyntax, diagnostics); // span-like types are returnable in general - if (_lazyReturnType.IsRestrictedType(ignoreSpanLikeTypes: true)) + if (returnType.IsRestrictedType(ignoreSpanLikeTypes: true)) { - if (_lazyReturnType.SpecialType == SpecialType.System_TypedReference && + if (returnType.SpecialType == SpecialType.System_TypedReference && (this.ContainingType.SpecialType == SpecialType.System_TypedReference || this.ContainingType.SpecialType == SpecialType.System_ArgIterator)) { // Two special cases: methods in the special types TypedReference and ArgIterator are allowed to return TypedReference @@ -182,27 +143,11 @@ private void MethodChecks(MethodDeclarationSyntax syntax, Binder withTypeParamsB else { // The return type of a method, delegate, or function pointer cannot be '{0}' - diagnostics.Add(ErrorCode.ERR_MethodReturnCantBeRefAny, syntax.ReturnType.Location, _lazyReturnType.Type); + diagnostics.Add(ErrorCode.ERR_MethodReturnCantBeRefAny, syntax.ReturnType.Location, returnType.Type); } } - var returnsVoid = _lazyReturnType.IsVoidType(); - if (this.RefKind != RefKind.None && returnsVoid) - { - Debug.Assert(returnTypeSyntax.HasErrors); - } - - // set ReturnsVoid flag - this.SetReturnsVoid(returnsVoid); - - this.CheckEffectiveAccessibility(_lazyReturnType, _lazyParameters, diagnostics); - - var location = locations[0]; - // Checks taken from MemberDefiner::defineMethod - if (this.Name == WellKnownMemberNames.DestructorName && this.ParameterCount == 0 && this.Arity == 0 && this.ReturnsVoid) - { - diagnostics.Add(ErrorCode.WRN_FinalizeMethod, location); - } + Debug.Assert(this.RefKind == RefKind.None || !returnType.IsVoidType() || returnTypeSyntax.HasErrors); ImmutableArray declaredConstraints = default; @@ -220,21 +165,50 @@ private void MethodChecks(MethodDeclarationSyntax syntax, Binder withTypeParamsB IReadOnlyDictionary isValueTypeOverride = null; declaredConstraints = signatureBinder.WithAdditionalFlags(BinderFlags.GenericConstraintsClause | BinderFlags.SuppressConstraintChecks). - BindTypeParameterConstraintClauses(this, _typeParameters, syntax.TypeParameterList, syntax.ConstraintClauses, + BindTypeParameterConstraintClauses(this, TypeParameters, syntax.TypeParameterList, syntax.ConstraintClauses, ref isValueTypeOverride, diagnostics, isForOverride: true); } // Force resolution of nullable type parameter used in the signature of an override or explicit interface implementation // based on constraints specified by the declaration. - foreach (var param in _lazyParameters) + foreach (var param in parameters) { forceMethodTypeParameters(param.TypeWithAnnotations, this, declaredConstraints); } - forceMethodTypeParameters(_lazyReturnType, this, declaredConstraints); + forceMethodTypeParameters(returnType, this, declaredConstraints); } + + return (returnType, parameters, _lazyIsVararg, declaredConstraints); + + static void forceMethodTypeParameters(TypeWithAnnotations type, SourceOrdinaryMethodSymbol method, ImmutableArray declaredConstraints) + { + type.VisitType(null, (type, args, unused2) => + { + if (type.DefaultType is TypeParameterSymbol typeParameterSymbol && typeParameterSymbol.DeclaringMethod == (object)args.method) + { + if (!args.declaredConstraints.IsDefault && + (args.declaredConstraints[typeParameterSymbol.Ordinal].Constraints & TypeParameterConstraintKind.ReferenceType) != 0) + { + type.TryForceResolveAsNullableReferenceType(); + } + else + { + type.TryForceResolveAsNullableValueType(); + } + } + return false; + }, typePredicate: null, arg: (method, declaredConstraints), canDigThroughNullable: false, useDefaultType: true); + } + } + + protected override void ExtensionMethodChecks(DiagnosticBag diagnostics) + { + var syntax = GetSyntax(); + var location = locations[0]; + // errors relevant for extension methods if (IsExtensionMethod) { @@ -276,7 +250,7 @@ private void MethodChecks(MethodDeclarationSyntax syntax, Binder withTypeParamsB else { // Verify ExtensionAttribute is available. - var attributeConstructor = withTypeParamsBinder.Compilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_ExtensionAttribute__ctor); + var attributeConstructor = DeclaringCompilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_ExtensionAttribute__ctor); if ((object)attributeConstructor == null) { var memberDescriptor = WellKnownMembers.GetDescriptor(WellKnownMember.System_Runtime_CompilerServices_ExtensionAttribute__ctor); @@ -288,205 +262,19 @@ private void MethodChecks(MethodDeclarationSyntax syntax, Binder withTypeParamsB } } } - - if (IsPartial) - { - if (MethodKind == MethodKind.ExplicitInterfaceImplementation) - { - diagnostics.Add(ErrorCode.ERR_PartialMethodNotExplicit, location); - } - - if (!ContainingType.IsPartial()) - { - diagnostics.Add(ErrorCode.ERR_PartialMethodOnlyInPartialClass, location); - } - } - - if (!IsPartial) - { - LazyAsyncMethodChecks(CancellationToken.None); - Debug.Assert(state.HasComplete(CompletionPart.FinishAsyncMethodChecks)); - } - - // The runtime will not treat this method as an override or implementation of another - // method unless both the signatures and the custom modifiers match. Hence, in the - // case of overrides and *explicit* implementations, we need to copy the custom modifiers - // that are in the signature of the overridden/implemented method. (From source, we know - // that there can only be one such method, so there are no conflicts.) This is - // unnecessary for implicit implementations because, if the custom modifiers don't match, - // we'll insert a bridge method (an explicit implementation that delegates to the implicit - // implementation) with the correct custom modifiers - // (see SourceMemberContainerTypeSymbol.SynthesizeInterfaceMemberImplementation). - - // This value may not be correct, but we need something while we compute this.OverriddenMethod. - // May be re-assigned below. - Debug.Assert(_lazyReturnType.CustomModifiers.IsEmpty); - _lazyRefCustomModifiers = ImmutableArray.Empty; - - MethodSymbol overriddenOrExplicitlyImplementedMethod = null; - - // Note: we're checking if the syntax indicates explicit implementation rather, - // than if explicitInterfaceType is null because we don't want to look for an - // overridden property if this is supposed to be an explicit implementation. - if (syntax.ExplicitInterfaceSpecifier == null) - { - Debug.Assert(_lazyExplicitInterfaceImplementations.IsDefault); - _lazyExplicitInterfaceImplementations = ImmutableArray.Empty; - - // If this method is an override, we may need to copy custom modifiers from - // the overridden method (so that the runtime will recognize it as an override). - // We check for this case here, while we can still modify the parameters and - // return type without losing the appearance of immutability. - if (this.IsOverride) - { - // This computation will necessarily be performed with partially incomplete - // information. There is no way we can determine the complete signature - // (i.e. including custom modifiers) until we have found the method that - // this method overrides. To accommodate this, MethodSymbol.OverriddenOrHiddenMembers - // is written to allow relaxed matching of custom modifiers for source methods, - // on the assumption that they will be updated appropriately. - overriddenOrExplicitlyImplementedMethod = this.OverriddenMethod; - - if ((object)overriddenOrExplicitlyImplementedMethod != null) - { - CustomModifierUtils.CopyMethodCustomModifiers(overriddenOrExplicitlyImplementedMethod, this, out _lazyReturnType, - out _lazyRefCustomModifiers, - out _lazyParameters, alsoCopyParamsModifier: true); - } - } - else if (_refKind == RefKind.RefReadOnly) - { - var modifierType = withTypeParamsBinder.GetWellKnownType(WellKnownType.System_Runtime_InteropServices_InAttribute, diagnostics, syntax.ReturnType); - - _lazyRefCustomModifiers = ImmutableArray.Create(CSharpCustomModifier.CreateRequired(modifierType)); - } - } - else if ((object)_explicitInterfaceType != null) - { - //do this last so that it can assume the method symbol is constructed (except for ExplicitInterfaceImplementation) - overriddenOrExplicitlyImplementedMethod = this.FindExplicitlyImplementedMethod(_explicitInterfaceType, syntax.Identifier.ValueText, syntax.ExplicitInterfaceSpecifier, diagnostics); - - if ((object)overriddenOrExplicitlyImplementedMethod != null) - { - Debug.Assert(_lazyExplicitInterfaceImplementations.IsDefault); - _lazyExplicitInterfaceImplementations = ImmutableArray.Create(overriddenOrExplicitlyImplementedMethod); - - CustomModifierUtils.CopyMethodCustomModifiers(overriddenOrExplicitlyImplementedMethod, this, out _lazyReturnType, - out _lazyRefCustomModifiers, - out _lazyParameters, alsoCopyParamsModifier: false); - this.FindExplicitlyImplementedMemberVerification(overriddenOrExplicitlyImplementedMethod, diagnostics); - TypeSymbol.CheckNullableReferenceTypeMismatchOnImplementingMember(this.ContainingType, this, overriddenOrExplicitlyImplementedMethod, isExplicit: true, diagnostics); - } - else - { - Debug.Assert(_lazyExplicitInterfaceImplementations.IsDefault); - _lazyExplicitInterfaceImplementations = ImmutableArray.Empty; - - Debug.Assert(_lazyReturnType.CustomModifiers.IsEmpty); - } - } - - if (!declaredConstraints.IsDefault && overriddenOrExplicitlyImplementedMethod is object) - { - for (int i = 0; i < declaredConstraints.Length; i++) - { - ErrorCode report; - - switch (declaredConstraints[i].Constraints & (TypeParameterConstraintKind.ReferenceType | TypeParameterConstraintKind.ValueType)) - { - case TypeParameterConstraintKind.ReferenceType: - if (!_typeParameters[i].IsReferenceType) - { - report = ErrorCode.ERR_OverrideRefConstraintNotSatisfied; - break; - } - continue; - case TypeParameterConstraintKind.ValueType: - if (!_typeParameters[i].IsNonNullableValueType()) - { - report = ErrorCode.ERR_OverrideValConstraintNotSatisfied; - break; - } - continue; - default: - continue; - } - - diagnostics.Add(report, _typeParameters[i].Locations[0], this, _typeParameters[i], - overriddenOrExplicitlyImplementedMethod.TypeParameters[i], overriddenOrExplicitlyImplementedMethod); - } - } - - CheckModifiers(syntax.ExplicitInterfaceSpecifier != null, _hasAnyBody, location, diagnostics); - - return; - - static void forceMethodTypeParameters(TypeWithAnnotations type, SourceOrdinaryMethodSymbol method, ImmutableArray declaredConstraints) - { - type.VisitType(null, (type, args, unused2) => - { - if (type.DefaultType is TypeParameterSymbol typeParameterSymbol && typeParameterSymbol.DeclaringMethod == (object)args.method) - { - if (!args.declaredConstraints.IsDefault && - (args.declaredConstraints[typeParameterSymbol.Ordinal].Constraints & TypeParameterConstraintKind.ReferenceType) != 0) - { - type.TryForceResolveAsNullableReferenceType(); - } - else - { - type.TryForceResolveAsNullableValueType(); - } - } - return false; - }, typePredicate: null, arg: (method, declaredConstraints), canDigThroughNullable: false, useDefaultType: true); - } } - protected sealed override void LazyAsyncMethodChecks(CancellationToken cancellationToken) + protected override MethodSymbol FindExplicitlyImplementedMethod(DiagnosticBag diagnostics) { - Debug.Assert(this.IsPartial == state.HasComplete(CompletionPart.FinishMethodChecks), - "Partial methods complete method checks during construction. " + - "Other methods can't complete method checks before executing this method."); - - if (!this.IsAsync) - { - CompleteAsyncMethodChecks(diagnosticsOpt: null, cancellationToken: cancellationToken); - return; - } - - DiagnosticBag diagnostics = DiagnosticBag.GetInstance(); - AsyncMethodChecks(diagnostics); - - CompleteAsyncMethodChecks(diagnostics, cancellationToken); - diagnostics.Free(); + var syntax = GetSyntax(); + return this.FindExplicitlyImplementedMethod(_explicitInterfaceType, syntax.Identifier.ValueText, syntax.ExplicitInterfaceSpecifier, diagnostics); } - private void CompleteAsyncMethodChecks(DiagnosticBag diagnosticsOpt, CancellationToken cancellationToken) - { - if (state.NotePartComplete(CompletionPart.StartAsyncMethodChecks)) - { - if (diagnosticsOpt != null) - { - AddDeclarationDiagnostics(diagnosticsOpt); - } - if (IsPartialDefinition) - { - DeclaringCompilation.SymbolDeclaredEvent(this); - } - state.NotePartComplete(CompletionPart.FinishAsyncMethodChecks); - } - else - { - state.SpinWaitComplete(CompletionPart.FinishAsyncMethodChecks, cancellationToken); - } - } + protected override Location ReturnTypeLocation => GetSyntax().ReturnType.Location; - protected override void MethodChecks(DiagnosticBag diagnostics) - { - var syntax = GetSyntax(); - var withTypeParametersBinder = this.DeclaringCompilation.GetBinderFactory(syntax.SyntaxTree).GetBinder(syntax.ReturnType, syntax, this); - MethodChecks(syntax, withTypeParametersBinder, diagnostics); - } + protected override TypeSymbol ExplicitInterfaceType => _explicitInterfaceType; + + protected override bool HasAnyBody => _hasAnyBody; internal MethodDeclarationSyntax GetSyntax() { @@ -494,9 +282,12 @@ internal MethodDeclarationSyntax GetSyntax() return (MethodDeclarationSyntax)syntaxReferenceOpt.GetSyntax(); } - public override ImmutableArray TypeParameters + protected override void CompleteAsyncMethodChecksBetweenStartAndFinish() { - get { return _typeParameters; } + if (IsPartialDefinition) + { + DeclaringCompilation.SymbolDeclaredEvent(this); + } } public override ImmutableArray GetTypeParameterConstraintClauses() @@ -535,30 +326,7 @@ public override bool IsVararg } } - public override ImmutableArray Locations - { - get - { - return this.locations; - } - } - - internal override int ParameterCount - { - get - { - return !_lazyParameters.IsDefault ? _lazyParameters.Length : GetSyntax().ParameterList.ParameterCount; - } - } - - public override ImmutableArray Parameters - { - get - { - LazyMethodChecks(); - return _lazyParameters; - } - } + protected override int GetParameterCountFromSyntax() => GetSyntax().ParameterList.ParameterCount; public override RefKind RefKind { @@ -568,15 +336,6 @@ public override RefKind RefKind } } - public override TypeWithAnnotations ReturnTypeWithAnnotations - { - get - { - LazyMethodChecks(); - return _lazyReturnType; - } - } - internal static void InitializePartialMethodParts(SourceOrdinaryMethodSymbol definition, SourceOrdinaryMethodSymbol implementation) { Debug.Assert(definition.IsPartialDefinition); @@ -685,40 +444,6 @@ public override string GetDocumentationCommentXml(CultureInfo preferredCulture = return SourceDocumentationCommentUtils.GetAndCacheDocumentationComment(SourcePartialImplementation ?? this, expandIncludes, ref lazyDocComment); } - internal override bool IsExplicitInterfaceImplementation - { - get - { - return this.GetSyntax().ExplicitInterfaceSpecifier != null; - } - } - - public override ImmutableArray ExplicitInterfaceImplementations - { - get - { - LazyMethodChecks(); - return _lazyExplicitInterfaceImplementations; - } - } - - public override ImmutableArray RefCustomModifiers - { - get - { - LazyMethodChecks(); - return _lazyRefCustomModifiers; - } - } - - public override string Name - { - get - { - return _name; - } - } - protected override SourceMemberMethodSymbol BoundAttributesSource { get @@ -758,98 +483,10 @@ internal override bool IsExpressionBodied get { return _isExpressionBodied; } } - internal bool HasExplicitAccessModifier { get; } - - private (DeclarationModifiers mods, bool hasExplicitAccessMod, bool modifierErrors) MakeModifiers(SyntaxTokenList modifiers, MethodKind methodKind, bool hasBody, Location location, DiagnosticBag diagnostics) - { - bool isInterface = this.ContainingType.IsInterface; - bool isExplicitInterfaceImplementation = methodKind == MethodKind.ExplicitInterfaceImplementation; - var defaultAccess = isInterface && modifiers.IndexOf(SyntaxKind.PartialKeyword) < 0 && !isExplicitInterfaceImplementation ? DeclarationModifiers.Public : DeclarationModifiers.Private; - - // Check that the set of modifiers is allowed - var allowedModifiers = DeclarationModifiers.Partial | DeclarationModifiers.Unsafe; - var defaultInterfaceImplementationModifiers = DeclarationModifiers.None; - - if (!isExplicitInterfaceImplementation) - { - allowedModifiers |= DeclarationModifiers.New | - DeclarationModifiers.Sealed | - DeclarationModifiers.Abstract | - DeclarationModifiers.Static | - DeclarationModifiers.Virtual | - DeclarationModifiers.AccessibilityMask; - - if (!isInterface) - { - allowedModifiers |= DeclarationModifiers.Override; - } - else - { - // This is needed to make sure we can detect 'public' modifier specified explicitly and - // check it against language version below. - defaultAccess = DeclarationModifiers.None; - - defaultInterfaceImplementationModifiers |= DeclarationModifiers.Sealed | - DeclarationModifiers.Abstract | - DeclarationModifiers.Static | - DeclarationModifiers.Virtual | - DeclarationModifiers.Extern | - DeclarationModifiers.Async | - DeclarationModifiers.Partial | - DeclarationModifiers.AccessibilityMask; - } - } - else if (isInterface) - { - Debug.Assert(isExplicitInterfaceImplementation); - allowedModifiers |= DeclarationModifiers.Abstract; - } - - allowedModifiers |= DeclarationModifiers.Extern | DeclarationModifiers.Async; - - if (ContainingType.IsStructType()) - { - allowedModifiers |= DeclarationModifiers.ReadOnly; - } - - // In order to detect whether explicit accessibility mods were provided, we pass the default value - // for 'defaultAccess' and manually add in the 'defaultAccess' flags after the call. - bool hasExplicitAccessMod; - var mods = ModifierUtils.MakeAndCheckNontypeMemberModifiers(modifiers, defaultAccess: DeclarationModifiers.None, allowedModifiers, location, diagnostics, out bool modifierErrors); - if ((mods & DeclarationModifiers.AccessibilityMask) == 0) - { - hasExplicitAccessMod = false; - mods |= defaultAccess; - } - else - { - hasExplicitAccessMod = true; - } - - this.CheckUnsafeModifier(mods, diagnostics); - - ModifierUtils.ReportDefaultInterfaceImplementationModifiers(hasBody, mods, - defaultInterfaceImplementationModifiers, - location, diagnostics); - - mods = AddImpliedModifiers(mods, isInterface, methodKind, hasBody); - return (mods, hasExplicitAccessMod, modifierErrors); - } - - private static DeclarationModifiers AddImpliedModifiers(DeclarationModifiers mods, bool containingTypeIsInterface, MethodKind methodKind, bool hasBody) + protected override DeclarationModifiers MakeDeclarationModifiers(DeclarationModifiers allowedModifiers, DiagnosticBag diagnostics) { - // Let's overwrite modifiers for interface and explicit interface implementation methods with what they are supposed to be. - // Proper errors must have been reported by now. - if (containingTypeIsInterface) - { - mods = ModifierUtils.AdjustModifiersForAnInterfaceMember(mods, hasBody, - methodKind == MethodKind.ExplicitInterfaceImplementation); - } - else if (methodKind == MethodKind.ExplicitInterfaceImplementation) - { - mods = (mods & ~DeclarationModifiers.AccessibilityMask) | DeclarationModifiers.Private; - } - return mods; + var syntax = GetSyntax(); + return ModifierUtils.MakeAndCheckNontypeMemberModifiers(syntax.Modifiers, defaultAccess: DeclarationModifiers.None, allowedModifiers, Locations[0], diagnostics, out _); } private ImmutableArray MakeTypeParameters(MethodDeclarationSyntax syntax, DiagnosticBag diagnostics) @@ -921,147 +558,6 @@ private ImmutableArray MakeTypeParameters(MethodDeclaration return result.ToImmutableAndFree(); } - private const DeclarationModifiers PartialMethodExtendedModifierMask = - DeclarationModifiers.Virtual | - DeclarationModifiers.Override | - DeclarationModifiers.New | - DeclarationModifiers.Sealed | - DeclarationModifiers.Extern; - - internal bool HasExtendedPartialModifier => (DeclarationModifiers & PartialMethodExtendedModifierMask) != 0; - - private void CheckModifiers(bool isExplicitInterfaceImplementation, bool hasBody, Location location, DiagnosticBag diagnostics) - { - bool isExplicitInterfaceImplementationInInterface = isExplicitInterfaceImplementation && ContainingType.IsInterface; - - if (IsPartial && HasExplicitAccessModifier) - { - Binder.CheckFeatureAvailability(SyntaxNode, MessageID.IDS_FeatureExtendedPartialMethods, diagnostics, location); - } - - if (IsPartial && IsAbstract) - { - diagnostics.Add(ErrorCode.ERR_PartialMethodInvalidModifier, location); - } - else if (IsPartial && !HasExplicitAccessModifier && !ReturnsVoid) - { - diagnostics.Add(ErrorCode.ERR_PartialMethodWithNonVoidReturnMustHaveAccessMods, location, this); - } - else if (IsPartial && !HasExplicitAccessModifier && HasExtendedPartialModifier) - { - diagnostics.Add(ErrorCode.ERR_PartialMethodWithExtendedModMustHaveAccessMods, location, this); - } - else if (IsPartial && !HasExplicitAccessModifier && Parameters.Any(p => p.RefKind == RefKind.Out)) - { - diagnostics.Add(ErrorCode.ERR_PartialMethodWithOutParamMustHaveAccessMods, location, this); - } - else if (this.DeclaredAccessibility == Accessibility.Private && (IsVirtual || (IsAbstract && !isExplicitInterfaceImplementationInInterface) || IsOverride)) - { - diagnostics.Add(ErrorCode.ERR_VirtualPrivate, location, this); - } - else if (IsStatic && (IsOverride || IsVirtual || IsAbstract)) - { - // A static member '{0}' cannot be marked as override, virtual, or abstract - diagnostics.Add(ErrorCode.ERR_StaticNotVirtual, location, this); - } - else if (IsOverride && (IsNew || IsVirtual)) - { - // A member '{0}' marked as override cannot be marked as new or virtual - diagnostics.Add(ErrorCode.ERR_OverrideNotNew, location, this); - } - else if (IsSealed && !IsOverride && !(isExplicitInterfaceImplementationInInterface && IsAbstract)) - { - // '{0}' cannot be sealed because it is not an override - diagnostics.Add(ErrorCode.ERR_SealedNonOverride, location, this); - } - else if (IsSealed && ContainingType.TypeKind == TypeKind.Struct) - { - // The modifier '{0}' is not valid for this item - diagnostics.Add(ErrorCode.ERR_BadMemberFlag, location, SyntaxFacts.GetText(SyntaxKind.SealedKeyword)); - } - else if (!ContainingType.IsInterfaceType() && _lazyReturnType.IsStatic) - { - // '{0}': static types cannot be used as return types - diagnostics.Add(ErrorCode.ERR_ReturnTypeIsStaticClass, location, _lazyReturnType.Type); - } - else if (IsAbstract && IsExtern) - { - diagnostics.Add(ErrorCode.ERR_AbstractAndExtern, location, this); - } - else if (IsAbstract && IsSealed && !isExplicitInterfaceImplementationInInterface) - { - diagnostics.Add(ErrorCode.ERR_AbstractAndSealed, location, this); - } - else if (IsAbstract && IsVirtual) - { - diagnostics.Add(ErrorCode.ERR_AbstractNotVirtual, location, this.Kind.Localize(), this); - } - else if (IsAbstract && ContainingType.TypeKind == TypeKind.Struct) - { - // The modifier '{0}' is not valid for this item - diagnostics.Add(ErrorCode.ERR_BadMemberFlag, location, SyntaxFacts.GetText(SyntaxKind.AbstractKeyword)); - } - else if (IsVirtual && ContainingType.TypeKind == TypeKind.Struct) - { - // The modifier '{0}' is not valid for this item - diagnostics.Add(ErrorCode.ERR_BadMemberFlag, location, SyntaxFacts.GetText(SyntaxKind.VirtualKeyword)); - } - else if (IsStatic && IsDeclaredReadOnly) - { - // Static member '{0}' cannot be marked 'readonly'. - diagnostics.Add(ErrorCode.ERR_StaticMemberCantBeReadOnly, location, this); - } - else if (IsAbstract && !ContainingType.IsAbstract && (ContainingType.TypeKind == TypeKind.Class || ContainingType.TypeKind == TypeKind.Submission)) - { - // '{0}' is abstract but it is contained in non-abstract class '{1}' - diagnostics.Add(ErrorCode.ERR_AbstractInConcreteClass, location, this, ContainingType); - } - else if (IsVirtual && ContainingType.IsSealed) - { - // '{0}' is a new virtual member in sealed class '{1}' - diagnostics.Add(ErrorCode.ERR_NewVirtualInSealed, location, this, ContainingType); - } - else if (!hasBody && IsAsync) - { - diagnostics.Add(ErrorCode.ERR_BadAsyncLacksBody, location); - } - else if (!hasBody && !IsExtern && !IsAbstract && !IsPartial && !IsExpressionBodied) - { - diagnostics.Add(ErrorCode.ERR_ConcreteMissingBody, location, this); - } - else if (ContainingType.IsSealed && this.DeclaredAccessibility.HasProtected() && !this.IsOverride) - { - diagnostics.Add(AccessCheck.GetProtectedMemberInSealedTypeError(ContainingType), location, this); - } - else if (ContainingType.IsStatic && !IsStatic) - { - diagnostics.Add(ErrorCode.ERR_InstanceMemberInStaticClass, location, Name); - } - else if (_lazyIsVararg && (IsGenericMethod || ContainingType.IsGenericType || _lazyParameters.Length > 0 && _lazyParameters[_lazyParameters.Length - 1].IsParams)) - { - diagnostics.Add(ErrorCode.ERR_BadVarargs, location); - } - else if (_lazyIsVararg && IsAsync) - { - diagnostics.Add(ErrorCode.ERR_VarargsAsync, location); - } - } - - internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) - { - base.AddSynthesizedAttributes(moduleBuilder, ref attributes); - - if (this.IsExtensionMethod) - { - // No need to check if [Extension] attribute was explicitly set since - // we'll issue CS1112 error in those cases and won't generate IL. - var compilation = this.DeclaringCompilation; - - AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute( - WellKnownMember.System_Runtime_CompilerServices_ExtensionAttribute__ctor)); - } - } - internal override void ForceComplete(SourceLocation locationOpt, CancellationToken cancellationToken) { var implementingPart = this.SourcePartialImplementation; @@ -1085,59 +581,23 @@ internal override void ForceComplete(SourceLocation locationOpt, CancellationTok this.SourcePartialImplementation?.IsDefinedInSourceTree(tree, definedWithinSpan, cancellationToken) == true; } - internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, DiagnosticBag diagnostics) + protected override void CheckConstraintsForExplicitInterfaceType(ConversionsBase conversions, DiagnosticBag diagnostics) { - base.AfterAddingTypeMembersChecks(conversions, diagnostics); - - var location = GetSyntax().ReturnType.Location; - 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. - if ((object)_explicitInterfaceType != null) { var syntax = this.GetSyntax(); Debug.Assert(syntax.ExplicitInterfaceSpecifier != null); - _explicitInterfaceType.CheckAllConstraints(compilation, conversions, new SourceLocation(syntax.ExplicitInterfaceSpecifier.Name), diagnostics); - } - - this.ReturnType.CheckAllConstraints(compilation, conversions, this.Locations[0], diagnostics); - - foreach (var parameter in this.Parameters) - { - parameter.Type.CheckAllConstraints(compilation, conversions, parameter.Locations[0], diagnostics); + _explicitInterfaceType.CheckAllConstraints(DeclaringCompilation, conversions, new SourceLocation(syntax.ExplicitInterfaceSpecifier.Name), diagnostics); } + } + protected override void PartialMethodChecks(DiagnosticBag diagnostics) + { var implementingPart = this.SourcePartialImplementation; if ((object)implementingPart != null) { PartialMethodChecks(this, implementingPart, diagnostics); } - - if (_refKind == RefKind.RefReadOnly) - { - compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: true); - } - - ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); - - if (ReturnType.ContainsNativeInteger()) - { - compilation.EnsureNativeIntegerAttributeExists(diagnostics, location, modifyCompilation: true); - } - - ParameterHelpers.EnsureNativeIntegerAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); - - if (compilation.ShouldEmitNullableAttributes(this) && ReturnTypeWithAnnotations.NeedsNullableAttribute()) - { - compilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); - } - - ParameterHelpers.EnsureNullableAttributeExists(compilation, this, Parameters, diagnostics, modifyCompilation: true); } /// diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs new file mode 100644 index 00000000000..b57067905ad --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceOrdinaryMethodSymbolBase.cs @@ -0,0 +1,668 @@ +// 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.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Threading; +using Microsoft.CodeAnalysis.CSharp.Emit; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + internal abstract class SourceOrdinaryMethodSymbolBase : SourceMemberMethodSymbol + { + private readonly ImmutableArray _typeParameters; + private readonly string _name; + + private ImmutableArray _lazyExplicitInterfaceImplementations; + private ImmutableArray _lazyRefCustomModifiers; + private ImmutableArray _lazyParameters; + private TypeWithAnnotations _lazyReturnType; + + protected SourceOrdinaryMethodSymbolBase( + NamedTypeSymbol containingType, + string name, + Location location, + CSharpSyntaxNode syntax, + MethodKind methodKind, + bool isIterator, + bool isExtensionMethod, + bool isPartial, + bool hasBody, + DiagnosticBag diagnostics) : + base(containingType, + syntax.GetReference(), + location, + isIterator) + { + _name = name; + + // The following two values are used to compute and store the initial value of the flags + // However, these two components are placeholders; the correct value will be + // computed lazily later and then the flags will be fixed up. + const bool returnsVoid = false; + + DeclarationModifiers declarationModifiers; + (declarationModifiers, HasExplicitAccessModifier) = this.MakeModifiers(methodKind, isPartial, hasBody, location, diagnostics); + + var isMetadataVirtualIgnoringModifiers = methodKind == MethodKind.ExplicitInterfaceImplementation; //explicit impls must be marked metadata virtual + + this.MakeFlags(methodKind, declarationModifiers, returnsVoid, isExtensionMethod, isMetadataVirtualIgnoringModifiers); + + _typeParameters = MakeTypeParameters(syntax, diagnostics); + + CheckFeatureAvailabilityAndRuntimeSupport(syntax, location, hasBody, diagnostics); + + if (hasBody) + { + CheckModifiersForBody(location, diagnostics); + } + + var info = ModifierUtils.CheckAccessibility(this.DeclarationModifiers, this, isExplicitInterfaceImplementation: methodKind == MethodKind.ExplicitInterfaceImplementation); + if (info != null) + { + diagnostics.Add(info, location); + } + } + + protected abstract ImmutableArray MakeTypeParameters(CSharpSyntaxNode node, DiagnosticBag diagnostics); + + public override bool ReturnsVoid + { + get + { + LazyMethodChecks(); + return base.ReturnsVoid; + } + } + + protected override void MethodChecks(DiagnosticBag diagnostics) + { + Debug.Assert(this.MethodKind != MethodKind.UserDefinedOperator, "SourceUserDefinedOperatorSymbolBase overrides this"); + + ImmutableArray declaredConstraints; + bool isVararg; + (_lazyReturnType, _lazyParameters, isVararg, declaredConstraints) = MakeParametersAndBindReturnType(diagnostics); + + // set ReturnsVoid flag + this.SetReturnsVoid(_lazyReturnType.IsVoidType()); + + this.CheckEffectiveAccessibility(_lazyReturnType, _lazyParameters, diagnostics); + + var location = locations[0]; + // Checks taken from MemberDefiner::defineMethod + if (this.Name == WellKnownMemberNames.DestructorName && this.ParameterCount == 0 && this.Arity == 0 && this.ReturnsVoid) + { + diagnostics.Add(ErrorCode.WRN_FinalizeMethod, location); + } + + ExtensionMethodChecks(diagnostics); + + if (IsPartial) + { + if (MethodKind == MethodKind.ExplicitInterfaceImplementation) + { + diagnostics.Add(ErrorCode.ERR_PartialMethodNotExplicit, location); + } + + if (!ContainingType.IsPartial()) + { + diagnostics.Add(ErrorCode.ERR_PartialMethodOnlyInPartialClass, location); + } + } + + if (!IsPartial) + { + LazyAsyncMethodChecks(CancellationToken.None); + Debug.Assert(state.HasComplete(CompletionPart.FinishAsyncMethodChecks)); + } + + // The runtime will not treat this method as an override or implementation of another + // method unless both the signatures and the custom modifiers match. Hence, in the + // case of overrides and *explicit* implementations, we need to copy the custom modifiers + // that are in the signature of the overridden/implemented method. (From source, we know + // that there can only be one such method, so there are no conflicts.) This is + // unnecessary for implicit implementations because, if the custom modifiers don't match, + // we'll insert a bridge method (an explicit implementation that delegates to the implicit + // implementation) with the correct custom modifiers + // (see SourceMemberContainerTypeSymbol.SynthesizeInterfaceMemberImplementation). + + // This value may not be correct, but we need something while we compute this.OverriddenMethod. + // May be re-assigned below. + Debug.Assert(_lazyReturnType.CustomModifiers.IsEmpty); + _lazyRefCustomModifiers = ImmutableArray.Empty; + + MethodSymbol overriddenOrExplicitlyImplementedMethod = null; + + // Note: we're checking if the syntax indicates explicit implementation rather, + // than if explicitInterfaceType is null because we don't want to look for an + // overridden property if this is supposed to be an explicit implementation. + if (MethodKind != MethodKind.ExplicitInterfaceImplementation) + { + Debug.Assert(_lazyExplicitInterfaceImplementations.IsDefault); + _lazyExplicitInterfaceImplementations = ImmutableArray.Empty; + + // If this method is an override, we may need to copy custom modifiers from + // the overridden method (so that the runtime will recognize it as an override). + // We check for this case here, while we can still modify the parameters and + // return type without losing the appearance of immutability. + if (this.IsOverride) + { + // This computation will necessarily be performed with partially incomplete + // information. There is no way we can determine the complete signature + // (i.e. including custom modifiers) until we have found the method that + // this method overrides. To accommodate this, MethodSymbol.OverriddenOrHiddenMembers + // is written to allow relaxed matching of custom modifiers for source methods, + // on the assumption that they will be updated appropriately. + overriddenOrExplicitlyImplementedMethod = this.OverriddenMethod; + + if ((object)overriddenOrExplicitlyImplementedMethod != null) + { + CustomModifierUtils.CopyMethodCustomModifiers(overriddenOrExplicitlyImplementedMethod, this, out _lazyReturnType, + out _lazyRefCustomModifiers, + out _lazyParameters, alsoCopyParamsModifier: true); + } + } + else if (RefKind == RefKind.RefReadOnly) + { + var modifierType = Binder.GetWellKnownType(DeclaringCompilation, WellKnownType.System_Runtime_InteropServices_InAttribute, diagnostics, ReturnTypeLocation); + + _lazyRefCustomModifiers = ImmutableArray.Create(CSharpCustomModifier.CreateRequired(modifierType)); + } + } + else if ((object)ExplicitInterfaceType != null) + { + //do this last so that it can assume the method symbol is constructed (except for ExplicitInterfaceImplementation) + overriddenOrExplicitlyImplementedMethod = FindExplicitlyImplementedMethod(diagnostics); + + if ((object)overriddenOrExplicitlyImplementedMethod != null) + { + Debug.Assert(_lazyExplicitInterfaceImplementations.IsDefault); + _lazyExplicitInterfaceImplementations = ImmutableArray.Create(overriddenOrExplicitlyImplementedMethod); + + CustomModifierUtils.CopyMethodCustomModifiers(overriddenOrExplicitlyImplementedMethod, this, out _lazyReturnType, + out _lazyRefCustomModifiers, + out _lazyParameters, alsoCopyParamsModifier: false); + this.FindExplicitlyImplementedMemberVerification(overriddenOrExplicitlyImplementedMethod, diagnostics); + TypeSymbol.CheckNullableReferenceTypeMismatchOnImplementingMember(this.ContainingType, this, overriddenOrExplicitlyImplementedMethod, isExplicit: true, diagnostics); + } + else + { + Debug.Assert(_lazyExplicitInterfaceImplementations.IsDefault); + _lazyExplicitInterfaceImplementations = ImmutableArray.Empty; + + Debug.Assert(_lazyReturnType.CustomModifiers.IsEmpty); + } + } + + if (!declaredConstraints.IsDefault && overriddenOrExplicitlyImplementedMethod is object) + { + for (int i = 0; i < declaredConstraints.Length; i++) + { + ErrorCode report; + + switch (declaredConstraints[i].Constraints & (TypeParameterConstraintKind.ReferenceType | TypeParameterConstraintKind.ValueType)) + { + case TypeParameterConstraintKind.ReferenceType: + if (!_typeParameters[i].IsReferenceType) + { + report = ErrorCode.ERR_OverrideRefConstraintNotSatisfied; + break; + } + continue; + case TypeParameterConstraintKind.ValueType: + if (!_typeParameters[i].IsNonNullableValueType()) + { + report = ErrorCode.ERR_OverrideValConstraintNotSatisfied; + break; + } + continue; + default: + continue; + } + + diagnostics.Add(report, _typeParameters[i].Locations[0], this, _typeParameters[i], + overriddenOrExplicitlyImplementedMethod.TypeParameters[i], overriddenOrExplicitlyImplementedMethod); + } + } + + CheckModifiers(MethodKind == MethodKind.ExplicitInterfaceImplementation, isVararg, HasAnyBody, location, diagnostics); + + return; + } + + protected abstract (TypeWithAnnotations ReturnType, ImmutableArray Parameters, bool IsVararg, ImmutableArray declaredConstraintsForOverrideOrImplement) MakeParametersAndBindReturnType(DiagnosticBag diagnostics); + + protected abstract void ExtensionMethodChecks(DiagnosticBag diagnostics); + + protected abstract MethodSymbol FindExplicitlyImplementedMethod(DiagnosticBag diagnostics); + + protected abstract Location ReturnTypeLocation { get; } + + protected abstract TypeSymbol ExplicitInterfaceType { get; } + + protected abstract bool HasAnyBody { get; } + + protected sealed override void LazyAsyncMethodChecks(CancellationToken cancellationToken) + { + Debug.Assert(this.IsPartial == state.HasComplete(CompletionPart.FinishMethodChecks), + "Partial methods complete method checks during construction. " + + "Other methods can't complete method checks before executing this method."); + + if (!this.IsAsync) + { + CompleteAsyncMethodChecks(diagnosticsOpt: null, cancellationToken: cancellationToken); + return; + } + + DiagnosticBag diagnostics = DiagnosticBag.GetInstance(); + AsyncMethodChecks(diagnostics); + + CompleteAsyncMethodChecks(diagnostics, cancellationToken); + diagnostics.Free(); + } + + private void CompleteAsyncMethodChecks(DiagnosticBag diagnosticsOpt, CancellationToken cancellationToken) + { + if (state.NotePartComplete(CompletionPart.StartAsyncMethodChecks)) + { + if (diagnosticsOpt != null) + { + AddDeclarationDiagnostics(diagnosticsOpt); + } + + CompleteAsyncMethodChecksBetweenStartAndFinish(); + state.NotePartComplete(CompletionPart.FinishAsyncMethodChecks); + } + else + { + state.SpinWaitComplete(CompletionPart.FinishAsyncMethodChecks, cancellationToken); + } + } + + protected virtual void CompleteAsyncMethodChecksBetweenStartAndFinish() + { + } + + public override ImmutableArray TypeParameters + { + get { return _typeParameters; } + } + + public override ImmutableArray Locations + { + get + { + return this.locations; + } + } + + internal override int ParameterCount + { + get + { + if (!_lazyParameters.IsDefault) + { + int result = _lazyParameters.Length; + Debug.Assert(result == GetParameterCountFromSyntax()); + return result; + } + + return GetParameterCountFromSyntax(); + } + } + + protected abstract int GetParameterCountFromSyntax(); + + public override ImmutableArray Parameters + { + get + { + LazyMethodChecks(); + return _lazyParameters; + } + } + + public override TypeWithAnnotations ReturnTypeWithAnnotations + { + get + { + LazyMethodChecks(); + return _lazyReturnType; + } + } + + public abstract override string GetDocumentationCommentXml(CultureInfo preferredCulture = null, bool expandIncludes = false, CancellationToken cancellationToken = default(CancellationToken)); + + internal override bool IsExplicitInterfaceImplementation + { + get + { + return MethodKind == MethodKind.ExplicitInterfaceImplementation; + } + } + + public override ImmutableArray ExplicitInterfaceImplementations + { + get + { + LazyMethodChecks(); + return _lazyExplicitInterfaceImplementations; + } + } + + public override ImmutableArray RefCustomModifiers + { + get + { + LazyMethodChecks(); + return _lazyRefCustomModifiers; + } + } + + public override string Name + { + get + { + return _name; + } + } + + protected abstract override SourceMemberMethodSymbol BoundAttributesSource { get; } + + internal abstract override OneOrMany> GetAttributeDeclarations(); + + internal bool HasExplicitAccessModifier { get; } + + private (DeclarationModifiers mods, bool hasExplicitAccessMod) MakeModifiers(MethodKind methodKind, bool isPartial, bool hasBody, Location location, DiagnosticBag diagnostics) + { + bool isInterface = this.ContainingType.IsInterface; + bool isExplicitInterfaceImplementation = methodKind == MethodKind.ExplicitInterfaceImplementation; + var defaultAccess = isInterface && isPartial && !isExplicitInterfaceImplementation ? DeclarationModifiers.Public : DeclarationModifiers.Private; + + // Check that the set of modifiers is allowed + var allowedModifiers = DeclarationModifiers.Partial | DeclarationModifiers.Unsafe; + var defaultInterfaceImplementationModifiers = DeclarationModifiers.None; + + if (!isExplicitInterfaceImplementation) + { + allowedModifiers |= DeclarationModifiers.New | + DeclarationModifiers.Sealed | + DeclarationModifiers.Abstract | + DeclarationModifiers.Static | + DeclarationModifiers.Virtual | + DeclarationModifiers.AccessibilityMask; + + if (!isInterface) + { + allowedModifiers |= DeclarationModifiers.Override; + } + else + { + // This is needed to make sure we can detect 'public' modifier specified explicitly and + // check it against language version below. + defaultAccess = DeclarationModifiers.None; + + defaultInterfaceImplementationModifiers |= DeclarationModifiers.Sealed | + DeclarationModifiers.Abstract | + DeclarationModifiers.Static | + DeclarationModifiers.Virtual | + DeclarationModifiers.Extern | + DeclarationModifiers.Async | + DeclarationModifiers.Partial | + DeclarationModifiers.AccessibilityMask; + } + } + else if (isInterface) + { + Debug.Assert(isExplicitInterfaceImplementation); + allowedModifiers |= DeclarationModifiers.Abstract; + } + + allowedModifiers |= DeclarationModifiers.Extern | DeclarationModifiers.Async; + + if (ContainingType.IsStructType()) + { + allowedModifiers |= DeclarationModifiers.ReadOnly; + } + + // In order to detect whether explicit accessibility mods were provided, we pass the default value + // for 'defaultAccess' and manually add in the 'defaultAccess' flags after the call. + bool hasExplicitAccessMod; + DeclarationModifiers mods = MakeDeclarationModifiers(allowedModifiers, diagnostics); + if ((mods & DeclarationModifiers.AccessibilityMask) == 0) + { + hasExplicitAccessMod = false; + mods |= defaultAccess; + } + else + { + hasExplicitAccessMod = true; + } + + this.CheckUnsafeModifier(mods, diagnostics); + + ModifierUtils.ReportDefaultInterfaceImplementationModifiers(hasBody, mods, + defaultInterfaceImplementationModifiers, + location, diagnostics); + + mods = AddImpliedModifiers(mods, isInterface, methodKind, hasBody); + return (mods, hasExplicitAccessMod); + } + + protected abstract DeclarationModifiers MakeDeclarationModifiers(DeclarationModifiers allowedModifiers, DiagnosticBag diagnostics); + + private static DeclarationModifiers AddImpliedModifiers(DeclarationModifiers mods, bool containingTypeIsInterface, MethodKind methodKind, bool hasBody) + { + // Let's overwrite modifiers for interface and explicit interface implementation methods with what they are supposed to be. + // Proper errors must have been reported by now. + if (containingTypeIsInterface) + { + mods = ModifierUtils.AdjustModifiersForAnInterfaceMember(mods, hasBody, + methodKind == MethodKind.ExplicitInterfaceImplementation); + } + else if (methodKind == MethodKind.ExplicitInterfaceImplementation) + { + mods = (mods & ~DeclarationModifiers.AccessibilityMask) | DeclarationModifiers.Private; + } + return mods; + } + + private const DeclarationModifiers PartialMethodExtendedModifierMask = + DeclarationModifiers.Virtual | + DeclarationModifiers.Override | + DeclarationModifiers.New | + DeclarationModifiers.Sealed | + DeclarationModifiers.Extern; + + internal bool HasExtendedPartialModifier => (DeclarationModifiers & PartialMethodExtendedModifierMask) != 0; + + private void CheckModifiers(bool isExplicitInterfaceImplementation, bool isVararg, bool hasBody, Location location, DiagnosticBag diagnostics) + { + bool isExplicitInterfaceImplementationInInterface = isExplicitInterfaceImplementation && ContainingType.IsInterface; + + if (IsPartial && HasExplicitAccessModifier) + { + Binder.CheckFeatureAvailability(SyntaxNode, MessageID.IDS_FeatureExtendedPartialMethods, diagnostics, location); + } + + if (IsPartial && IsAbstract) + { + diagnostics.Add(ErrorCode.ERR_PartialMethodInvalidModifier, location); + } + else if (IsPartial && !HasExplicitAccessModifier && !ReturnsVoid) + { + diagnostics.Add(ErrorCode.ERR_PartialMethodWithNonVoidReturnMustHaveAccessMods, location, this); + } + else if (IsPartial && !HasExplicitAccessModifier && HasExtendedPartialModifier) + { + diagnostics.Add(ErrorCode.ERR_PartialMethodWithExtendedModMustHaveAccessMods, location, this); + } + else if (IsPartial && !HasExplicitAccessModifier && Parameters.Any(p => p.RefKind == RefKind.Out)) + { + diagnostics.Add(ErrorCode.ERR_PartialMethodWithOutParamMustHaveAccessMods, location, this); + } + else if (this.DeclaredAccessibility == Accessibility.Private && (IsVirtual || (IsAbstract && !isExplicitInterfaceImplementationInInterface) || IsOverride)) + { + diagnostics.Add(ErrorCode.ERR_VirtualPrivate, location, this); + } + else if (IsStatic && (IsOverride || IsVirtual || IsAbstract)) + { + // A static member '{0}' cannot be marked as override, virtual, or abstract + diagnostics.Add(ErrorCode.ERR_StaticNotVirtual, location, this); + } + else if (IsOverride && (IsNew || IsVirtual)) + { + // A member '{0}' marked as override cannot be marked as new or virtual + diagnostics.Add(ErrorCode.ERR_OverrideNotNew, location, this); + } + else if (IsSealed && !IsOverride && !(isExplicitInterfaceImplementationInInterface && IsAbstract)) + { + // '{0}' cannot be sealed because it is not an override + diagnostics.Add(ErrorCode.ERR_SealedNonOverride, location, this); + } + else if (IsSealed && ContainingType.TypeKind == TypeKind.Struct) + { + // The modifier '{0}' is not valid for this item + diagnostics.Add(ErrorCode.ERR_BadMemberFlag, location, SyntaxFacts.GetText(SyntaxKind.SealedKeyword)); + } + else if (!ContainingType.IsInterfaceType() && _lazyReturnType.IsStatic) + { + // '{0}': static types cannot be used as return types + diagnostics.Add(ErrorCode.ERR_ReturnTypeIsStaticClass, location, _lazyReturnType.Type); + } + else if (IsAbstract && IsExtern) + { + diagnostics.Add(ErrorCode.ERR_AbstractAndExtern, location, this); + } + else if (IsAbstract && IsSealed && !isExplicitInterfaceImplementationInInterface) + { + diagnostics.Add(ErrorCode.ERR_AbstractAndSealed, location, this); + } + else if (IsAbstract && IsVirtual) + { + diagnostics.Add(ErrorCode.ERR_AbstractNotVirtual, location, this.Kind.Localize(), this); + } + else if (IsAbstract && ContainingType.TypeKind == TypeKind.Struct) + { + // The modifier '{0}' is not valid for this item + diagnostics.Add(ErrorCode.ERR_BadMemberFlag, location, SyntaxFacts.GetText(SyntaxKind.AbstractKeyword)); + } + else if (IsVirtual && ContainingType.TypeKind == TypeKind.Struct) + { + // The modifier '{0}' is not valid for this item + diagnostics.Add(ErrorCode.ERR_BadMemberFlag, location, SyntaxFacts.GetText(SyntaxKind.VirtualKeyword)); + } + else if (IsStatic && IsDeclaredReadOnly) + { + // Static member '{0}' cannot be marked 'readonly'. + diagnostics.Add(ErrorCode.ERR_StaticMemberCantBeReadOnly, location, this); + } + else if (IsAbstract && !ContainingType.IsAbstract && (ContainingType.TypeKind == TypeKind.Class || ContainingType.TypeKind == TypeKind.Submission)) + { + // '{0}' is abstract but it is contained in non-abstract class '{1}' + diagnostics.Add(ErrorCode.ERR_AbstractInConcreteClass, location, this, ContainingType); + } + else if (IsVirtual && ContainingType.IsSealed) + { + // '{0}' is a new virtual member in sealed class '{1}' + diagnostics.Add(ErrorCode.ERR_NewVirtualInSealed, location, this, ContainingType); + } + else if (!hasBody && IsAsync) + { + diagnostics.Add(ErrorCode.ERR_BadAsyncLacksBody, location); + } + else if (!hasBody && !IsExtern && !IsAbstract && !IsPartial && !IsExpressionBodied) + { + diagnostics.Add(ErrorCode.ERR_ConcreteMissingBody, location, this); + } + else if (ContainingType.IsSealed && this.DeclaredAccessibility.HasProtected() && !this.IsOverride) + { + diagnostics.Add(AccessCheck.GetProtectedMemberInSealedTypeError(ContainingType), location, this); + } + else if (ContainingType.IsStatic && !IsStatic) + { + diagnostics.Add(ErrorCode.ERR_InstanceMemberInStaticClass, location, Name); + } + else if (isVararg && (IsGenericMethod || ContainingType.IsGenericType || _lazyParameters.Length > 0 && _lazyParameters[_lazyParameters.Length - 1].IsParams)) + { + diagnostics.Add(ErrorCode.ERR_BadVarargs, location); + } + else if (isVararg && IsAsync) + { + diagnostics.Add(ErrorCode.ERR_VarargsAsync, location); + } + } + + internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder attributes) + { + base.AddSynthesizedAttributes(moduleBuilder, ref attributes); + + if (this.IsExtensionMethod) + { + // No need to check if [Extension] attribute was explicitly set since + // we'll issue CS1112 error in those cases and won't generate IL. + var compilation = this.DeclaringCompilation; + + AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute( + WellKnownMember.System_Runtime_CompilerServices_ExtensionAttribute__ctor)); + } + } + + internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions, DiagnosticBag diagnostics) + { + base.AfterAddingTypeMembersChecks(conversions, diagnostics); + + var location = ReturnTypeLocation; + 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. + CheckConstraintsForExplicitInterfaceType(conversions, diagnostics); + + this.ReturnType.CheckAllConstraints(compilation, conversions, this.Locations[0], diagnostics); + + foreach (var parameter in this.Parameters) + { + parameter.Type.CheckAllConstraints(compilation, conversions, parameter.Locations[0], diagnostics); + } + + PartialMethodChecks(diagnostics); + + if (RefKind == RefKind.RefReadOnly) + { + compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: true); + } + + ParameterHelpers.EnsureIsReadOnlyAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); + + if (ReturnType.ContainsNativeInteger()) + { + compilation.EnsureNativeIntegerAttributeExists(diagnostics, location, modifyCompilation: true); + } + + ParameterHelpers.EnsureNativeIntegerAttributeExists(compilation, Parameters, diagnostics, modifyCompilation: true); + + if (compilation.ShouldEmitNullableAttributes(this) && ReturnTypeWithAnnotations.NeedsNullableAttribute()) + { + compilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true); + } + + ParameterHelpers.EnsureNullableAttributeExists(compilation, this, Parameters, diagnostics, modifyCompilation: true); + } + + protected abstract void CheckConstraintsForExplicitInterfaceType(ConversionsBase conversions, DiagnosticBag diagnostics); + + protected abstract void PartialMethodChecks(DiagnosticBag diagnostics); + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs index 761cb8c7f7b..7bd24deef3f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertyAccessorSymbol.cs @@ -174,7 +174,7 @@ internal sealed override ImmutableArray NotNullWhenFalseMembers isMetadataVirtualIgnoringModifiers: explicitInterfaceImplementations.Any()); CheckFeatureAvailabilityAndRuntimeSupport(syntax, location, hasBody: true, diagnostics: diagnostics); - CheckModifiersForBody(syntax, location, diagnostics); + CheckModifiersForBody(location, diagnostics); var info = ModifierUtils.CheckAccessibility(this.DeclarationModifiers, this, isExplicitInterfaceImplementation); if (info != null) @@ -248,7 +248,7 @@ internal sealed override ImmutableArray NotNullWhenFalseMembers if (hasBody || hasExpressionBody) { - CheckModifiersForBody(syntax, location, diagnostics); + CheckModifiersForBody(location, diagnostics); } var info = ModifierUtils.CheckAccessibility(this.DeclarationModifiers, this, isExplicitInterfaceImplementation); -- GitLab