Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -258,14 +258,12 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a
Debug.Assert(boundConstructorArguments.All(a => !a.NeedsToBeConverted()));

ImmutableArray<string?> boundConstructorArgumentNamesOpt = analyzedArguments.ConstructorArguments.GetNames();
// We delay reporting required member errors because:
// 1. If we didn't do it lazily during early attribute binding, we'd cause a cycle.
// 2. If we do it lazily during early attribute binding, we force every early-bound attribute to rebind later because the lazy diagnostic can't be computed yet.
// So instead, we report the error in `LoadAndValidateAttributes` after all attributes have been bound.
ImmutableArray<BoundAssignmentOperator> boundNamedArguments = analyzedArguments.NamedArguments?.ToImmutableAndFree() ?? ImmutableArray<BoundAssignmentOperator>.Empty;
Debug.Assert(boundNamedArguments.All(arg => !arg.Right.NeedsToBeConverted()));

if (attributeConstructor is not null)
{
CheckRequiredMembersInObjectInitializer(attributeConstructor, ImmutableArray<BoundExpression>.CastUp(boundNamedArguments), node, diagnostics);
}

analyzedArguments.ConstructorArguments.Free();

return new BoundAttribute(
Expand Down
4 changes: 4 additions & 0 deletions src/Compilers/CSharp/Portable/Symbols/Symbol_Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,10 @@ internal bool LoadAndValidateAttributes(
{
var boundAttribute = boundAttributeArray[i];
Debug.Assert(boundAttribute is not null);
if (boundAttribute.Constructor is { } ctor)
{
Binder.CheckRequiredMembersInObjectInitializer(ctor, ImmutableArray<BoundExpression>.CastUp(boundAttribute.NamedArguments), boundAttribute.Syntax, diagnostics);
}
NullableWalker.AnalyzeIfNeeded(binders[i], boundAttribute, boundAttribute.Syntax, diagnostics.DiagnosticBag);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7170,4 +7170,33 @@ public class C2 : C1
Diagnostic(ErrorCode.ERR_RequiredMemberMustBeSet, "C2").WithArguments("C1.P1").WithLocation(1, 9)
);
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74108")]
public void CycleOnConstructorAppliedToSelf()
{
var source = """
namespace System.Diagnostics;

[System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple=true)]
public sealed class ConditionalAttribute : Attribute
{
[Conditional("blah")]
public ConditionalAttribute(string condition)
{
Condition = condition;
}

public string Condition { get;}
}
""";

CreateCompilation(source, targetFramework: TargetFramework.NetStandard20).VerifyEmitDiagnostics(
// (6,6): warning CS0436: The type 'ConditionalAttribute' in '' conflicts with the imported type 'ConditionalAttribute' in 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51'. Using the type defined in ''.
// [Conditional("blah")]
Diagnostic(ErrorCode.WRN_SameFullNameThisAggAgg, "Conditional").WithArguments("", "System.Diagnostics.ConditionalAttribute", "netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51", "System.Diagnostics.ConditionalAttribute").WithLocation(6, 6),
// (6,6): error CS0592: Attribute 'Conditional' is not valid on this declaration type. It is only valid on 'class, method' declarations.
// [Conditional("blah")]
Diagnostic(ErrorCode.ERR_AttributeOnBadSymbolType, "Conditional").WithArguments("Conditional", "class, method").WithLocation(6, 6)
);
}
}