Skip to content

Commit 04e23e9

Browse files
committed
Extensions: misc checks on receiver parameter and extension members
1 parent 554fe0e commit 04e23e9

File tree

48 files changed

+2026
-192
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+2026
-192
lines changed

docs/contributing/Compiler Test Plan.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ This document provides guidance for thinking about language interactions and tes
8080
- Readonly members on structs (methods, property/indexer accessors, custom event accessors)
8181
- SkipLocalsInit
8282
- Method override or explicit implementation with `where T : { class, struct, default }`
83+
- `extension` blocks
8384

8485
# Code
8586
- Operators (see Eric's list below)

src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2121,7 +2121,7 @@ private BoundExpression BindNonMethod(SimpleNameSyntax node, Symbol symbol, Bind
21212121
{
21222122
Error(diagnostics, ErrorCode.ERR_InvalidPrimaryConstructorParameterReference, node, parameter);
21232123
}
2124-
else if (parameter.ContainingSymbol is NamedTypeSymbol { IsExtension: true } &&
2124+
else if (parameter.IsExtensionParameter() &&
21252125
(InParameterDefaultValue || InAttributeArgument ||
21262126
this.ContainingMember() is not { Kind: not SymbolKind.NamedType, IsStatic: false } || // We are not in an instance member
21272127
(object)this.ContainingMember().ContainingSymbol != parameter.ContainingSymbol) &&

src/Compilers/CSharp/Portable/Binder/Binder_NameConflicts.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ internal void ValidateParameterNameConflicts(
7474
diagnostics.Add(ErrorCode.ERR_LocalSameNameAsExtensionTypeParameter, GetLocation(p), name);
7575
}
7676
}
77-
else if (p.ContainingSymbol is NamedTypeSymbol { IsExtension: true })
77+
else if (p.IsExtensionParameter())
7878
{
7979
diagnostics.Add(ErrorCode.ERR_TypeParameterSameNameAsExtensionParameter, tp.GetFirstLocationOrNone(), name);
8080
}

src/Compilers/CSharp/Portable/Binder/ExecutableCodeBinder.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,24 @@ public static void ValidateIteratorMethod(CSharpCompilation compilation, MethodS
107107
return;
108108
}
109109

110-
foreach (var parameter in iterator.Parameters)
110+
var parameters = !iterator.IsStatic
111+
? iterator.GetParametersIncludingExtensionParameter()
112+
: iterator.Parameters;
113+
114+
foreach (var parameter in parameters)
111115
{
116+
bool isReceiverParameter = parameter.IsExtensionParameter();
112117
if (parameter.RefKind != RefKind.None)
113118
{
114-
diagnostics.Add(ErrorCode.ERR_BadIteratorArgType, parameter.GetFirstLocation());
119+
var location = isReceiverParameter
120+
? ((MethodDeclarationSyntax)iterator.GetNonNullSyntaxNode()).Identifier.GetLocation()
121+
: parameter.GetFirstLocation();
122+
123+
diagnostics.Add(ErrorCode.ERR_BadIteratorArgType, location);
115124
}
116-
else if (parameter.Type.IsPointerOrFunctionPointer())
125+
else if (parameter.Type.IsPointerOrFunctionPointer() && !isReceiverParameter)
117126
{
127+
// We already reported an error elsewhere if the receiver parameter of an extension is a pointer type.
118128
diagnostics.Add(ErrorCode.ERR_UnsafeIteratorArgType, parameter.GetFirstLocation());
119129
}
120130
}

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,9 @@ internal static bool HasInternalAccessTo(this AssemblySymbol fromAssembly, Assem
696696

697697
internal static ErrorCode GetProtectedMemberInSealedTypeError(NamedTypeSymbol containingType)
698698
{
699-
return containingType.TypeKind == TypeKind.Struct ? ErrorCode.ERR_ProtectedInStruct : ErrorCode.WRN_ProtectedInSealed;
699+
return containingType.IsExtension ? ErrorCode.ERR_ProtectedInExtension
700+
: containingType.TypeKind == TypeKind.Struct ? ErrorCode.ERR_ProtectedInStruct
701+
: ErrorCode.WRN_ProtectedInSealed;
700702
}
701703
}
702704
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3879,7 +3879,7 @@ private static EffectiveParameters GetEffectiveParametersInNormalForm<TMember>(
38793879
hasAnyRefOmittedArgument = false;
38803880

38813881
bool isNewExtensionMember = member.GetIsNewExtensionMember();
3882-
ImmutableArray<ParameterSymbol> parameters = isNewExtensionMember ? GetParametersIncludingReceiver(member) : member.GetParameters();
3882+
ImmutableArray<ParameterSymbol> parameters = member.GetParametersIncludingExtensionParameter();
38833883

38843884
// We simulate an extra parameter for vararg methods
38853885
int parameterCount = parameters.Length + (member.GetIsVararg() ? 1 : 0);
@@ -4040,7 +4040,7 @@ private static EffectiveParameters GetEffectiveParametersInExpandedForm<TMember>
40404040
var types = ArrayBuilder<TypeWithAnnotations>.GetInstance();
40414041
var refs = ArrayBuilder<RefKind>.GetInstance();
40424042
bool anyRef = false;
4043-
var parameters = member.GetIsNewExtensionMember() ? GetParametersIncludingReceiver(member) : member.GetParameters();
4043+
var parameters = member.GetParametersIncludingExtensionParameter();
40444044
bool hasAnyRefArg = argumentRefKinds.Any();
40454045
hasAnyRefOmittedArgument = false;
40464046
TypeWithAnnotations paramsIterationType = default;

src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolutionResult.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -895,7 +895,7 @@ private static void ReportMissingRequiredParameter(
895895
// to required formal parameter 'y'.
896896

897897
TMember badMember = bad.Member;
898-
ImmutableArray<ParameterSymbol> parameters = badMember.GetIsNewExtensionMember() ? OverloadResolution.GetParametersIncludingReceiver(badMember) : badMember.GetParameters();
898+
ImmutableArray<ParameterSymbol> parameters = badMember.GetParametersIncludingExtensionParameter();
899899
int badParamIndex = bad.Result.BadParameter;
900900
string badParamName;
901901
if (badParamIndex == parameters.Length)
@@ -1115,7 +1115,7 @@ private bool HadBadArguments(
11151115
// as there is no explicit call to Add method.
11161116

11171117
int argumentOffset = arguments.IncludesReceiverAsArgument ? 1 : 0;
1118-
var parameters = method.GetIsNewExtensionMember() ? OverloadResolution.GetParametersIncludingReceiver(method) : method.GetParameters();
1118+
var parameters = method.GetParametersIncludingExtensionParameter();
11191119

11201120
for (int i = argumentOffset; i < parameters.Length; i++)
11211121
{
@@ -1170,7 +1170,7 @@ private static void ReportBadArgumentError(
11701170

11711171
// Early out: if the bad argument is an __arglist parameter then simply report that:
11721172

1173-
var parameters = method.GetIsNewExtensionMember() ? OverloadResolution.GetParametersIncludingReceiver(method) : method.GetParameters();
1173+
var parameters = method.GetParametersIncludingExtensionParameter();
11741174
if (method.GetIsVararg() && parm == parameters.Length)
11751175
{
11761176
// NOTE: No SymbolDistinguisher required, since one of the arguments is "__arglist".

src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/OverloadResolution_ArgsToParameters.cs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,6 @@ public ImmutableArray<int> ToImmutableArray()
5555
}
5656
}
5757

58-
internal static ImmutableArray<ParameterSymbol> GetParametersIncludingReceiver(Symbol symbol)
59-
{
60-
Debug.Assert(symbol.GetIsNewExtensionMember());
61-
// Tracked by https://github.com/dotnet/roslyn/issues/76130 : consider optimizing
62-
return [symbol.ContainingType.ExtensionParameter, .. symbol.GetParameters()];
63-
}
64-
6558
private static ImmutableArray<TypeWithAnnotations> GetParameterTypesIncludingReceiver(Symbol symbol)
6659
{
6760
Debug.Assert(symbol.GetIsNewExtensionMember());
@@ -79,7 +72,7 @@ private static ArgumentAnalysisResult AnalyzeArguments(
7972
Debug.Assert(arguments != null);
8073

8174
bool isNewExtensionMember = symbol.GetIsNewExtensionMember();
82-
ImmutableArray<ParameterSymbol> parameters = isNewExtensionMember ? GetParametersIncludingReceiver(symbol) : symbol.GetParameters();
75+
ImmutableArray<ParameterSymbol> parameters = symbol.GetParametersIncludingExtensionParameter();
8376
bool isVararg = symbol.GetIsVararg();
8477

8578
// The easy out is that we have no named arguments and are in normal form.

src/Compilers/CSharp/Portable/CSharpResources.resx

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2525,7 +2525,7 @@ A catch() block after a catch (System.Exception e) block can catch non-CLS excep
25252525
<value> The parameter modifier '{0}' cannot be used with '{1}'</value>
25262526
</data>
25272527
<data name="ERR_BadTypeforThis" xml:space="preserve">
2528-
<value>The first parameter of an extension method cannot be of type '{0}'</value>
2528+
<value>The receiver parameter of an extension cannot be of type '{0}'</value>
25292529
</data>
25302530
<data name="ERR_BadParamModThis" xml:space="preserve">
25312531
<value>A parameter array cannot be used with 'this' modifier on an extension method</value>
@@ -5914,6 +5914,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
59145914
<data name="ERR_InExtensionMustBeValueType" xml:space="preserve">
59155915
<value>The first 'in' or 'ref readonly' parameter of the extension method '{0}' must be a concrete (non-generic) value type.</value>
59165916
</data>
5917+
<data name="ERR_InExtensionParameterMustBeValueType" xml:space="preserve">
5918+
<value>The 'in' or 'ref readonly' receiver parameter of extension must be a concrete (non-generic) value type.</value>
5919+
</data>
59175920
<data name="ERR_FieldsInRoStruct" xml:space="preserve">
59185921
<value>Instance fields of readonly structs must be readonly.</value>
59195922
</data>
@@ -5932,6 +5935,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
59325935
<data name="ERR_RefExtensionMustBeValueTypeOrConstrainedToOne" xml:space="preserve">
59335936
<value>The first parameter of a 'ref' extension method '{0}' must be a value type or a generic type constrained to struct.</value>
59345937
</data>
5938+
<data name="ERR_RefExtensionParameterMustBeValueTypeOrConstrainedToOne" xml:space="preserve">
5939+
<value>The 'ref' receiver parameter of an extension block must be a value type or a generic type constrained to struct.</value>
5940+
</data>
59355941
<data name="ERR_OutAttrOnInParam" xml:space="preserve">
59365942
<value>An in parameter cannot have the Out attribute.</value>
59375943
</data>
@@ -8134,4 +8140,19 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
81348140
<data name="ERR_PPIgnoredFollowsIf" xml:space="preserve">
81358141
<value>'#:' directives cannot be after '#if' directive</value>
81368142
</data>
8143+
<data name="ERR_ProtectedInExtension" xml:space="preserve">
8144+
<value>'{0}': new protected member declared in an extension block</value>
8145+
</data>
8146+
<data name="ERR_InstanceMemberWithUnnamedExtensionsParameter" xml:space="preserve">
8147+
<value>'{0}': cannot declare instance members in an extension block with an unnamed receiver parameter</value>
8148+
</data>
8149+
<data name="ERR_InitInExtension" xml:space="preserve">
8150+
<value>'{0}': cannot declare init-only accessors in an extension block</value>
8151+
</data>
8152+
<data name="ERR_ModifierOnUnnamedReceiverParameter" xml:space="preserve">
8153+
<value>Cannot use modifiers on the unnamed receiver parameter of extension block</value>
8154+
</data>
8155+
<data name="ERR_ExtensionTypeNameDisallowed" xml:space="preserve">
8156+
<value>Types and aliases cannot be named 'extension'.</value>
8157+
</data>
81378158
</root>

src/Compilers/CSharp/Portable/Errors/ErrorCode.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2390,6 +2390,14 @@ internal enum ErrorCode
23902390
ERR_PPIgnoredNeedsFileBasedProgram = 9298,
23912391
ERR_PPIgnoredFollowsIf = 9299,
23922392

2393+
ERR_RefExtensionParameterMustBeValueTypeOrConstrainedToOne = 9300,
2394+
ERR_InExtensionParameterMustBeValueType = 9301,
2395+
ERR_ProtectedInExtension = 9302,
2396+
ERR_InstanceMemberWithUnnamedExtensionsParameter = 9303,
2397+
ERR_InitInExtension = 9304,
2398+
ERR_ModifierOnUnnamedReceiverParameter = 9305,
2399+
ERR_ExtensionTypeNameDisallowed = 9306,
2400+
23932401
// Note: you will need to do the following after adding errors:
23942402
// 1) Update ErrorFacts.IsBuildOnlyDiagnostic (src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs)
23952403
// 2) Add message to CSharpResources.resx

0 commit comments

Comments
 (0)