Skip to content

Commit babf51e

Browse files
authored
Extensions: Adjust accessibility and file-type checks for extension members (#77657)
1 parent 45baf31 commit babf51e

File tree

7 files changed

+454
-57
lines changed

7 files changed

+454
-57
lines changed

src/Compilers/CSharp/Portable/Binder/Semantics/AccessCheck.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,9 @@ private static bool IsMemberAccessible(
327327
return true;
328328
}
329329

330+
// For the purpose of accessibility checks, extension members are considered to be declared within the enclosing static type
330331
return IsNonPublicMemberAccessible(
331-
containingType,
332+
containingType.IsExtension && containingType.ContainingType is { } extensionEnclosingType ? extensionEnclosingType : containingType,
332333
declaredAccessibility,
333334
within,
334335
throughTypeOpt,

src/Compilers/CSharp/Portable/Emitter/Model/NamedTypeSymbolAdapter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,7 @@ bool Cci.ITypeDefinition.IsSealed
628628
{
629629
Debug.Assert((object)method != null);
630630

631-
if ((alwaysIncludeConstructors && method.MethodKind == MethodKind.Constructor) || method.GetCciAdapter().ShouldInclude(context))
631+
if ((alwaysIncludeConstructors && method.MethodKind == MethodKind.Constructor) || method is SynthesizedExtensionMarker || method.GetCciAdapter().ShouldInclude(context))
632632
{
633633
yield return method.GetCciAdapter();
634634
}

src/Compilers/CSharp/Portable/Symbols/Extensions/SynthesizedExtensionMarker.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ internal SynthesizedExtensionMarker(SourceMemberContainerTypeSymbol extensionTyp
3030
return;
3131
}
3232

33-
private static DeclarationModifiers GetDeclarationModifiers() => DeclarationModifiers.Public | DeclarationModifiers.Static;
33+
private static DeclarationModifiers GetDeclarationModifiers() => DeclarationModifiers.Private | DeclarationModifiers.Static;
3434

3535
internal override bool HasSpecialName => true; // PROTOTYPE: reconcile with spec
3636

src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PENamedTypeSymbol.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ internal sealed override ParameterSymbol? ExtensionParameter
407407
var methodSymbol = getMarkerMethodSymbol(@this, uncommon);
408408

409409
// PROTOTYPE: do we want to tighten the flags check further? (require that type be sealed?)
410-
if (methodSymbol.DeclaredAccessibility != Accessibility.Public ||
410+
if (methodSymbol.DeclaredAccessibility != Accessibility.Private ||
411411
methodSymbol.IsGenericMethod ||
412412
!methodSymbol.IsStatic ||
413413
!methodSymbol.ReturnsVoid ||
@@ -2238,11 +2238,15 @@ private PooledDictionary<MethodDefinitionHandle, PEMethodSymbol> CreateMethods(A
22382238
// for ordinary embeddable struct types we import private members so that we can report appropriate errors if the structure is used
22392239
var isOrdinaryEmbeddableStruct = (this.TypeKind == TypeKind.Struct) && (this.SpecialType == Microsoft.CodeAnalysis.SpecialType.None) && this.ContainingAssembly.IsLinked;
22402240

2241+
MethodDefinitionHandle? extensionMarkerMethod = _lazyUncommonProperties?.lazyExtensionInfo?.MarkerMethod;
2242+
Debug.Assert(extensionMarkerMethod is not null || this.TypeKind is not TypeKind.Extension);
2243+
22412244
try
22422245
{
22432246
foreach (var methodHandle in module.GetMethodsOfTypeOrThrow(_handle))
22442247
{
2245-
if (isOrdinaryEmbeddableStruct || module.ShouldImportMethod(_handle, methodHandle, moduleSymbol.ImportOptions))
2248+
if (isOrdinaryEmbeddableStruct || module.ShouldImportMethod(_handle, methodHandle, moduleSymbol.ImportOptions) ||
2249+
extensionMarkerMethod == methodHandle)
22462250
{
22472251
var method = new PEMethodSymbol(moduleSymbol, this, methodHandle);
22482252
members.Add(method);

src/Compilers/CSharp/Portable/Symbols/Source/SourceMemberMethodSymbol.cs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,26 +378,42 @@ protected void CheckEffectiveAccessibility(TypeWithAnnotations returnType, Immut
378378
}
379379
}
380380

381+
if (!IsStatic && this.GetIsNewExtensionMember() && ContainingType.ExtensionParameter is { } extensionParameter)
382+
{
383+
if (!extensionParameter.TypeWithAnnotations.IsAtLeastAsVisibleAs(this, ref useSiteInfo))
384+
{
385+
// Inconsistent accessibility: parameter type '{1}' is less accessible than method '{0}'
386+
diagnostics.Add(code, GetFirstLocation(), this, extensionParameter.Type);
387+
}
388+
}
389+
381390
diagnostics.Add(GetFirstLocation(), useSiteInfo);
382391
}
383392

384393
protected void CheckFileTypeUsage(TypeWithAnnotations returnType, ImmutableArray<ParameterSymbol> parameters, BindingDiagnosticBag diagnostics)
385394
{
386-
if (ContainingType.HasFileLocalTypes())
395+
NamedTypeSymbol containingType = ContainingType;
396+
397+
if (containingType is { IsExtension: true, ContainingType: { } enclosing })
398+
{
399+
containingType = enclosing;
400+
}
401+
402+
if (containingType.HasFileLocalTypes())
387403
{
388404
return;
389405
}
390406

391407
if (returnType.Type.HasFileLocalTypes())
392408
{
393-
diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, GetFirstLocation(), returnType.Type, ContainingType);
409+
diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, GetFirstLocation(), returnType.Type, containingType);
394410
}
395411

396412
foreach (var param in parameters)
397413
{
398414
if (param.Type.HasFileLocalTypes())
399415
{
400-
diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, GetFirstLocation(), param.Type, ContainingType);
416+
diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, GetFirstLocation(), param.Type, containingType);
401417
}
402418
}
403419
}

src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbol.cs

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -576,9 +576,18 @@ private TypeWithAnnotations ComputeType(Binder binder, SyntaxNode syntax, Bindin
576576
diagnostics.Add((this.IsIndexer ? ErrorCode.ERR_BadVisIndexerReturn : ErrorCode.ERR_BadVisPropertyType), Location, this, type.Type);
577577
}
578578

579-
if (type.Type.HasFileLocalTypes() && !ContainingType.HasFileLocalTypes())
579+
if (type.Type.HasFileLocalTypes())
580580
{
581-
diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, Location, type.Type, ContainingType);
581+
NamedTypeSymbol containingType = ContainingType;
582+
if (containingType is { IsExtension: true, ContainingType: { } enclosing })
583+
{
584+
containingType = enclosing;
585+
}
586+
587+
if (!containingType.HasFileLocalTypes())
588+
{
589+
diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, Location, type.Type, containingType);
590+
}
582591
}
583592

584593
diagnostics.Add(Location, useSiteInfo);
@@ -652,22 +661,34 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions,
652661

653662
var useSiteInfo = new CompoundUseSiteInfo<AssemblySymbol>(diagnostics, ContainingAssembly);
654663

664+
var containingTypeForFileTypeCheck = this.ContainingType;
665+
if (containingTypeForFileTypeCheck is { IsExtension: true, ContainingType: { } enclosing })
666+
{
667+
containingTypeForFileTypeCheck = enclosing;
668+
}
669+
655670
foreach (ParameterSymbol param in Parameters)
656671
{
657672
if (!IsExplicitInterfaceImplementation && !this.IsNoMoreVisibleThan(param.Type, ref useSiteInfo))
658673
{
659674
diagnostics.Add(ErrorCode.ERR_BadVisIndexerParam, Location, this, param.Type);
660675
}
661-
else if (param.Type.HasFileLocalTypes() && !this.ContainingType.HasFileLocalTypes())
676+
else if (param.Type.HasFileLocalTypes() && !containingTypeForFileTypeCheck.HasFileLocalTypes())
662677
{
663-
diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, Location, param.Type, this.ContainingType);
678+
diagnostics.Add(ErrorCode.ERR_FileTypeDisallowedInSignature, Location, param.Type, containingTypeForFileTypeCheck);
664679
}
665680
else if (SetMethod is object && param.Name == ParameterSymbol.ValueParameterName)
666681
{
667682
diagnostics.Add(ErrorCode.ERR_DuplicateGeneratedName, param.TryGetFirstLocation() ?? Location, param.Name);
668683
}
669684
}
670685

686+
if (!IsStatic && this.GetIsNewExtensionMember() && ContainingType.ExtensionParameter is { } extensionParameter &&
687+
!this.IsNoMoreVisibleThan(extensionParameter.Type, ref useSiteInfo))
688+
{
689+
diagnostics.Add(ErrorCode.ERR_BadVisIndexerParam, Location, this, extensionParameter.Type);
690+
}
691+
671692
diagnostics.Add(Location, useSiteInfo);
672693

673694
if (IsPartialDefinition && OtherPartOfPartial is { } implementation)

0 commit comments

Comments
 (0)