Skip to content

Commit df8d805

Browse files
authored
Local function GetAttributes (#39135)
1 parent 8194c46 commit df8d805

File tree

7 files changed

+325
-104
lines changed

7 files changed

+325
-104
lines changed

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

3+
#nullable enable
4+
35
using System;
46
using System.Diagnostics;
57
using Roslyn.Utilities;
@@ -200,7 +202,7 @@ public override string ToString()
200202
return ToString(null, null);
201203
}
202204

203-
public string ToString(string format, IFormatProvider formatProvider)
205+
public string ToString(string? format, IFormatProvider? formatProvider)
204206
{
205207
return ErrorFacts.GetMessage(_id, formatProvider as System.Globalization.CultureInfo);
206208
}
@@ -221,7 +223,7 @@ public static LocalizableErrorArgument Localize(this MessageID id)
221223
// If this method returns non-null, use that.
222224
// Features should be mutually exclusive between RequiredFeature and RequiredVersion.
223225
// (hence the above rule - RequiredVersion throws when RequiredFeature returns non-null)
224-
internal static string RequiredFeature(this MessageID feature)
226+
internal static string? RequiredFeature(this MessageID feature)
225227
{
226228
// Check for current experimental features, if any, in the current branch.
227229
switch (feature)
@@ -235,7 +237,7 @@ internal static bool CheckFeatureAvailability(
235237
this MessageID feature,
236238
DiagnosticBag diagnostics,
237239
SyntaxNode syntax,
238-
Location location = null)
240+
Location? location = null)
239241
{
240242
var diag = GetFeatureAvailabilityDiagnosticInfoOpt(feature, (CSharpParseOptions)syntax.SyntaxTree.Options);
241243
if (diag is object)
@@ -260,15 +262,15 @@ internal static bool CheckFeatureAvailability(
260262
return true;
261263
}
262264

263-
internal static CSDiagnosticInfo GetFeatureAvailabilityDiagnosticInfoOpt(this MessageID feature, CSharpParseOptions options)
265+
internal static CSDiagnosticInfo? GetFeatureAvailabilityDiagnosticInfoOpt(this MessageID feature, CSharpParseOptions options)
264266
=> options.IsFeatureEnabled(feature) ? null : GetDisabledFeatureDiagnosticInfo(feature, options.LanguageVersion);
265267

266-
internal static CSDiagnosticInfo GetFeatureAvailabilityDiagnosticInfoOpt(this MessageID feature, CSharpCompilation compilation)
268+
internal static CSDiagnosticInfo? GetFeatureAvailabilityDiagnosticInfoOpt(this MessageID feature, CSharpCompilation compilation)
267269
=> compilation.IsFeatureEnabled(feature) ? null : GetDisabledFeatureDiagnosticInfo(feature, compilation.LanguageVersion);
268270

269271
private static CSDiagnosticInfo GetDisabledFeatureDiagnosticInfo(MessageID feature, LanguageVersion availableVersion)
270272
{
271-
string requiredFeature = feature.RequiredFeature();
273+
string? requiredFeature = feature.RequiredFeature();
272274
if (requiredFeature != null)
273275
{
274276
return new CSDiagnosticInfo(ErrorCode.ERR_FeatureIsExperimental, feature.Localize(), requiredFeature);

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

Lines changed: 59 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

3+
#nullable enable
4+
35
using System.Collections.Generic;
46
using System.Collections.Immutable;
57
using System.Diagnostics;
@@ -12,7 +14,7 @@
1214

1315
namespace Microsoft.CodeAnalysis.CSharp.Symbols
1416
{
15-
internal sealed class LocalFunctionSymbol : SourceMethodSymbol
17+
internal sealed class LocalFunctionSymbol : SourceMethodSymbol, IAttributeTargetSymbol
1618
{
1719
private readonly Binder _binder;
1820
private readonly LocalFunctionStatementSyntax _syntax;
@@ -25,8 +27,11 @@ internal sealed class LocalFunctionSymbol : SourceMethodSymbol
2527
private bool _lazyIsVarArg;
2628
// Initialized in two steps. Hold a copy if accessing during initialization.
2729
private ImmutableArray<TypeParameterConstraintClause> _lazyTypeParameterConstraints;
28-
private TypeWithAnnotations.Boxed _lazyReturnType;
29-
private TypeWithAnnotations.Boxed _lazyIteratorElementType;
30+
private TypeWithAnnotations.Boxed? _lazyReturnType;
31+
private TypeWithAnnotations.Boxed? _lazyIteratorElementType;
32+
33+
private CustomAttributesBag<CSharpAttributeData>? _lazyCustomAttributesBag;
34+
private CustomAttributesBag<CSharpAttributeData>? _lazyReturnTypeCustomAttributesBag;
3035

3136
// Lock for initializing lazy fields and registering their diagnostics
3237
// Acquire this lock when initializing lazy objects to guarantee their declaration
@@ -121,6 +126,9 @@ internal void GetDeclarationDiagnostics(DiagnosticBag addTo)
121126

122127
ComputeReturnType();
123128

129+
GetAttributes();
130+
GetReturnTypeAttributes();
131+
124132
addTo.AddRange(_declarationDiagnostics);
125133
}
126134

@@ -202,7 +210,7 @@ public override TypeWithAnnotations ReturnTypeWithAnnotations
202210
get
203211
{
204212
ComputeReturnType();
205-
return _lazyReturnType.Value;
213+
return _lazyReturnType!.Value;
206214
}
207215
}
208216

@@ -341,17 +349,17 @@ internal override TypeWithAnnotations IteratorElementTypeWithAnnotations
341349

342350
internal override MethodImplAttributes ImplementationAttributes => default(MethodImplAttributes);
343351

344-
internal override ObsoleteAttributeData ObsoleteAttributeData => null;
352+
internal override ObsoleteAttributeData? ObsoleteAttributeData => null;
345353

346-
internal override MarshalPseudoCustomAttributeData ReturnValueMarshallingInformation => null;
354+
internal override MarshalPseudoCustomAttributeData? ReturnValueMarshallingInformation => null;
347355

348356
internal override CallingConvention CallingConvention => CallingConvention.Default;
349357

350358
internal override bool HasDeclarativeSecurity => false;
351359

352360
internal override bool RequiresSecurityObject => false;
353361

354-
public override Symbol AssociatedSymbol => null;
362+
public override Symbol? AssociatedSymbol => null;
355363

356364
public override Accessibility DeclaredAccessibility => ModifierUtils.EffectiveAccessibility(_declarationModifiers);
357365

@@ -375,7 +383,13 @@ internal override TypeWithAnnotations IteratorElementTypeWithAnnotations
375383

376384
internal override bool IsDeclaredReadOnly => false;
377385

378-
public override DllImportData GetDllImportData() => null;
386+
IAttributeTargetSymbol IAttributeTargetSymbol.AttributesOwner => this;
387+
388+
AttributeLocation IAttributeTargetSymbol.AllowedAttributeLocations => AttributeLocation.Method | AttributeLocation.Return;
389+
390+
AttributeLocation IAttributeTargetSymbol.DefaultAttributeLocation => AttributeLocation.Method;
391+
392+
public override DllImportData? GetDllImportData() => null;
379393

380394
internal override ImmutableArray<string> GetAppliedConditionalSymbols() => ImmutableArray<string>.Empty;
381395

@@ -393,25 +407,29 @@ internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree l
393407
throw ExceptionUtilities.Unreachable;
394408
}
395409

396-
internal override bool TryGetThisParameter(out ParameterSymbol thisParameter)
410+
internal override bool TryGetThisParameter(out ParameterSymbol? thisParameter)
397411
{
398412
// Local function symbols have no "this" parameter
399413
thisParameter = null;
400414
return true;
401415
}
402416

403-
private static void ReportAttributesDisallowed(SyntaxList<AttributeListSyntax> attributes, DiagnosticBag diagnostics)
417+
private void ReportAttributesDisallowed(SyntaxList<AttributeListSyntax> attributes, DiagnosticBag diagnostics)
404418
{
405-
foreach (var attrList in attributes)
419+
var diagnosticInfo = MessageID.IDS_FeatureLocalFunctionAttributes.GetFeatureAvailabilityDiagnosticInfoOpt((CSharpParseOptions)_syntax.SyntaxTree.Options);
420+
if (diagnosticInfo is object)
406421
{
407-
diagnostics.Add(ErrorCode.ERR_AttributesInLocalFuncDecl, attrList.Location);
422+
foreach (var attrList in attributes)
423+
{
424+
diagnostics.Add(diagnosticInfo, attrList.Location);
425+
}
408426
}
409427
}
410428

411429
private ImmutableArray<SourceMethodTypeParameterSymbol> MakeTypeParameters(DiagnosticBag diagnostics)
412430
{
413431
var result = ArrayBuilder<SourceMethodTypeParameterSymbol>.GetInstance();
414-
var typeParameters = _syntax.TypeParameterList.Parameters;
432+
var typeParameters = _syntax.TypeParameterList?.Parameters ?? default;
415433
for (int ordinal = 0; ordinal < typeParameters.Count; ordinal++)
416434
{
417435
var parameter = typeParameters[ordinal];
@@ -420,7 +438,6 @@ private ImmutableArray<SourceMethodTypeParameterSymbol> MakeTypeParameters(Diagn
420438
diagnostics.Add(ErrorCode.ERR_IllegalVarianceSyntax, parameter.VarianceKeyword.GetLocation());
421439
}
422440

423-
// Attributes are currently disallowed on local function type parameters
424441
ReportAttributesDisallowed(parameter.AttributeLists, diagnostics);
425442

426443
var identifier = parameter.Identifier;
@@ -493,6 +510,33 @@ public override ImmutableArray<TypeParameterConstraintClause> GetTypeParameterCo
493510
return _lazyTypeParameterConstraints;
494511
}
495512

513+
public override ImmutableArray<CSharpAttributeData> GetAttributes()
514+
{
515+
var lazyCustomAttributesBag = _lazyCustomAttributesBag;
516+
if (lazyCustomAttributesBag == null)
517+
{
518+
LoadAndValidateAttributes(OneOrMany.Create(_syntax.AttributeLists), ref _lazyCustomAttributesBag);
519+
lazyCustomAttributesBag = _lazyCustomAttributesBag;
520+
}
521+
522+
return lazyCustomAttributesBag.Attributes;
523+
}
524+
525+
public override ImmutableArray<CSharpAttributeData> GetReturnTypeAttributes()
526+
{
527+
var lazyReturnTypeCustomAttributesBag = _lazyReturnTypeCustomAttributesBag;
528+
if (lazyReturnTypeCustomAttributesBag == null)
529+
{
530+
LoadAndValidateAttributes(
531+
OneOrMany.Create(_syntax.AttributeLists),
532+
ref _lazyReturnTypeCustomAttributesBag,
533+
symbolPart: AttributeLocation.Return);
534+
lazyReturnTypeCustomAttributesBag = _lazyReturnTypeCustomAttributesBag;
535+
}
536+
537+
return lazyReturnTypeCustomAttributesBag.Attributes;
538+
}
539+
496540
public override int GetHashCode()
497541
{
498542
// this is what lambdas do (do not use hashes of other fields)
@@ -504,8 +548,7 @@ public sealed override bool Equals(Symbol symbol, TypeCompareKind compareKind)
504548
if ((object)this == symbol) return true;
505549

506550
var localFunction = symbol as LocalFunctionSymbol;
507-
return (object)localFunction != null
508-
&& localFunction._syntax == _syntax;
551+
return localFunction?._syntax == _syntax;
509552
}
510553
}
511554
}

src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenAsyncIteratorTests.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6571,12 +6571,8 @@ async System.Collections.Generic.IAsyncEnumerable<int> local([EnumeratorCancella
65716571
}
65726572
}
65736573
";
6574-
var comp = CreateCompilationWithAsyncIterator(new[] { source, EnumeratorCancellationAttributeType });
6575-
comp.VerifyDiagnostics(
6576-
// (13,70): error CS8205: Attributes are not allowed on local function parameters or type parameters
6577-
// async System.Collections.Generic.IAsyncEnumerable<int> local([EnumeratorCancellation] CancellationToken token)
6578-
Diagnostic(ErrorCode.ERR_AttributesInLocalFuncDecl, "[EnumeratorCancellation]").WithLocation(13, 70)
6579-
);
6574+
var comp = CreateCompilationWithTasksExtensions(new[] { source, EnumeratorCancellationAttributeType, AsyncStreamsTypes }, parseOptions: TestOptions.RegularPreview);
6575+
comp.VerifyDiagnostics();
65806576
}
65816577

65826578
[Fact, WorkItem(35166, "https://github.com/dotnet/roslyn/issues/35166")]

0 commit comments

Comments
 (0)