Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate readonlyattribute if it does not exist #18715

Merged
merged 23 commits into from
May 18, 2017
Merged

Generate readonlyattribute if it does not exist #18715

merged 23 commits into from
May 18, 2017

Conversation

OmarTawfik
Copy link
Contributor

@OmarTawfik OmarTawfik commented Apr 14, 2017

Things done in this PR:

  • Refactor GetAttributes() and GetReturnTypeAttributes() of various helpers through out the code base to accept a parameter of type PEModuleBuilder instead of CompilationState.
  • Symbols now store the fact that they need an IsReadOnly attribute on the compilation object.
  • The PEAssemblyBuilder uses that fact and either generates that type during emitting, or gives an error if the user also defines that type in the same compilation.
  • Added a well known type Microsoft.CodeAnalysis.EmbeddedAttribute that both C# and VB compilers recognize and ignore types annotated by it during look up. All types embedded will have both this attribute and CompilerGeneratedAttribute.
  • Bug Defective tests: testing source attributes vs emitted attributes #18799 was filed because tests were testing synthesized attributes through internal APIs instead of testing compiled modules. Tests have a workaround (and a TODO) for that until the bug is fixed (optimally in master).

cc @dotnet/roslyn-compiler @VSadov @AlekseyTs

@OmarTawfik OmarTawfik added the PR For Personal Review Only The PR doesn’t require anyone other than the developer to review it. label Apr 14, 2017
@OmarTawfik OmarTawfik added Area-Compilers and removed PR For Personal Review Only The PR doesn’t require anyone other than the developer to review it. labels Apr 26, 2017
@OmarTawfik OmarTawfik requested a review from VSadov April 26, 2017 07:23
@@ -609,7 +609,7 @@ IPlatformInvokeInformation PlatformInvokeData
/// <summary>
/// Custom attributes associated with the method's return value.
/// </summary>
IEnumerable<ICustomAttribute> ReturnValueAttributes { get; }
IEnumerable<ICustomAttribute> GetReturnValueAttributes(CommonPEModuleBuilder moduleBuilder);
Copy link
Member

@cston cston Apr 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider passing an EmitContext argument instead, for consistency with other Cci interface methods. #Resolved

{
return _additionalTypes;
Interlocked.CompareExchange(ref _embeddedAttributesConstructors, CreateEmbeddedAttributesIfNeeded(diagnostics), null);
Copy link
Member

@cston cston Apr 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interlocked.CompareExchange [](start = 12, length = 27)

GetAdditionalTopLevelTypes should be called once (or at most once per emit) so Interlocked.CompareExchange shouldn't be necessary. #Resolved

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. the identity of the dictionary is unimportant. Even if we assign optimistically on multiple threads a dictionary with same contents. interlocked would not be needed. Simple assignment is sufficient.


In reply to: 113524192 [](ancestors = 113524192)


internal virtual SynthesizedAttributeData SynthesizeEmbeddedAttribute()
{
// For modules, this attribute should be present. Only assemblies generate and embedd this type.
Copy link
Member

@cston cston Apr 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo here and method below: embed #Resolved

private NamedTypeSymbol _baseType;
private MissingNamespaceSymbol _namespace;
private ImmutableArray<Symbol> _members;
private ModuleSymbol _module;
Copy link
Member

@cston cston Apr 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

readonly for all fields. #Resolved

_module = compilation.SourceModule;

_namespace = new MissingNamespaceSymbol(compilation.GlobalNamespace, nameParts[0]);
for (int i = 1; i + 1 < nameParts.Length; i++)
Copy link
Member

@cston cston Apr 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_namespace = compilation.GlobalNamespace;
for (int i = 0; ...
``` #Resolved

@@ -264,7 +264,8 @@ internal enum WellKnownType

Microsoft_CodeAnalysis_Runtime_Instrumentation,

System_Runtime_CompilerServices_ReadOnlyAttribute,
System_Runtime_CompilerServices_IsReadOnlyAttribute,
Microsoft_CodeAnalysis_EmbeddedAttribute,
Copy link
Contributor

@AlekseyTs AlekseyTs Apr 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Microsoft_CodeAnalysis_EmbeddedAttribute [](start = 8, length = 40)

I am just started looking at this change, but the fact that we added this type to the well-known types list feels very surprising to me. Well-known types are not used to identify attribute applications, and, when we are embedding types we are using the types that we embed, which aren't well-known (they are hidden from everyone, but the compiler). #Closed

public SourceEmbeddedAttributeSymbol(WellKnownType wellKnownType, CSharpCompilation compilation)
{
var nameParts = WellKnownTypes.GetMetadataName(wellKnownType).Split('.');
Debug.Assert(nameParts.Length > 1, "Attribute cannot be in global namespace?");
Copy link
Member

@cston cston Apr 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the assert necessary? #ByDesign

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd say yes, since the code below operates on that fact.


In reply to: 113527748 [](ancestors = 113527748)

@@ -609,7 +609,7 @@ IPlatformInvokeInformation PlatformInvokeData
/// <summary>
/// Custom attributes associated with the method's return value.
/// </summary>
IEnumerable<ICustomAttribute> ReturnValueAttributes { get; }
IEnumerable<ICustomAttribute> GetReturnValueAttributes(CommonPEModuleBuilder moduleBuilder);
Copy link
Contributor

@AlekseyTs AlekseyTs Apr 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IEnumerable GetReturnValueAttributes(CommonPEModuleBuilder moduleBuilder); [](start = 8, length = 92)

Consider passing an EmitContext argument instead, for consistency with other Cci interface methods.

I agree with this suggestion, contracts in interfaces should use EmitContext. #Closed

<value>Do not use 'System.Runtime.CompilerServices.IsReadOnlyAttribute'. This is reserved for compiler usage.</value>
</data>
<data name="ERR_TypeReserved" xml:space="preserve">
<value>The type '{0}' is reserved to be used by the compiler.</value>
Copy link
Contributor

@AlekseyTs AlekseyTs Apr 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type [](start = 15, length = 4)

"type name" vs. "type" ? #Closed

@@ -1116,7 +1116,12 @@ internal SingleLookupResult CheckViability(Symbol symbol, int arity, LookupOptio
? ((AliasSymbol)symbol).GetAliasTarget(basesBeingResolved)
: symbol;

if (WrongArity(symbol, arity, diagnose, options, out diagInfo))
// Check for external symbols marked with 'Microsoft.CodeAnalysis.Embedded' attribute
if (!this.Compilation.SourceModule.Equals(unwrappedSymbol.ContainingModule) && unwrappedSymbol.IsHiddenByCodeAnalysisEmbeddedAttribute())
Copy link
Contributor

@AlekseyTs AlekseyTs Apr 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!this.Compilation.SourceModule.Equals(unwrappedSymbol.ContainingModule) [](start = 16, length = 71)

Do we have tests covering significance of this condition? #Closed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Curious - why "external" part makes a difference? I thought the embedded symbols are not bindable regardless of where defined.


In reply to: 113533730 [](ancestors = 113533730)

Copy link
Contributor Author

@OmarTawfik OmarTawfik Apr 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have tests that cover one condition. Adding more tests now.
edit: @VSadov I'm correcting this now. Lookup would fail if on embedded attributes defined somewhere else. If you define your own embedded attribute we would ignore it. UNLESS the compiler needs to generate one, then we will produce an error.


In reply to: 113549567 [](ancestors = 113549567,113533730)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you point me to a test that demonstrates significance of the first condition?


In reply to: 114024707 [](ancestors = 114024707,113549567,113533730)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check AttributeTests_Embedded.cs:
ReferencingEmbeddedAttributesFromADifferentAssemblyFails_Internal
ReferencingEmbeddedAttributesFromADifferentAssemblyFails_Public
and
EmbeddedAttributeInSourceShouldTriggerAnErrorIfCompilerNeedsToGenerateOne


In reply to: 115050349 [](ancestors = 115050349,114024707,113549567,113533730)

@@ -30,6 +31,8 @@ public partial class CSharpCompilation
/// </summary>
private Symbol[] _lazyWellKnownTypeMembers;

internal bool NeedsGeneratedIsReadOnlyAttribute { get; private set; }
Copy link
Contributor

@AlekseyTs AlekseyTs Apr 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

internal bool NeedsGeneratedIsReadOnlyAttribute { get; private set; } [](start = 8, length = 69)

It would be good to have a comment when one should expect this property to have actual value. We should probably have a way to "freeze" the value and a way to assert an attempt to set the value it after it was frozen and an attempt to read the value before it is frozen. #Closed

member: WellKnownMember.System_Runtime_CompilerServices_IsReadOnlyAttribute__ctor,
diagnostics: DeclarationDiagnostics,
location: readOnlySymbol.Locations.FirstOrDefault() ?? Location.None,
isOptional: Options.OutputKind != OutputKind.NetModule);
Copy link
Contributor

@AlekseyTs AlekseyTs Apr 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isOptional: Options.OutputKind != OutputKind.NetModule [](start = 16, length = 54)

It feels like this doesn't give us the behavior we want. I think we want to embed the attribute only if the type cannot be found, the lack of the required constructor should result in an error rather than in an embedded attribute. #Closed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the attribute defined in current module and does not have right constructor, embedding another attribute would cause a conflict. Is that the reason for giving an error?


In reply to: 113537098 [](ancestors = 113537098)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I think the lookup should be modified to give a different error if the type was found but it was lacking the correct signature


In reply to: 113554955 [](ancestors = 113554955,113537098)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I think the lookup should be modified to give a different error if the type was found but it was lacking the correct signature

I think the existing error for a missing well-known member will work just fine.


In reply to: 114047541 [](ancestors = 114047541,113554955,113537098)

var attributeSymbol = Binder.GetWellKnownTypeMember(
compilation: this,
member: WellKnownMember.System_Runtime_CompilerServices_IsReadOnlyAttribute__ctor,
diagnostics: DeclarationDiagnostics,
Copy link
Contributor

@AlekseyTs AlekseyTs Apr 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DeclarationDiagnostics [](start = 29, length = 22)

Rather than adding diagnostics to DeclarationDiagnostics directly, we probably should use Symbol.AddDeclarationDiagnostics API instead as some symbols customize where declaration diagnostics should go. #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moving to a model where diagnosticsbag is defined by the syntax needing it whenever possible.


In reply to: 113538171 [](ancestors = 113538171)

@@ -415,10 +418,22 @@ internal SynthesizedAttributeData SynthesizeDebuggerStepThroughAttribute()
return TrySynthesizeAttribute(WellKnownMember.System_Diagnostics_DebuggerStepThroughAttribute__ctor);
}

internal SynthesizedAttributeData SynthesizeReadOnlyAttribute()
internal void EnsureIsReadOnlyAttributeExists(Symbol readOnlySymbol)
Copy link
Contributor

@AlekseyTs AlekseyTs Apr 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EnsureIsReadOnlyAttributeExists [](start = 22, length = 31)

Please ensure we have tests for local functions. #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've a test for the positive case. Will add a test for the negative case as well.


In reply to: 113539714 [](ancestors = 113539714)

/// <summary>
/// Lazily initiated HasEmbeddedAttribute
/// </summary>
private ThreeState _lazyHasEmbeddedAttribute = ThreeState.Unknown;
Copy link
Member

@cston cston Apr 26, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be part of UncommonProperties rather than a field directly on PENamedTypeSymbol? #Closed

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, this is very uncommon case.


In reply to: 113542264 [](ancestors = 113542264)

Copy link
Contributor Author

@OmarTawfik OmarTawfik Apr 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment recalled.


In reply to: 113556704 [](ancestors = 113556704,113542264)

Copy link
Member

@cston cston Apr 30, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UncommonProperties are only instantiated for PENamedTypeSymbols with uncommon attributes. The instances of UncommonProperties are not shared. #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noted. Will fix it now.


In reply to: 114065603 [](ancestors = 114065603)


private void CreateEmbeddedAttributeIfNeeded(ref SynthesizedEmbeddedAttributeSymbol symbol, DiagnosticBag diagnostics, AttributeDescription description)
{
if ((object)symbol == null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there still a case where symbol can be null? If not, remove the null check and perhaps return symbol rather than using a ref parameter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is possibly going to be rewritten in Vlad's next PR, as there are more attributes to be generated. His feature will possibly need to generate some attributes that are generated in this PR, so the check will be necessary.
I see the future of embedded attributes going in one of two directions:

  1. keeping the current design, having each feature calling CreateEmbeddedAttributeIfNeeded on the attributes it needs (possibly overlapping), and thus the current design protects against that.
  2. If the features that need embedded attributes grow in numbers, there might be a need for an independent type manager to hold/manage them.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now we are adding only IsByRefLike, so there is no need for #2 just yet.
The current code seems ok to me for now.

{
factory.ExpressionStatement(baseConstructorCall),
factory.Return(),
};
Copy link
Member

@cston cston May 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor point: factory.Block() has overload with params array so these could be inlined below. #Resolved

}";

CreateCompilationWithMscorlib(code, assemblyName: "testModule").VerifyEmitDiagnostics(
// (4,18): error CS8413: The type name 'EmbeddedAttribute' is reserved to be used by the compiler.
Copy link
Member

@cston cston May 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider reporting the fully qualified type name so it's clear we're not reserving the type name regardless of namespace. #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point.

// error CS0518: Predefined type 'System.Attribute' is not defined or imported
Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Attribute").WithLocation(1, 1),
// error CS0518: Predefined type 'System.Attribute' is not defined or imported
Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound).WithArguments("System.Attribute").WithLocation(1, 1));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. because the compiler is trying to synthesize two attributes here (Embedded and IsReadOnly).

// error CS1729: 'Attribute' does not contain a constructor that takes 0 arguments
Diagnostic(ErrorCode.ERR_BadCtorArgCount).WithArguments("System.Attribute", "0").WithLocation(1, 1),
// error CS1729: 'Attribute' does not contain a constructor that takes 0 arguments
Diagnostic(ErrorCode.ERR_BadCtorArgCount).WithArguments("System.Attribute", "0").WithLocation(1, 1));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. because the compiler is trying to synthesize two attributes here (Embedded and IsReadOnly).

@cston
Copy link
Member

cston commented May 11, 2017

LGTM

@OmarTawfik
Copy link
Contributor Author

@cston @AlekseyTs addressed the latest set of comments.
I think this PR is getting too big for codeflow (it started misbehaving and I'm not able to see older comments, trying to contact them). Please let me know if I missed something.
Also, I'd like to merge this PR (if you sign off on the functional changes here) sooner than later to unblock @VSadov. If there are any missing tests left I can address them in the next PR.

Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please wait for sign-off from me.

builder.AddRange(_additionalTypes);

CreateEmbeddedAttributesIfNeeded();
diagnostics.AddRange(_lazyEmbeddedAttributesDiagnostics);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

diagnostics.AddRange(_lazyEmbeddedAttributesDiagnostics); [](start = 12, length = 57)

This is likely to cause duplication of errors. We should avoid that, we specifically avoid reporting duplicate errors in PEModuleBuilder, see _reportedErrorTypesMap for example. Please revert to the previous approach.

{
private int value;

public override ref readonly int Method(ref readonly int x) => ref value;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public override ref readonly int Method(ref readonly int x) => ref value; [](start = 3, length = 74)

The override scenarios should be tested inside IsReadOnlyAttribute type. This test could be valuable too, but it doesn't test most interesting scenario.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar for explicit interface implementations. In these tests, test both scenarios when base type/interface is defined in the same assembly and when it is defined in a different assembly.

}

[Fact]
public void RefReadOnlyDefinitionsInsideUserDefinedIsReadOnlyAttribute_Class_CorrectParent()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RefReadOnlyDefinitionsInsideUserDefinedIsReadOnlyAttribute_Class_CorrectParent [](start = 20, length = 78)

Added tests for indexers, operators, properties, and overrides. There is already a test that verifies we produce errors if IsReadOnly was an interface, because it is lacking the constructor.

I don't see any tests added for explicit interface implementations. I believe I asked for them in the previous review pass.

@AlekseyTs
Copy link
Contributor

Done with review pass. I believe the remaining issues that I track are summarized in the last three comments above. At the very least the requested tests should be added before the PR can be merged, I think.

@OmarTawfik
Copy link
Contributor Author

@AlekseyTs added the tests requested in your last comment in 40748ee

About your other comment:

This is likely to cause duplication of errors. We should avoid that, we specifically avoid reporting duplicate errors in PEModuleBuilder, see _reportedErrorTypesMap for example. Please revert to the previous approach.

This contradicts with the point @cston pointed out. Basically, if I reverted, first call to GetAdditionalTopLevelTypes(diagnostics) will return diagnostics that the subsequent calls will not.
The current proposed implementation de-dupes errors between different attributes, so that if two features asked to embed the same attribute, we won't duplicate errors then. But the errors are saved so that the API is consistent between different external calls to get additional types.
Do you want to revert that behavior? am I missing something?

@AlekseyTs
Copy link
Contributor

Do you want to revert that behavior? am I missing something?

Yes, I believe we should revert the behavior. There is no requirement for this API to produce the same errors on every call. In fact, I believe it is preferred that this API wouldn't produce errors due to the same reasons on the second and subsequent calls.

@@ -703,6 +704,13 @@ internal override void AfterAddingTypeMembersChecks(ConversionsBase conversions,
Debug.Assert(explicitInterfaceSpecifier != null);
_explicitInterfaceType.CheckAllConstraints(conversions, new SourceLocation(explicitInterfaceSpecifier.Name), diagnostics);
}

if (_refKind == RefKind.RefReadOnly)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (_refKind == RefKind.RefReadOnly) [](start = 12, length = 36)

It feels like we should be moving similar checks for all source symbols into an override of AfterAddingTypeMembersChecks API. This should ensure that the checks won't cause circularity issues in the future due to some changes in the compiler's behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the general rule applies, because for some symbols like SourceConstructorMethod we need the parameters to be computed first before we perform that check, and calling Parameters in AfterAddingTypeMembersChecks would call MethodChecks in turn.
For SourcePropertySymbol, it wasn't necessary since it computes parameters without calling MethodChecks. Some symbols I believe don't even use this API like LocalFunctionSymbol.


if (_refKind == RefKind.RefReadOnly)
{
DeclaringCompilation.EnsureIsReadOnlyAttributeExists(diagnostics, _location, modifyCompilationForRefReadOnly: true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_location [](start = 82, length = 9)

I didn't notice any location changes for errors produced by this call. Is location not changed in iteration 18, or do we have a test gap?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have tests covering these diagnostics, like IsReadOnlyAttributeExistsWithWrongConstructorSignature_Assembly. However, Covering every permutation for every symbol type and error type and location (parameter vs return type) I believe should be left to the final test plan (lot's of moving pieces right now in this feature).
However, I've added 5 more tests for symbols for that specific error location for this PR.

@OmarTawfik
Copy link
Contributor Author

@AlekseyTs I reverted the change and added a few more tests. Also answered the last comment about AfterAddingTypeMembersChecks.

@OmarTawfik
Copy link
Contributor Author

@AlekseyTs moved calls of EnsureIsReadOnlyAttributeExists for declaration-level symbols to AfterAddingTypeMembersChecks as we discussed. Also added a couple more tests for reporting diagnostics for non-declaration symbols (lambdas and local functions) to cover the discussed gap.

@AlekseyTs AlekseyTs dismissed their stale review May 16, 2017 17:31

dismissed

Copy link
Contributor

@AlekseyTs AlekseyTs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, modulo the failing tests and merge conflicts.

@OmarTawfik OmarTawfik merged commit d63f038 into dotnet:features/readonly-ref May 18, 2017
@OmarTawfik OmarTawfik deleted the generate-readonlyattribute-if-it-does-not-exist branch May 18, 2017 18:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants