From f6ac4747d82787b1139bfd3895c8fc0d9bfa0d6c Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Wed, 4 Dec 2019 10:49:01 -0800 Subject: [PATCH] Record used assembly references for more scenarios. - Merged extern aliases - Embeddable attributes - Usage of canonical definitions for NoPia embedded types - nameof of a method - Deconstruction - Collection initializers Related to #37768. --- .../Portable/Binder/Binder_Attributes.cs | 2 +- .../Portable/Binder/Binder_Expressions.cs | 4 +- .../CSharp/Portable/Binder/Binder_Symbols.cs | 12 +- .../Compilation/CSharpSemanticModel.cs | 2 +- .../Portable/Compiler/MethodCompiler.cs | 17 +- .../Portable/Emitter/Model/PEModuleBuilder.cs | 16 +- .../Lowering/LocalRewriter/LocalRewriter.cs | 6 +- .../Symbols/Compilation_UsedAssemblies.cs | 35 +- .../Symbols/Metadata/PE/PEModuleSymbol.cs | 5 + .../Portable/Symbols/MissingModuleSymbol.cs | 5 + .../CSharp/Portable/Symbols/ModuleSymbol.cs | 2 + .../Portable/Symbols/ReferenceManager.cs | 8 +- .../Retargeting/RetargetingModuleSymbol.cs | 5 + .../Symbols/Source/SourceModuleSymbol.cs | 2 +- .../Symbols/UsedAssembliesRecorder.cs | 223 ++- .../Symbol/Compilation/UsedAssembliesTests.cs | 1645 ++++++++++++++++- .../Test/Symbol/Symbols/Source/EventTests.cs | 9 +- .../CommonReferenceManager.Resolution.cs | 25 +- .../CommonReferenceManager.State.cs | 37 +- .../ReferenceManager/MergedAliases.cs | 3 + .../Test/Utilities/CSharp/CSharpTestBase.cs | 33 +- .../Portable/Symbols/ReferenceManager.vb | 7 +- 22 files changed, 1933 insertions(+), 170 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs index 98866c3c4e8c4..dca0350425366 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs @@ -435,7 +435,7 @@ private Symbol BindNamedAttributeArgumentName(AttributeArgumentSyntax namedArgum HashSet useSiteDiagnostics = null; this.LookupMembersWithFallback(result, attributeType, name, 0, ref useSiteDiagnostics); diagnostics.Add(identifierName, useSiteDiagnostics); - Symbol resultSymbol = this.ResultSymbol(result, name, 0, identifierName, diagnostics, false, out wasError, qualifierOpt: null); + Symbol resultSymbol = this.ResultSymbol(result, name, 0, identifierName, diagnostics, false, out wasError, qualifierOpt: null, basesBeingResolved: null); resultKind = result.Kind; result.Free(); return resultSymbol; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 6f1b012d48f0f..4f9f3ba12b12b 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -5811,7 +5811,7 @@ private BoundExpression BindMemberAccessWithBoundLeft( if (lookupResult.IsMultiViable) { bool wasError; - Symbol sym = ResultSymbol(lookupResult, rightName, rightArity, node, diagnostics, false, out wasError, ns, options); + Symbol sym = ResultSymbol(lookupResult, rightName, rightArity, node, diagnostics, false, out wasError, ns, basesBeingResolved: null, options); if (wasError) { return new BoundBadExpression(node, LookupResultKind.Ambiguous, lookupResult.Symbols.AsImmutable(), ImmutableArray.Create(boundLeft), CreateErrorType(rightName), hasErrors: true); @@ -6869,7 +6869,7 @@ private Symbol GetSymbolOrMethodOrPropertyGroup(LookupResult result, SyntaxNode } methodOrPropertyGroup.Clear(); - return ResultSymbol(result, plainName, arity, node, diagnostics, false, out wasError, qualifierOpt); + return ResultSymbol(result, plainName, arity, node, diagnostics, false, out wasError, qualifierOpt, basesBeingResolved: null); } private static bool IsMethodOrPropertyGroup(ArrayBuilder members) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index d3f1dff5b6350..416d2b2a38065 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -232,7 +232,8 @@ private NamespaceOrTypeOrAliasSymbolWithAnnotations BindTypeOrAliasOrKeyword(Syn diagnostics: resultDiagnostics, suppressUseSiteDiagnostics: false, wasError: out wasError, - qualifierOpt: null); + qualifierOpt: null, + basesBeingResolved: null); // Here, we're mimicking behavior of dev10. If the identifier fails to bind // as a type, even if the reason is (e.g.) a type/alias conflict, then treat @@ -354,7 +355,7 @@ internal Symbol BindNamespaceAliasSymbol(IdentifierNameSyntax node, DiagnosticBa this.LookupSymbolsWithFallback(result, plainName, 0, ref useSiteDiagnostics, null, LookupOptions.NamespaceAliasesOnly); diagnostics.Add(node, useSiteDiagnostics); - Symbol bindingResult = ResultSymbol(result, plainName, 0, node, diagnostics, false, out wasError, qualifierOpt: null, options: LookupOptions.NamespaceAliasesOnly); + Symbol bindingResult = ResultSymbol(result, plainName, 0, node, diagnostics, false, out wasError, qualifierOpt: null, basesBeingResolved: null, options: LookupOptions.NamespaceAliasesOnly); result.Free(); return bindingResult; @@ -831,7 +832,7 @@ protected NamespaceOrTypeOrAliasSymbolWithAnnotations BindNonGenericSimpleNamesp { bool wasError; - bindingResult = ResultSymbol(result, identifierValueText, 0, node, diagnostics, suppressUseSiteDiagnostics, out wasError, qualifierOpt, options); + bindingResult = ResultSymbol(result, identifierValueText, 0, node, diagnostics, suppressUseSiteDiagnostics, out wasError, qualifierOpt, basesBeingResolved, options); if (bindingResult.Kind == SymbolKind.Alias) { var aliasTarget = ((AliasSymbol)bindingResult).GetAliasTarget(basesBeingResolved); @@ -1097,7 +1098,7 @@ private NamedTypeSymbol LookupGenericTypeName( diagnostics.Add(node, useSiteDiagnostics); bool wasError; - Symbol lookupResultSymbol = ResultSymbol(lookupResult, plainName, arity, node, diagnostics, (basesBeingResolved != null), out wasError, qualifierOpt, options); + Symbol lookupResultSymbol = ResultSymbol(lookupResult, plainName, arity, node, diagnostics, (basesBeingResolved != null), out wasError, qualifierOpt, basesBeingResolved, options); // As we said in the method above, there are three cases here: // @@ -1536,6 +1537,7 @@ internal Symbol ResultSymbol( bool suppressUseSiteDiagnostics, out bool wasError, NamespaceOrTypeSymbol qualifierOpt, + ConsList basesBeingResolved, LookupOptions options = default(LookupOptions)) { Symbol symbol = resultSymbol(result, simpleName, arity, where, diagnostics, suppressUseSiteDiagnostics, out wasError, qualifierOpt, options); @@ -1552,7 +1554,7 @@ internal Symbol ResultSymbol( break; case SymbolKind.Alias: - if (shouldRecordUsedAssemblyReferences() && ((AliasSymbol)symbol).Target is TypeSymbol type) + if (shouldRecordUsedAssemblyReferences() && ((AliasSymbol)symbol).GetAliasTarget(basesBeingResolved) is TypeSymbol type) { AddAssembliesUsedByTypeReference(type); } diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index cb304de26b93a..74d12d57d35ce 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -1652,7 +1652,7 @@ private void AppendSymbolsWithNameAndArity( // binder.ResultSymbol is defined only for type/namespace lookups bool wasError; var diagnostics = DiagnosticBag.GetInstance(); // client code never expects a null diagnostic bag. - Symbol singleSymbol = binder.ResultSymbol(lookupResult, name, arity, this.Root, diagnostics, true, out wasError, container, options); + Symbol singleSymbol = binder.ResultSymbol(lookupResult, name, arity, this.Root, diagnostics, true, out wasError, container, basesBeingResolved: null, options); diagnostics.Free(); if (!wasError) diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs index 53e96ea99e2a2..efd066b1618dd 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodCompiler.cs @@ -762,13 +762,20 @@ private void CompileSynthesizedMethods(TypeCompilationState compilationState) _diagnostics.AddRange(diagnosticsThisMethod); diagnosticsThisMethod.Free(); - // error while generating IL - if (emittedBody == null) + if (_emitMethodBodies) { - break; - } + // error while generating IL + if (emittedBody == null) + { + break; + } - _moduleBeingBuiltOpt.SetMethodBody(method, emittedBody); + _moduleBeingBuiltOpt.SetMethodBody(method, emittedBody); + } + else + { + Debug.Assert(emittedBody is null); + } } } finally diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs index b5e830bb15d9f..9e43fceca087a 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs @@ -1534,7 +1534,7 @@ protected virtual SynthesizedAttributeData TrySynthesizeIsByRefLikeAttribute() return Compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_IsByRefLikeAttribute__ctor); } - private void EnsureEmbeddableAttributeExists(EmbeddableAttributes attribute) + private void EnsureEmbeddableAttributeExists(EmbeddableAttributes attribute, bool recordUsage) { Debug.Assert(!_needsGeneratedAttributes_IsFrozen); @@ -1544,25 +1544,25 @@ private void EnsureEmbeddableAttributeExists(EmbeddableAttributes attribute) } // Don't report any errors. They should be reported during binding. - if (Compilation.CheckIfAttributeShouldBeEmbedded(attribute, recordUsage: false, diagnosticsOpt: null, locationOpt: null)) + if (Compilation.CheckIfAttributeShouldBeEmbedded(attribute, recordUsage, diagnosticsOpt: null, locationOpt: null)) { SetNeedsGeneratedAttributes(attribute); } } - internal void EnsureIsReadOnlyAttributeExists() + internal void EnsureIsReadOnlyAttributeExists(bool recordUsage) { - EnsureEmbeddableAttributeExists(EmbeddableAttributes.IsReadOnlyAttribute); + EnsureEmbeddableAttributeExists(EmbeddableAttributes.IsReadOnlyAttribute, recordUsage); } - internal void EnsureIsUnmanagedAttributeExists() + internal void EnsureIsUnmanagedAttributeExists(bool recordUsage) { - EnsureEmbeddableAttributeExists(EmbeddableAttributes.IsUnmanagedAttribute); + EnsureEmbeddableAttributeExists(EmbeddableAttributes.IsUnmanagedAttribute, recordUsage); } - internal void EnsureNullableAttributeExists() + internal void EnsureNullableAttributeExists(bool recordUsage) { - EnsureEmbeddableAttributeExists(EmbeddableAttributes.NullableAttribute); + EnsureEmbeddableAttributeExists(EmbeddableAttributes.NullableAttribute, recordUsage); } } } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index b8eec121d3865..fe4e322a2c803 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -258,7 +258,7 @@ public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatemen var typeParameters = localFunction.TypeParameters; if (typeParameters.Any(typeParameter => typeParameter.HasUnmanagedTypeConstraint)) { - _factory.CompilationState.ModuleBuilderOpt?.EnsureIsUnmanagedAttributeExists(); + _factory.CompilationState.ModuleBuilderOpt?.EnsureIsUnmanagedAttributeExists(recordUsage: true); } if (_factory.CompilationState.Compilation.ShouldEmitNullableAttributes(localFunction)) @@ -271,7 +271,7 @@ public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatemen if (constraintsNeedNullableAttribute || returnTypeNeedsNullableAttribute || parametersNeedNullableAttribute) { - _factory.CompilationState.ModuleBuilderOpt?.EnsureNullableAttributeExists(); + _factory.CompilationState.ModuleBuilderOpt?.EnsureNullableAttributeExists(recordUsage: true); } } @@ -840,7 +840,7 @@ private void CheckRefReadOnlySymbols(MethodSymbol symbol) if (symbol.ReturnsByRefReadonly || symbol.Parameters.Any(p => p.RefKind == RefKind.In)) { - _factory.CompilationState.ModuleBuilderOpt?.EnsureIsReadOnlyAttributeExists(); + _factory.CompilationState.ModuleBuilderOpt?.EnsureIsReadOnlyAttributeExists(recordUsage: true); } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Compilation_UsedAssemblies.cs b/src/Compilers/CSharp/Portable/Symbols/Compilation_UsedAssemblies.cs index 02310d76d17b4..04041f102d2b2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Compilation_UsedAssemblies.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Compilation_UsedAssemblies.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Threading; @@ -9,40 +10,56 @@ using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; +#nullable enable + namespace Microsoft.CodeAnalysis.CSharp { public partial class CSharpCompilation { - private ConcurrentSet _lazyUsedAssemblyReferences; + private ConcurrentSet? _lazyUsedAssemblyReferences; private bool _usedAssemblyReferencesFrozen; internal override ImmutableArray GetUsedAssemblyReferences(CancellationToken cancellationToken = default) { - ConcurrentSet usedAssemblies = GetCompleteSetOfUsedAssemblies(cancellationToken); + ConcurrentSet? usedAssemblies = GetCompleteSetOfUsedAssemblies(cancellationToken); if (usedAssemblies is null) { return ImmutableArray.Empty; } - var builder = ArrayBuilder.GetInstance(usedAssemblies.Count); + var setOfReferences = new HashSet(ReferenceEqualityComparer.Instance); foreach (var reference in References) { if (reference.Properties.Kind == MetadataImageKind.Assembly) { Symbol symbol = GetAssemblyOrModuleSymbol(reference); - if (symbol is object && usedAssemblies.Contains((AssemblySymbol)symbol)) + if (symbol is object && usedAssemblies.Contains((AssemblySymbol)symbol) && + setOfReferences.Add(reference) && + GetBoundReferenceManager().MergedAssemblyReferencesMap.TryGetValue(reference, out ImmutableArray merged)) { - builder.Add(reference); + // Include all "merged" references as well because they might "define" used extern aliases. + setOfReferences.AddAll(merged); } } } + // Use stable ordering for the result, matching the order in References. + var builder = ArrayBuilder.GetInstance(setOfReferences.Count); + + foreach (var reference in References) + { + if (setOfReferences.Contains(reference)) + { + builder.Add(reference); + } + } + return builder.ToImmutableAndFree(); } - private ConcurrentSet GetCompleteSetOfUsedAssemblies(CancellationToken cancellationToken) + private ConcurrentSet? GetCompleteSetOfUsedAssemblies(CancellationToken cancellationToken) { if (!_usedAssemblyReferencesFrozen && !Volatile.Read(ref _usedAssemblyReferencesFrozen)) { @@ -129,7 +146,7 @@ void completeTheSetOfUsedAssemblies(bool seenErrors, CancellationToken cancellat while (stack.Count != 0) { AssemblySymbol current = stack.Pop(); - ConcurrentSet usedAssemblies; + ConcurrentSet? usedAssemblies; switch (current) { @@ -194,7 +211,7 @@ void completeTheSetOfUsedAssemblies(bool seenErrors, CancellationToken cancellat } } - internal bool AddUsedAssembly(AssemblySymbol assembly) + internal bool AddUsedAssembly(AssemblySymbol? assembly) { if (assembly is null || assembly == SourceAssembly || assembly.IsMissing) { @@ -217,7 +234,7 @@ internal bool AddUsedAssembly(AssemblySymbol assembly) return result; } - internal void AddAssembliesUsedByTypeReference(TypeSymbol typeOpt) + internal void AddAssembliesUsedByTypeReference(TypeSymbol? typeOpt) { while (true) { diff --git a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs index 201fe1a390348..0033b1598968d 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEModuleSymbol.cs @@ -199,6 +199,11 @@ public override AssemblySymbol ContainingAssembly } } + internal override bool MightContainNoPiaLocalTypes() + { + return _module.ContainsNoPiaLocalTypes(); + } + public override ImmutableArray Locations { get diff --git a/src/Compilers/CSharp/Portable/Symbols/MissingModuleSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/MissingModuleSymbol.cs index 6ab1b3b3100c5..de7c6bb2ae54c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/MissingModuleSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/MissingModuleSymbol.cs @@ -98,6 +98,11 @@ public override NamespaceSymbol GlobalNamespace } } + internal sealed override bool MightContainNoPiaLocalTypes() + { + return false; + } + public override int GetHashCode() { return assembly.GetHashCode(); diff --git a/src/Compilers/CSharp/Portable/Symbols/ModuleSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/ModuleSymbol.cs index f479125112233..532a7a94931f1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ModuleSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ModuleSymbol.cs @@ -363,6 +363,8 @@ public NamespaceSymbol GetModuleNamespace(INamespaceSymbol namespaceSymbol) } } + internal abstract bool MightContainNoPiaLocalTypes(); + /// /// If this symbol represents a metadata module returns the underlying . /// diff --git a/src/Compilers/CSharp/Portable/Symbols/ReferenceManager.cs b/src/Compilers/CSharp/Portable/Symbols/ReferenceManager.cs index 2a11bcdfa31a2..26980ff2c3a11 100644 --- a/src/Compilers/CSharp/Portable/Symbols/ReferenceManager.cs +++ b/src/Compilers/CSharp/Portable/Symbols/ReferenceManager.cs @@ -403,6 +403,8 @@ private bool CreateAndSetSourceAssemblyFullBind(CSharpCompilation compilation) Dictionary referencedAssembliesMap, referencedModulesMap; ImmutableArray> aliasesOfReferencedAssemblies; + Dictionary> mergedAssemblyReferencesMapOpt; + BuildReferencedAssembliesAndModulesMaps( bindingResult, references, @@ -413,7 +415,8 @@ private bool CreateAndSetSourceAssemblyFullBind(CSharpCompilation compilation) supersedeLowerVersions, out referencedAssembliesMap, out referencedModulesMap, - out aliasesOfReferencedAssemblies); + out aliasesOfReferencedAssemblies, + out mergedAssemblyReferencesMapOpt); // Create AssemblySymbols for assemblies that can't use any existing symbols. var newSymbols = new List(); @@ -507,7 +510,8 @@ private bool CreateAndSetSourceAssemblyFullBind(CSharpCompilation compilation) moduleReferences, assemblySymbol.SourceModule.GetReferencedAssemblySymbols(), aliasesOfReferencedAssemblies, - assemblySymbol.SourceModule.GetUnifiedAssemblies()); + assemblySymbol.SourceModule.GetUnifiedAssemblies(), + mergedAssemblyReferencesMapOpt); // Make sure that the given compilation holds on this instance of reference manager. Debug.Assert(ReferenceEquals(compilation._referenceManager, this) || HasCircularReference); diff --git a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingModuleSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingModuleSymbol.cs index 3627890d5e5fe..0c75f29ce320e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingModuleSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Retargeting/RetargetingModuleSymbol.cs @@ -130,6 +130,11 @@ public override NamespaceSymbol GlobalNamespace } } + internal override bool MightContainNoPiaLocalTypes() + { + return _underlyingModule.MightContainNoPiaLocalTypes(); + } + public override bool IsImplicitlyDeclared { get { return _underlyingModule.IsImplicitlyDeclared; } diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs index 6cd509f306499..eadd765ebc4d5 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceModuleSymbol.cs @@ -110,7 +110,7 @@ internal bool AnyReferencedAssembliesAreLinked } } - internal bool MightContainNoPiaLocalTypes() + internal override bool MightContainNoPiaLocalTypes() { return AnyReferencedAssembliesAreLinked || ContainsExplicitDefinitionOfNoPiaLocalTypes; diff --git a/src/Compilers/CSharp/Portable/Symbols/UsedAssembliesRecorder.cs b/src/Compilers/CSharp/Portable/Symbols/UsedAssembliesRecorder.cs index 7f3c0d51f9c98..38bd476508083 100644 --- a/src/Compilers/CSharp/Portable/Symbols/UsedAssembliesRecorder.cs +++ b/src/Compilers/CSharp/Portable/Symbols/UsedAssembliesRecorder.cs @@ -1,6 +1,12 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +#nullable enable + +using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp { @@ -34,62 +40,253 @@ private UsedAssembliesRecorder(CSharpCompilation compilation) _compilation = compilation; } - private void AddAssembliesUsedBySymbolReference(BoundExpression receiverOpt, Symbol symbol) + private void AddAssembliesUsedBySymbolReference(BoundExpression? receiverOpt, Symbol symbol) { if (symbol.IsStatic && receiverOpt?.Kind != BoundKind.TypeExpression) { _compilation.AddAssembliesUsedByTypeReference(symbol.ContainingType); } + + if (MightReferenceNoPiaLocalTypes(symbol)) + { + // Need to make sure we record assemblies containing canonical definitions for NoPia embedded types + switch (symbol.OriginalDefinition) + { + case FieldSymbol field: + addFromType(field.TypeWithAnnotations); + break; + case PropertySymbol property: + addFromType(property.TypeWithAnnotations); + addFromModifiers(property.RefCustomModifiers); + addFromParameters(property.Parameters); + break; + case MethodSymbol method: + addFromType(method.ReturnTypeWithAnnotations); + addFromModifiers(method.RefCustomModifiers); + addFromParameters(method.Parameters); + break; + case EventSymbol @event: + addFromType(@event.TypeWithAnnotations); + break; + default: + throw ExceptionUtilities.UnexpectedValue(symbol.Kind); + } + } + + void addFromParameters(ImmutableArray parameters) + { + foreach (var parameter in parameters) + { + addFromType(parameter.TypeWithAnnotations); + addFromModifiers(parameter.RefCustomModifiers); + } + } + + void addFromType(TypeWithAnnotations type) + { + _compilation.AddAssembliesUsedByTypeReference(type.Type); + addFromModifiers(type.CustomModifiers); + } + + void addFromModifiers(ImmutableArray customModifiers) + { + foreach (CSharpCustomModifier modifier in customModifiers) + { + _compilation.AddAssembliesUsedByTypeReference(modifier.ModifierSymbol); + } + } } - public override BoundNode VisitFieldAccess(BoundFieldAccess node) + private bool MightReferenceNoPiaLocalTypes(Symbol symbol) + { + ModuleSymbol containingModule = symbol.ContainingModule; + return containingModule is object && + containingModule != _compilation.SourceModule && + containingModule.MightContainNoPiaLocalTypes(); + } + + public override BoundNode? VisitFieldAccess(BoundFieldAccess node) { AddAssembliesUsedBySymbolReference(node.ReceiverOpt, node.FieldSymbol); return base.VisitFieldAccess(node); } - public override BoundNode VisitPropertyAccess(BoundPropertyAccess node) + public override BoundNode? VisitPropertyAccess(BoundPropertyAccess node) { AddAssembliesUsedBySymbolReference(node.ReceiverOpt, node.PropertySymbol); return base.VisitPropertyAccess(node); } - public override BoundNode VisitIndexerAccess(BoundIndexerAccess node) + public override BoundNode? VisitIndexerAccess(BoundIndexerAccess node) { AddAssembliesUsedBySymbolReference(node.ReceiverOpt, node.Indexer); return base.VisitIndexerAccess(node); } - public override BoundNode VisitCall(BoundCall node) + public override BoundNode? VisitCall(BoundCall node) { AddAssembliesUsedBySymbolReference(node.ReceiverOpt, node.Method); return base.VisitCall(node); } - public override BoundNode VisitEventAccess(BoundEventAccess node) + public override BoundNode? VisitEventAccess(BoundEventAccess node) { AddAssembliesUsedBySymbolReference(node.ReceiverOpt, node.EventSymbol); return base.VisitEventAccess(node); } - public override BoundNode VisitEventAssignmentOperator(BoundEventAssignmentOperator node) + public override BoundNode? VisitEventAssignmentOperator(BoundEventAssignmentOperator node) { AddAssembliesUsedBySymbolReference(node.ReceiverOpt, node.Event); return base.VisitEventAssignmentOperator(node); } - public override BoundNode VisitNameOfOperator(BoundNameOfOperator node) + public override BoundNode? VisitNameOfOperator(BoundNameOfOperator node) { - if (node.Argument is BoundNamespaceExpression nsExpr) + switch (node.Argument) { - Debug.Assert(!nsExpr.NamespaceSymbol.IsGlobalNamespace); - _compilation.AddAssembliesUsedByNamespaceReference(nsExpr.NamespaceSymbol); + case BoundNamespaceExpression nsExpr: + Debug.Assert(!nsExpr.NamespaceSymbol.IsGlobalNamespace); + _compilation.AddAssembliesUsedByNamespaceReference(nsExpr.NamespaceSymbol); + break; + + case BoundMethodGroup methodGroup: + if (methodGroup.ReceiverOpt?.Kind != BoundKind.TypeExpression) + { + foreach (var symbol in methodGroup.Methods) + { + _compilation.AddAssembliesUsedByTypeReference(symbol.ContainingType); + } + } + break; + case BoundPropertyGroup _: + ExceptionUtilities.UnexpectedValue(node.Argument.Kind); + break; } return base.VisitNameOfOperator(node); } - public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) + public override BoundNode? VisitConversion(BoundConversion node) + { + VisitConversion(node.Conversion); + + // Need to make sure we record assemblies containing canonical definitions for NoPia embedded types + HashSet? lookedAt = null; + + if (!node.Conversion.IsIdentity) + { + addUsedAssemblies(node.Operand.Type); + } + + return base.VisitConversion(node); + + void addUsedAssemblies(TypeSymbol? typeOpt) + { + while (true) + { + switch (typeOpt) + { + case null: + case DynamicTypeSymbol _: + break; + case PointerTypeSymbol pointer: + typeOpt = pointer.PointedAtTypeWithAnnotations.DefaultType; + continue; + case ArrayTypeSymbol array: + typeOpt = array.ElementTypeWithAnnotations.DefaultType; + continue; + case NamedTypeSymbol named: + if (!shouldVisit(named)) + { + break; + } + + named = named.TupleUnderlyingTypeOrSelf(); + addAssembliesUsedByTypeArguments(named); + addAssembliesUsedByImplementedInterfaces(named.OriginalDefinition); + + typeOpt = named.BaseTypeNoUseSiteDiagnostics; + continue; + case TypeParameterSymbol typeParameter: + if (shouldVisit(typeParameter)) + { + addAssembliesUsedByConstraints(typeParameter.OriginalDefinition); + } + break; + default: + throw ExceptionUtilities.UnexpectedValue(typeOpt.TypeKind); + } + + break; + } + } + + bool shouldVisit(TypeSymbol type) + { + return (lookedAt ??= new HashSet(Symbols.SymbolEqualityComparer.ConsiderEverything)).Add(type); + } + + void addAssembliesUsedByTypeArguments(NamedTypeSymbol current) + { + do + { + foreach (var typeArgument in current.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics) + { + addUsedAssemblies(typeArgument.Type); + } + + current = current.ContainingType; + } + while (current is object); + } + + void addAssembliesUsedByImplementedInterfaces(NamedTypeSymbol current) + { + Debug.Assert(current.IsDefinition); + if (MightReferenceNoPiaLocalTypes(current)) + { + foreach (var @interface in current.InterfacesNoUseSiteDiagnostics()) + { + _compilation.AddAssembliesUsedByTypeReference(@interface); + addUsedAssemblies(@interface); + } + } + } + + void addAssembliesUsedByConstraints(TypeParameterSymbol current) + { + Debug.Assert(current.IsDefinition); + if (current.ContainingModule == _compilation.SourceModule) + { + foreach (var type in current.ConstraintTypesNoUseSiteDiagnostics) + { + addUsedAssemblies(type.Type); + } + } + } + } + + private void VisitConversion(Conversion conversion) + { + foreach (var underlying in conversion.UnderlyingConversions.NullToEmpty()) + { + VisitConversion(underlying); + } + + if (conversion.Kind == ConversionKind.Deconstruction) + { + Visit(conversion.DeconstructionInfo.Invocation); + } + } + + public override BoundNode? VisitCollectionElementInitializer(BoundCollectionElementInitializer node) + { + AddAssembliesUsedBySymbolReference(receiverOpt: null, node.AddMethod); + return base.VisitCollectionElementInitializer(node); + } + + public override BoundNode? VisitBinaryOperator(BoundBinaryOperator node) { // It is very common for bound trees to be left-heavy binary operators, eg, // a + b + c + d + ... @@ -116,7 +313,7 @@ public override BoundNode VisitBinaryOperator(BoundBinaryOperator node) return null; } - public override BoundNode VisitUserDefinedConditionalLogicalOperator(BoundUserDefinedConditionalLogicalOperator node) + public override BoundNode? VisitUserDefinedConditionalLogicalOperator(BoundUserDefinedConditionalLogicalOperator node) { // In order to avoid blowing the stack, we end up visiting right children // before left children; this should not be a problem diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/UsedAssembliesTests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/UsedAssembliesTests.cs index 94c004426b8c5..e2b601a4ca941 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/UsedAssembliesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/UsedAssembliesTests.cs @@ -130,13 +130,24 @@ private static void AssertUsedAssemblyReferences(string source, MetadataReferenc AssertEx.Equal(comp.References.Where(r => r.Properties.Kind == MetadataImageKind.Assembly), comp.GetUsedAssemblyReferences()); } - private void CompileWithUsedAssemblyReferences(string source, TargetFramework targetFramework, params MetadataReference[] references) + private ImmutableArray CompileWithUsedAssemblyReferences(string source, TargetFramework targetFramework, params MetadataReference[] references) { - Compilation comp = CreateCompilation(source, targetFramework: targetFramework, references: references); - CompileWithUsedAssemblyReferences(comp); + return CompileWithUsedAssemblyReferences(source, targetFramework, options: null, references); } - private void CompileWithUsedAssemblyReferences(Compilation comp, string expectedOutput = null) + private ImmutableArray CompileWithUsedAssemblyReferences(string source, TargetFramework targetFramework, CSharpCompilationOptions options, params MetadataReference[] references) + { + options ??= TestOptions.ReleaseDll; + options = options.WithSpecificDiagnosticOptions(options.SpecificDiagnosticOptions.Add("CS1591", ReportDiagnostic.Suppress)); + + Compilation comp = CreateEmptyCompilation(source, + references: TargetFrameworkUtil.GetReferences(targetFramework).Concat(references), + options: options, + parseOptions: TestOptions.Regular.WithDocumentationMode(DocumentationMode.Diagnose)); + return CompileWithUsedAssemblyReferences(comp); + } + + private ImmutableArray CompileWithUsedAssemblyReferences(Compilation comp, string expectedOutput = null) { var used = comp.GetUsedAssemblyReferences(); CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: expectedOutput).VerifyDiagnostics(); @@ -146,6 +157,18 @@ private void CompileWithUsedAssemblyReferences(Compilation comp, string expected var comp2 = comp.RemoveAllReferences().AddReferences(used.Concat(comp.References.Where(r => r.Properties.Kind == MetadataImageKind.Module))); comp2.VerifyDiagnostics(); CompileAndVerify(comp2, verify: Verification.Skipped, expectedOutput: expectedOutput).VerifyDiagnostics(); + + return used; + } + + private ImmutableArray CompileWithUsedAssemblyReferences(string source, params MetadataReference[] references) + { + return CompileWithUsedAssemblyReferences(source, TargetFramework.Standard, references); + } + + private void CompileWithUsedAssemblyReferences(string source, CSharpCompilationOptions options, params MetadataReference[] references) + { + CompileWithUsedAssemblyReferences(source, TargetFramework.Standard, options: options, references); } [Fact] @@ -325,6 +348,24 @@ public static void Main() verify(source2, comp0Ref, comp1Ref); verify(source2, comp0ImageRef, comp1Ref); + var source3 = +@" +using static C1; + +public class C2 +{ + public static void Main() + { + Assert(F1); + } + + [System.Diagnostics.Conditional(""Always"")] + public static void Assert(int condition) {} +} +"; + + verify(source3, comp0Ref, comp1Ref); + void verify(string source2, MetadataReference reference0, MetadataReference reference1) where TAssemblySymbol : AssemblySymbol { Compilation comp2 = AssertUsedAssemblyReferences(source2, reference0, reference1); @@ -1488,156 +1529,426 @@ public static void Main() } [Fact] - public void FieldDeclaration_01() + public void MethodReference_08() { + var source0 = +@" +public class C0 +{ +} +"; + var comp0 = CreateCompilation(source0); + comp0.VerifyDiagnostics(); + var comp0Ref = comp0.ToMetadataReference(); + var source1 = @" -namespace N1 +public class C1 { - public class C1 + public C0 M1() => null; +} +"; + var comp1 = CreateCompilation(source1, references: new[] { comp0Ref }); + comp1.VerifyDiagnostics(); + + var comp1Ref = comp1.ToMetadataReference(); + var comp1ImageRef = comp1.EmitToImageReference(); + + var source3 = +@" +public class C3 +{ + public static void Main() { - public class C11 - { - } + _ = nameof(C1.M1); } } "; - var comp1 = CreateCompilation(source1); - var comp1Ref = comp1.ToMetadataReference(); - verify(comp1Ref, + AssertUsedAssemblyReferences(source3, comp0Ref, comp1Ref); + AssertUsedAssemblyReferences(source3, comp0Ref, comp1ImageRef); + + var source5 = @" -public class C2 +using static C1; + +public class C3 { - public static N1.C1.C11 F1 = null; + public static void Main() + { + _ = nameof(M1); + } } -"); - verify(comp1Ref, +"; + + AssertUsedAssemblyReferences(source5, new[] { comp0Ref, comp1Ref }, + // (2,1): hidden CS8019: Unnecessary using directive. + // using static C1; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using static C1;").WithLocation(2, 1), + // (8,20): error CS0103: The name 'M1' does not exist in the current context + // _ = nameof(M1); + Diagnostic(ErrorCode.ERR_NameNotInContext, "M1").WithArguments("M1").WithLocation(8, 20) + ); + + + var source6 = @" -using N2 = N1; -public class C2 +public class C3 { - public static N2.C1.C11 F1 = null; + public static void Main() + { + var x = new C1(); + _ = nameof(x.M1); + } } -"); - verify(comp1Ref, +"; + + AssertUsedAssemblyReferences(source6, comp0Ref, comp1Ref); + AssertUsedAssemblyReferences(source6, comp0Ref, comp1ImageRef); + } + + [Fact] + public void MethodReference_09() + { + var source0 = @" -using N1; -public class C2 +public class C0 { - public static C1.C11 F1 = null; } -"); - verify(comp1Ref, +"; + var comp0 = CreateCompilation(source0); + comp0.VerifyDiagnostics(); + var comp0Ref = comp0.ToMetadataReference(); + + var source1 = @" -using static N1.C1; -public class C2 +public class C1 { - public static C11 F1 = null; + public static C0 M1() => null; } -"); - verify(comp1Ref, +"; + var comp1 = CreateCompilation(source1, references: new[] { comp0Ref }); + comp1.VerifyDiagnostics(); + + var comp1Ref = comp1.ToMetadataReference(); + var comp1ImageRef = comp1.EmitToImageReference(); + + var source3 = @" -using C111 = N1.C1.C11; -public class C2 +public class C3 { - public static C111 F1 = null; + public static void Main() + { + _ = nameof(C1.M1); + } } -"); +"; - void verify(MetadataReference reference, string source2) - { - AssertUsedAssemblyReferences(source2, reference); - } + AssertUsedAssemblyReferences(source3, comp0Ref, comp1Ref); + AssertUsedAssemblyReferences(source3, comp0Ref, comp1ImageRef); + + var source5 = +@" +using static C1; + +public class C3 +{ + public static void Main() + { + _ = nameof(M1); + } +} +"; + + AssertUsedAssemblyReferences(source5, comp0Ref, comp1Ref); + AssertUsedAssemblyReferences(source5, comp0Ref, comp1ImageRef); } [Fact] - public void UnusedUsings_01() + public void MethodReference_10() { var source1 = @" -namespace N1 +public class C1 { - public static class C1 - { - } + [System.Diagnostics.Conditional(""Always"")] + public static void M1(){} } "; var comp1 = CreateCompilation(source1); var comp1Ref = comp1.ToMetadataReference(); + var comp1ImageRef = comp1.EmitToImageReference(); - verify1(comp1Ref, + var source2 = @" -using N1; - public class C2 { + public static void Main() + { + C1.M1(); + } } -", - // (2,1): hidden CS8019: Unnecessary using directive. - // using N1; - Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using N1;").WithLocation(2, 1) - ); +"; - verify1(comp1Ref, + verify(source2, comp1ImageRef); + verify(source2, comp1Ref); + + + var source3 = @" -using static N1.C1; +using static C1; public class C2 { + public static void Main() + { + M1(); + } } -", - // (2,1): hidden CS8019: Unnecessary using directive. - // using static N1.C1; - Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using static N1.C1;").WithLocation(2, 1) - ); +"; - verify1(comp1Ref, -@" -using alias = N1.C1; + verify(source3, comp1ImageRef); + verify(source3, comp1Ref); -public class C2 + void verify(string source2, MetadataReference reference) where TAssemblySymbol : AssemblySymbol + { + Compilation comp2 = AssertUsedAssemblyReferences(source2, reference); + Assert.IsType(((CSharpCompilation)comp2).GetAssemblyOrModuleSymbol(reference)); + } + } + + [Fact] + public void MethodReference_11() + { + var source0 = +@" +public class C0 : System.Collections.IEnumerable { + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + throw new System.NotImplementedException(); + } } -", - // (2,1): hidden CS8019: Unnecessary using directive. - // using alias = N1.C1; - Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using alias = N1.C1;").WithLocation(2, 1) - ); +"; + var comp0 = CreateCompilation(source0); + var comp0Ref = comp0.ToMetadataReference(); - verify1(comp1Ref.WithAliases(new[] { "N1C1" }), + var source1 = @" -extern alias N1C1; - -public class C2 +public static class C1 { + public static void Add(this C0 x, int y) => throw null; } -", - // (2,1): hidden CS8020: Unused extern alias. - // extern alias N1C1; - Diagnostic(ErrorCode.HDN_UnusedExternAlias, "extern alias N1C1;").WithLocation(2, 1) - ); - - verify1(comp1Ref, -@"namespace N2 { -using N1; +"; + var comp1 = CreateCompilation(source1, references: new[] { comp0Ref }); + var comp1Ref = comp1.ToMetadataReference(); + var source2 = +@" public class C2 { + public static void Main() + { + _ = new C0() { 1 }; + } } -}", - // (2,1): hidden CS8019: Unnecessary using directive. - // using N1; - Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using N1;").WithLocation(2, 1) - ); +"; - verify1(comp1Ref, -@"namespace N2 { -using static N1.C1; + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp1Ref); + } -public class C2 + [Fact] + public void MethodReference_12() + { + var source0 = +@" +public class C0 : System.Collections.IEnumerable { -} + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + throw new System.NotImplementedException(); + } +} +"; + var comp0 = CreateCompilation(source0); + var comp0Ref = comp0.ToMetadataReference(); + + var source1 = +@" +public static class C1 +{ + [System.Diagnostics.Conditional(""Always"")] + public static void Add(this C0 x, int y) => throw null; +} +"; + var comp1 = CreateCompilation(source1, references: new[] { comp0Ref }); + var comp1Ref = comp1.ToMetadataReference(); + + var source2 = +@" +public class C2 +{ + public static void Main() + { + _ = new C0() { 1 }; + } +} +"; + + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp1Ref); + } + + [Fact] + public void FieldDeclaration_01() + { + var source1 = +@" +namespace N1 +{ + public class C1 + { + public class C11 + { + } + } +} +"; + var comp1 = CreateCompilation(source1); + + var comp1Ref = comp1.ToMetadataReference(); + verify(comp1Ref, +@" +public class C2 +{ + public static N1.C1.C11 F1 = null; +} +"); + verify(comp1Ref, +@" +using N2 = N1; +public class C2 +{ + public static N2.C1.C11 F1 = null; +} +"); + verify(comp1Ref, +@" +using N1; +public class C2 +{ + public static C1.C11 F1 = null; +} +"); + verify(comp1Ref, +@" +using static N1.C1; +public class C2 +{ + public static C11 F1 = null; +} +"); + verify(comp1Ref, +@" +using C111 = N1.C1.C11; +public class C2 +{ + public static C111 F1 = null; +} +"); + + void verify(MetadataReference reference, string source2) + { + AssertUsedAssemblyReferences(source2, reference); + } + } + + [Fact] + public void UnusedUsings_01() + { + var source1 = +@" +namespace N1 +{ + public static class C1 + { + } +} +"; + var comp1 = CreateCompilation(source1); + var comp1Ref = comp1.ToMetadataReference(); + + verify1(comp1Ref, +@" +using N1; + +public class C2 +{ +} +", + // (2,1): hidden CS8019: Unnecessary using directive. + // using N1; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using N1;").WithLocation(2, 1) + ); + + verify1(comp1Ref, +@" +using static N1.C1; + +public class C2 +{ +} +", + // (2,1): hidden CS8019: Unnecessary using directive. + // using static N1.C1; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using static N1.C1;").WithLocation(2, 1) + ); + + verify1(comp1Ref, +@" +using alias = N1.C1; + +public class C2 +{ +} +", + // (2,1): hidden CS8019: Unnecessary using directive. + // using alias = N1.C1; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using alias = N1.C1;").WithLocation(2, 1) + ); + + verify1(comp1Ref.WithAliases(new[] { "N1C1" }), +@" +extern alias N1C1; + +public class C2 +{ +} +", + // (2,1): hidden CS8020: Unused extern alias. + // extern alias N1C1; + Diagnostic(ErrorCode.HDN_UnusedExternAlias, "extern alias N1C1;").WithLocation(2, 1) + ); + + verify1(comp1Ref, +@"namespace N2 { +using N1; + +public class C2 +{ +} +}", + // (2,1): hidden CS8019: Unnecessary using directive. + // using N1; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using N1;").WithLocation(2, 1) + ); + + verify1(comp1Ref, +@"namespace N2 { +using static N1.C1; + +public class C2 +{ +} }", // (2,1): hidden CS8019: Unnecessary using directive. // using static N1.C1; @@ -1822,6 +2133,15 @@ public static void Main() verify(source2, comp0Ref, comp1Ref); verify(source2, comp0ImageRef, comp1Ref); + var comp3 = CreateCompilation(source0); + var comp3Ref = comp3.ToMetadataReference(embedInteropTypes: false); + var comp3ImageRef = comp3.EmitToImageReference(embedInteropTypes: false); + + verify(source2, comp3ImageRef, comp1ImageRef); + verify(source2, comp3Ref, comp1ImageRef); + verify(source2, comp3Ref, comp1Ref); + verify(source2, comp3ImageRef, comp1Ref); + void verify(string source2, MetadataReference reference0, MetadataReference reference1) where TAssemblySymbol : AssemblySymbol { Compilation comp2 = AssertUsedAssemblyReferences(source2, new[] { reference0, reference1 }, reference1); @@ -1829,6 +2149,786 @@ void verify(string source2, MetadataReference reference0, Metad } } + [Fact] + public void NoPia_02() + { + var source0 = +@" +using System; +using System.Runtime.InteropServices; + +[assembly: PrimaryInteropAssemblyAttribute(1,1)] +[assembly: Guid(""f9c2d51d-4f44-45f0-9eda-c9d599b58257"")] + +[ComImport()] +[Guid(""f9c2d51d-4f44-45f0-9eda-c9d599b58279"")] +public interface ITest33 +{ +} +"; + var comp0 = CreateCompilation(source0); + comp0.VerifyDiagnostics(); + + var comp0Ref = comp0.ToMetadataReference(embedInteropTypes: true); + var comp0ImageRef = comp0.EmitToImageReference(embedInteropTypes: true); + + var source1 = +@" +public class C1 +{ + public static ITest33 F0 = null; +} +"; + var comp1 = AssertUsedAssemblyReferences(source1, references: new[] { comp0Ref }); + + var comp1Ref = comp1.ToMetadataReference(); + var comp1ImageRef = comp1.EmitToImageReference(); + + var comp3 = CreateCompilation(source0); + var comp3Ref = comp3.ToMetadataReference(embedInteropTypes: false); + var comp3ImageRef = comp3.EmitToImageReference(embedInteropTypes: false); + + verify( +@" +public class C2 +{ + public static void Main() + { + _ = C1.F0; + } +} +"); + + verify( +@" +public class C2 +{ + public static void Main() + { + _ = nameof(C1.F0); + } +} +"); + + verify( +@" +public class C2 +{ + /// + /// + /// + public static void Main() + { + } +} +"); + + void verify(string source2) + { + CompileWithUsedAssemblyReferences(source2, comp0ImageRef, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp1Ref); + CompileWithUsedAssemblyReferences(source2, comp0ImageRef, comp1Ref); + + CompileWithUsedAssemblyReferences(source2, comp3ImageRef, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp3Ref, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp3Ref, comp1Ref); + CompileWithUsedAssemblyReferences(source2, comp3ImageRef, comp1Ref); + } + } + + [Fact] + public void NoPia_03() + { + var source0 = +@" +using System; +using System.Runtime.InteropServices; + +[assembly: PrimaryInteropAssemblyAttribute(1,1)] +[assembly: Guid(""f9c2d51d-4f44-45f0-9eda-c9d599b58257"")] + +[ComImport()] +[Guid(""f9c2d51d-4f44-45f0-9eda-c9d599b58279"")] +public interface ITest33 +{ +} +"; + var comp0 = CreateCompilation(source0); + comp0.VerifyDiagnostics(); + + var comp0Ref = comp0.ToMetadataReference(embedInteropTypes: true); + var comp0ImageRef = comp0.EmitToImageReference(embedInteropTypes: true); + + var source1 = +@" +public class C1 +{ + public static ITest33 M0() => null; +} +"; + var comp1 = AssertUsedAssemblyReferences(source1, references: new[] { comp0Ref }); + + var comp1Ref = comp1.ToMetadataReference(); + var comp1ImageRef = comp1.EmitToImageReference(); + + var comp3 = CreateCompilation(source0); + var comp3Ref = comp3.ToMetadataReference(embedInteropTypes: false); + var comp3ImageRef = comp3.EmitToImageReference(embedInteropTypes: false); + + verify( +@" +public class C2 +{ + public static void Main() + { + C1.M0(); + } +} +"); + + verify( +@" +public class C2 +{ + public static void Main() + { + _ = nameof(C1.M0); + } +} +"); + + verify( +@" +public class C2 +{ + /// + /// + /// + public static void Main() + { + } +} +"); + + void verify(string source2) + { + CompileWithUsedAssemblyReferences(source2, comp0ImageRef, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp1Ref); + CompileWithUsedAssemblyReferences(source2, comp0ImageRef, comp1Ref); + + CompileWithUsedAssemblyReferences(source2, comp3ImageRef, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp3Ref, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp3Ref, comp1Ref); + CompileWithUsedAssemblyReferences(source2, comp3ImageRef, comp1Ref); + } + } + + [Fact] + public void NoPia_04() + { + var source0 = +@" +using System; +using System.Runtime.InteropServices; + +[assembly: PrimaryInteropAssemblyAttribute(1,1)] +[assembly: Guid(""f9c2d51d-4f44-45f0-9eda-c9d599b58257"")] + +[ComImport()] +[Guid(""f9c2d51d-4f44-45f0-9eda-c9d599b58279"")] +public interface ITest33 +{ +} +"; + var comp0 = CreateCompilation(source0); + comp0.VerifyDiagnostics(); + + var comp0Ref = comp0.ToMetadataReference(embedInteropTypes: true); + var comp0ImageRef = comp0.EmitToImageReference(embedInteropTypes: true); + + var source1 = +@" +public class C1 +{ + public static object M0(ITest33 x) => null; +} +"; + var comp1 = AssertUsedAssemblyReferences(source1, references: new[] { comp0Ref }); + + var comp1Ref = comp1.ToMetadataReference(); + var comp1ImageRef = comp1.EmitToImageReference(); + + var comp3 = CreateCompilation(source0); + var comp3Ref = comp3.ToMetadataReference(embedInteropTypes: false); + var comp3ImageRef = comp3.EmitToImageReference(embedInteropTypes: false); + + verify( +@" +public class C2 +{ + public static void Main() + { + C1.M0(null); + } +} +"); + + verify( +@" +public class C2 +{ + public static void Main() + { + _ = nameof(C1.M0); + } +} +"); + + verify( +@" +public class C2 +{ + /// + /// + /// + public static void Main() + { + } +} +"); + + void verify(string source2) + { + CompileWithUsedAssemblyReferences(source2, comp0ImageRef, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp1Ref); + CompileWithUsedAssemblyReferences(source2, comp0ImageRef, comp1Ref); + + CompileWithUsedAssemblyReferences(source2, comp3ImageRef, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp3Ref, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp3Ref, comp1Ref); + CompileWithUsedAssemblyReferences(source2, comp3ImageRef, comp1Ref); + } + } + + [Fact] + public void NoPia_05() + { + var source0 = +@" +using System; +using System.Runtime.InteropServices; + +[assembly: PrimaryInteropAssemblyAttribute(1,1)] +[assembly: Guid(""f9c2d51d-4f44-45f0-9eda-c9d599b58257"")] + +public delegate void DTest33(); +"; + var comp0 = CreateCompilation(source0); + comp0.VerifyDiagnostics(); + + var comp0Ref = comp0.ToMetadataReference(embedInteropTypes: true); + var comp0ImageRef = comp0.EmitToImageReference(embedInteropTypes: true); + + var source1 = +@" +#pragma warning disable CS0414 +public class C1 +{ + public static event DTest33 E0 = null; +} +"; + var comp1 = AssertUsedAssemblyReferences(source1, references: new[] { comp0Ref }); + + var comp1Ref = comp1.ToMetadataReference(); + var comp1ImageRef = comp1.EmitToImageReference(); + + var comp3 = CreateCompilation(source0); + var comp3Ref = comp3.ToMetadataReference(embedInteropTypes: false); + var comp3ImageRef = comp3.EmitToImageReference(embedInteropTypes: false); + + verify( +@" +public class C2 +{ + public static void Main() + { + C1.E0 += Main; + } +} +"); + + verify( +@" +public class C2 +{ + public static void Main() + { + _ = nameof(C1.E0); + } +} +"); + + verify( +@" +public class C2 +{ + /// + /// + /// + public static void Main() + { + } +} +"); + + void verify(string source2) + { + CompileWithUsedAssemblyReferences(source2, comp0ImageRef, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp1Ref); + CompileWithUsedAssemblyReferences(source2, comp0ImageRef, comp1Ref); + + CompileWithUsedAssemblyReferences(source2, comp3ImageRef, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp3Ref, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp3Ref, comp1Ref); + CompileWithUsedAssemblyReferences(source2, comp3ImageRef, comp1Ref); + } + } + + [Fact] + public void NoPia_06() + { + var source0 = +@" +using System; +using System.Runtime.InteropServices; + +[assembly: PrimaryInteropAssemblyAttribute(1,1)] +[assembly: Guid(""f9c2d51d-4f44-45f0-9eda-c9d599b58257"")] + +[ComImport()] +[Guid(""f9c2d51d-4f44-45f0-9eda-c9d599b58279"")] +public interface ITest33 +{ +} +"; + var comp0 = CreateCompilation(source0); + comp0.VerifyDiagnostics(); + + var comp0Ref = comp0.ToMetadataReference(embedInteropTypes: true); + var comp0ImageRef = comp0.EmitToImageReference(embedInteropTypes: true); + + var source1 = +@" +public class C1 +{ + public static ITest33 P0 => null; +} +"; + var comp1 = AssertUsedAssemblyReferences(source1, references: new[] { comp0Ref }); + + var comp1Ref = comp1.ToMetadataReference(); + var comp1ImageRef = comp1.EmitToImageReference(); + + var comp3 = CreateCompilation(source0); + var comp3Ref = comp3.ToMetadataReference(embedInteropTypes: false); + var comp3ImageRef = comp3.EmitToImageReference(embedInteropTypes: false); + + verify( +@" +public class C2 +{ + public static void Main() + { + _ = C1.P0; + } +} +"); + + verify( +@" +public class C2 +{ + public static void Main() + { + _ = nameof(C1.P0); + } +} +"); + + verify( +@" +public class C2 +{ + /// + /// + /// + public static void Main() + { + } +} +"); + + void verify(string source2) + { + CompileWithUsedAssemblyReferences(source2, comp0ImageRef, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp1Ref); + CompileWithUsedAssemblyReferences(source2, comp0ImageRef, comp1Ref); + + CompileWithUsedAssemblyReferences(source2, comp3ImageRef, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp3Ref, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp3Ref, comp1Ref); + CompileWithUsedAssemblyReferences(source2, comp3ImageRef, comp1Ref); + } + } + + [Fact] + public void NoPia_07() + { + var source0 = +@" +using System; +using System.Runtime.InteropServices; + +[assembly: PrimaryInteropAssemblyAttribute(1,1)] +[assembly: Guid(""f9c2d51d-4f44-45f0-9eda-c9d599b58257"")] + +[ComImport()] +[Guid(""f9c2d51d-4f44-45f0-9eda-c9d599b58279"")] +public interface ITest33 +{ +} +"; + var comp0 = CreateCompilation(source0); + comp0.VerifyDiagnostics(); + + var comp0Ref = comp0.ToMetadataReference(embedInteropTypes: true); + var comp0ImageRef = comp0.EmitToImageReference(embedInteropTypes: true); + + var source1 = +@" +public class C1 +{ + public object this[ITest33 x] => null; +} +"; + var comp1 = AssertUsedAssemblyReferences(source1, references: new[] { comp0Ref }); + + var comp1Ref = comp1.ToMetadataReference(); + var comp1ImageRef = comp1.EmitToImageReference(); + + var comp3 = CreateCompilation(source0); + var comp3Ref = comp3.ToMetadataReference(embedInteropTypes: false); + var comp3ImageRef = comp3.EmitToImageReference(embedInteropTypes: false); + + verify( +@" +public class C2 +{ + public static void Main() + { + _ = new C1()[null]; + } +} +"); + + void verify(string source2) + { + CompileWithUsedAssemblyReferences(source2, comp0ImageRef, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp1Ref); + CompileWithUsedAssemblyReferences(source2, comp0ImageRef, comp1Ref); + + CompileWithUsedAssemblyReferences(source2, comp3ImageRef, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp3Ref, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp3Ref, comp1Ref); + CompileWithUsedAssemblyReferences(source2, comp3ImageRef, comp1Ref); + } + } + + [Fact] + public void NoPia_08() + { + var source0 = +@" +using System; +using System.Runtime.InteropServices; + +[assembly: PrimaryInteropAssemblyAttribute(1,1)] +[assembly: Guid(""f9c2d51d-4f44-45f0-9eda-c9d599b58257"")] + +[ComImport()] +[Guid(""f9c2d51d-4f44-45f0-9eda-c9d599b58279"")] +public interface ITest33 +{ +} +"; + var comp0 = CreateCompilation(source0); + comp0.VerifyDiagnostics(); + + var comp0Ref = comp0.ToMetadataReference(embedInteropTypes: true); + var comp0ImageRef = comp0.EmitToImageReference(embedInteropTypes: true); + + var source1 = +@" +public class C1 : ITest33, I1 +{ +} + +public interface I1 +{ +} + +public class C2 : I2, I1 +{ +} + +public class C3 : C2 +{ +} + +public interface I2 +{ +} + +public interface I3 : ITest33, I1 +{ +} + +public interface I4 : I3 +{ +} + +public struct S1 : ITest33, I1 +{ +} +"; + var comp1 = AssertUsedAssemblyReferences(source1, references: new[] { comp0Ref }); + + var comp1Ref = comp1.ToMetadataReference(); + var comp1ImageRef = comp1.EmitToImageReference(); + + var comp3 = CreateCompilation(source0); + var comp3Ref = comp3.ToMetadataReference(embedInteropTypes: false); + var comp3ImageRef = comp3.EmitToImageReference(embedInteropTypes: false); + + verify( +@" +public class C +{ + public static void Main() + { + _ = (I2)new C2(); + } +} +"); + + verify( +@" +public class C +{ + public static void Main() + { + _ = (I1)new C2(); + } +} +"); + + verify( +@" +public class C +{ + public static void Main() + { + _ = (I1)new C1(); + } +} +"); + + verify( +@" +public class C +{ + public static void Main() + { + I2 x = new C2(); + _ = x; + } +} +"); + + verify( +@" +public class C +{ + public static void Main() + { + I1 x = new C2(); + _ = x; + } +} +"); + + verify( +@" +public class C +{ + public static void Main() + { + I1 x = new C1(); + _ = x; + } +} +"); + + verify( +@" +public class C +{ + public static void Main() + { + _ = (I2)new C3(); + } +} +"); + + verify( +@" +public class C +{ + public static void Main() + { + I2 x = new C3(); + _ = x; + } +} +"); + + verify( +@" +public class C +{ + public static void Main() + { + I3 x = null; + I1 y = x; + _ = y; + } +} +"); + + verify( +@" +public class C +{ + public static void Main() + { + I4 x = null; + I1 y = x; + _ = y; + } +} +"); + + verify( +@" +public class C +{ + public static void Main() + { + I x = null; + I y = x; + _ = y; + } +} + +interface I {} +"); + + verify( +@" +public class C +{ + public static void Main() + { + I x = null; + I y = x; + _ = y; + } +} + +interface I {} +"); + + verify( +@" +public class C +{ + public static void Main() + { + I[] x = new I[10]; + _ = x; + } +} + +interface I {} +"); + + verify( +@" +public class C +{ + public static void Main() + { + I1[] x = new C1[10]; + _ = x; + } +} +"); + + verify( +@" +public class C +{ + public static void Main() + { + S1? x = null; + I1 y = x; + _ = y; + } +} +"); + + verify( +@" +public class C +{ + public static void Main() where T : C1 + { + T x = null; + I1 y = x; + _ = y; + } +} +"); + void verify(string source2) + { + CompileWithUsedAssemblyReferences(source2, comp0ImageRef, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp1Ref); + CompileWithUsedAssemblyReferences(source2, comp0ImageRef, comp1Ref); + + CompileWithUsedAssemblyReferences(source2, comp3ImageRef, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp3Ref, comp1ImageRef); + CompileWithUsedAssemblyReferences(source2, comp3Ref, comp1Ref); + CompileWithUsedAssemblyReferences(source2, comp3ImageRef, comp1Ref); + } + } + [Fact] public void ArraysAndPointers_01() { @@ -3245,6 +4345,22 @@ End Get Set End Set End Property + + Public Shared Property P2(x As Integer) As C0 + Get + Return Nothing + End Get + Set + End Set + End Property + + Public Shared Property P2(x As String) As C0 + Get + Return Nothing + End Get + Set + End Set + End Property End Class "; var comp1 = CreateVisualBasicCompilation(source1, referencedAssemblies: TargetFrameworkUtil.GetReferences(TargetFramework.Standard, new[] { comp0ImageRef })); @@ -3329,6 +4445,84 @@ public static void Main() // _ = P1[0]; Diagnostic(ErrorCode.ERR_BindToBogusProp2, "P1").WithArguments("C1.P1[int]", "C1.get_P1(int)", "C1.set_P1(int, C0)").WithLocation(8, 13) ); + + var source6 = +@" +public class C3 +{ + public static void Main() + { + _ = nameof(C1.P1); + } +} +"; + + AssertUsedAssemblyReferences(source6, references, + // (6,23): error CS1545: Property, indexer, or event 'C1.P1[int]' is not supported by the language; try directly calling accessor methods 'C1.get_P1(int)' or 'C1.set_P1(int, C0)' + // _ = nameof(C1.P1); + Diagnostic(ErrorCode.ERR_BindToBogusProp2, "P1").WithArguments("C1.P1[int]", "C1.get_P1(int)", "C1.set_P1(int, C0)").WithLocation(6, 23) + ); + + var source7 = +@" +using static C1; + +public class C2 +{ + public static void Main() + { + _ = nameof(P1); + } +} +"; + + AssertUsedAssemblyReferences(source7, references, + // (2,1): hidden CS8019: Unnecessary using directive. + // using static C1; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using static C1;").WithLocation(2, 1), + // (8,20): error CS1545: Property, indexer, or event 'C1.P1[int]' is not supported by the language; try directly calling accessor methods 'C1.get_P1(int)' or 'C1.set_P1(int, C0)' + // _ = nameof(P1); + Diagnostic(ErrorCode.ERR_BindToBogusProp2, "P1").WithArguments("C1.P1[int]", "C1.get_P1(int)", "C1.set_P1(int, C0)").WithLocation(8, 20) + ); + + var source8 = +@" +public class C3 +{ + public static void Main() + { + _ = nameof(C1.P2); + } +} +"; + + AssertUsedAssemblyReferences(source8, references, + // (6,23): error CS1545: Property, indexer, or event 'C1.P2[int]' is not supported by the language; try directly calling accessor methods 'C1.get_P2(int)' or 'C1.set_P2(int, C0)' + // _ = nameof(C1.P2); + Diagnostic(ErrorCode.ERR_BindToBogusProp2, "P2").WithArguments("C1.P2[int]", "C1.get_P2(int)", "C1.set_P2(int, C0)").WithLocation(6, 23) + ); + + var source9 = +@" +using static C1; + +public class C2 +{ + public static void Main() + { + _ = nameof(P2); + } +} +"; + + AssertUsedAssemblyReferences(source9, references, + // (2,1): hidden CS8019: Unnecessary using directive. + // using static C1; + Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using static C1;").WithLocation(2, 1), + // (8,20): error CS1545: Property, indexer, or event 'C1.P2[int]' is not supported by the language; try directly calling accessor methods 'C1.get_P2(int)' or 'C1.set_P2(int, C0)' + // _ = nameof(P2); + Diagnostic(ErrorCode.ERR_BindToBogusProp2, "P2").WithArguments("C1.P2[int]", "C1.get_P2(int)", "C1.set_P2(int, C0)").WithLocation(8, 20) + ); } [Fact] @@ -3563,23 +4757,270 @@ class D3 } [Fact] - public void UseMissingAccessor() + public void WellKnownTypeReference_05() { - var text = @" -class C + var source1 = +@" +namespace System.Runtime.CompilerServices +{ + public class IsUnmanagedAttribute : System.Attribute { } +} +"; + var comp1 = CreateCompilation(source1); + comp1.VerifyDiagnostics(); + + var comp1Ref = comp1.ToMetadataReference(); + + var source2 = +@" +#pragma warning disable CS8321 + +public class Test +{ + public void M() + { + void N() where T : unmanaged + { + } + } +} +"; + CompileWithUsedAssemblyReferences(source2, options: TestOptions.DebugModule, comp1Ref); + } + + [Fact] + public void Deconstruction_01() + { + var source0 = +@" +public class C0 +{ +} +"; + var comp0 = CreateCompilation(source0); + var comp0Ref = comp0.ToMetadataReference(); + + var source1 = +@" +public static class C1 +{ + public static void Deconstruct(this C0 x, out int y, out int z) => throw null; +} +"; + var comp1 = CreateCompilation(source1, references: new[] { comp0Ref }); + var comp1Ref = comp1.ToMetadataReference(); + + var source2 = +@" +public class C2 +{ + public static void Main() + { + var (y, z) = new C0(); + _ = y; + _ = z; + } +} +"; + + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp1Ref); + } + + [Fact] + public void Deconstruction_02() + { + var source0 = +@" +public class C0 { - event System.Action E { remove { } } //CS0065 +} +"; + var comp0 = CreateCompilation(source0); + var comp0Ref = comp0.ToMetadataReference(); + + var source1 = +@" +public static class C1 +{ + public static void Deconstruct(this C0 x, out int y, out int z) => throw null; +} +"; + var comp1 = CreateCompilation(source1, references: new[] { comp0Ref }); + var comp1Ref = comp1.ToMetadataReference(); + + var source2 = +@" +public class C2 +{ + public static void Main() + { + var (x, (y, z)) = (1, new C0()); + _ = x; + _ = y; + _ = z; + } +} +"; + + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp1Ref); + } + + [Fact] + public void ExternAliases_01() + { + var source0 = +@" +public class C0 +{ +} +"; + var comp0 = CreateCompilation(source0); + var comp0Ref = comp0.ToMetadataReference(); + var comp0RefWithAlias = comp0Ref.WithAliases(new[] { "Alias0" }); + + var source1 = +@" +public static class C1 +{ + public static C0 F1; +} +"; + var comp1 = CreateCompilation(source1, references: new[] { comp0Ref }); + var comp1Ref = comp1.ToMetadataReference(); + var comp1RefWithAlias = comp1Ref.WithAliases(new[] { "Alias0" }); + + var source2 = +@" +extern alias Alias0; + +public class C2 +{ + public static void Main() + { + var x = new Alias0::C0(); + _ = x; + } +} +"; + + var used = CompileWithUsedAssemblyReferences(source2, comp0RefWithAlias, comp1RefWithAlias); + Assert.DoesNotContain(comp1RefWithAlias, used); + + used = CompileWithUsedAssemblyReferences(source2, comp0RefWithAlias, comp1Ref); + Assert.DoesNotContain(comp1Ref, used); + + CreateCompilation(source2, references: new[] { comp0Ref, comp1RefWithAlias }).VerifyDiagnostics( + // (8,29): error CS0234: The type or namespace name 'C0' does not exist in the namespace 'Alias0' (are you missing an assembly reference?) + // var x = new Alias0::C0(); + Diagnostic(ErrorCode.ERR_DottedTypeNameNotFoundInNS, "C0").WithArguments("C0", "Alias0").WithLocation(8, 29) + ); + } + + [Fact] + public void ExternAliases_02() + { + var source0 = +@" +public class C0 +{ +} +"; + var comp0 = CreateCompilation(source0); + MetadataReference comp0Ref = comp0.ToMetadataReference(); + var comp0RefWithAlias = comp0Ref.WithAliases(new[] { "Alias0" }); + + var source2 = +@" +extern alias Alias0; + +public class C2 +{ + public static void Main() + { + var x = new Alias0::C0(); + _ = x; + } +} +"; + + CompileWithUsedAssemblyReferences(source2, comp0RefWithAlias, comp0Ref); + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp0RefWithAlias); + + comp0Ref = comp0.EmitToImageReference(); + comp0RefWithAlias = comp0Ref.WithAliases(new[] { "Alias0" }); + + CompileWithUsedAssemblyReferences(source2, comp0RefWithAlias, comp0Ref); + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp0RefWithAlias); + } + + [Fact] + public void ExternAliases_03() + { + var source0 = +@" +public class C0 +{ +} +"; + var comp0 = CreateCompilation(source0); + MetadataReference comp0Ref = comp0.ToMetadataReference(); + var comp0RefWithAlias = comp0Ref.WithAliases(new[] { "Alias0" }); - void Goo() + var source2 = +@" +public class C2 +{ + public static void Main() + { + var x = new global::C0(); + _ = x; + } +} +"; + + CompileWithUsedAssemblyReferences(source2, comp0RefWithAlias, comp0Ref); + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp0RefWithAlias); + + comp0Ref = comp0.EmitToImageReference(); + comp0RefWithAlias = comp0Ref.WithAliases(new[] { "Alias0" }); + + CompileWithUsedAssemblyReferences(source2, comp0RefWithAlias, comp0Ref); + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp0RefWithAlias); + } + + [Fact] + public void ExternAliases_04() + { + var source0 = +@" +public class C0 +{ +} +"; + var comp0 = CreateCompilation(source0); + MetadataReference comp0Ref = comp0.ToMetadataReference(); + var comp0RefWithAlias = comp0Ref.WithAliases(new[] { "Alias0" }); + + var source2 = +@" +public class C2 +{ + public static void Main() { - E += null; //no separate error + var x = new C0(); + _ = x; } } "; - CreateCompilation(text).VerifyEmitDiagnostics( - // (4,25): error CS0065: 'C.E': event property must have both add and remove accessors - // event System.Action E { remove { } } - Diagnostic(ErrorCode.ERR_EventNeedsBothAccessors, "E").WithArguments("C.E")).GetUsedAssemblyReferences(); + + CompileWithUsedAssemblyReferences(source2, comp0RefWithAlias, comp0Ref); + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp0RefWithAlias); + + comp0Ref = comp0.EmitToImageReference(); + comp0RefWithAlias = comp0Ref.WithAliases(new[] { "Alias0" }); + + CompileWithUsedAssemblyReferences(source2, comp0RefWithAlias, comp0Ref); + CompileWithUsedAssemblyReferences(source2, comp0Ref, comp0RefWithAlias); } } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/EventTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/EventTests.cs index 8fa874b64102b..0d047d43b8025 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/EventTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/EventTests.cs @@ -1441,10 +1441,15 @@ void Goo() } } "; - CreateCompilation(text).VerifyDiagnostics( + var expected = new[] { // (4,25): error CS0065: 'C.E': event property must have both add and remove accessors // event System.Action E { remove { } } - Diagnostic(ErrorCode.ERR_EventNeedsBothAccessors, "E").WithArguments("C.E")); + Diagnostic(ErrorCode.ERR_EventNeedsBothAccessors, "E").WithArguments("C.E") + }; + + CreateCompilation(text).VerifyDiagnostics(expected).VerifyEmitDiagnostics(expected); + + CreateCompilation(text).VerifyEmitDiagnostics(expected); } [WorkItem(542570, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/542570")] diff --git a/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.Resolution.cs b/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.Resolution.cs index b1dbaf98d2e55..2c0c6a617513b 100644 --- a/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.Resolution.cs +++ b/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.Resolution.cs @@ -58,6 +58,7 @@ protected struct ResolvedReference private readonly int _index; private readonly ImmutableArray _aliasesOpt; private readonly ImmutableArray _recursiveAliasesOpt; + private readonly ImmutableArray _mergedReferencesOpt; // uninitialized aliases public ResolvedReference(int index, MetadataImageKind kind) @@ -67,20 +68,23 @@ public ResolvedReference(int index, MetadataImageKind kind) _kind = kind; _aliasesOpt = default(ImmutableArray); _recursiveAliasesOpt = default(ImmutableArray); + _mergedReferencesOpt = default(ImmutableArray); } // initialized aliases - public ResolvedReference(int index, MetadataImageKind kind, ImmutableArray aliasesOpt, ImmutableArray recursiveAliasesOpt) + public ResolvedReference(int index, MetadataImageKind kind, ImmutableArray aliasesOpt, ImmutableArray recursiveAliasesOpt, ImmutableArray mergedReferences) : this(index, kind) { // We have to have non-default aliases (empty are ok). We can have both recursive and non-recursive aliases if two references were merged. Debug.Assert(!aliasesOpt.IsDefault || !recursiveAliasesOpt.IsDefault); + Debug.Assert(!mergedReferences.IsDefault); _aliasesOpt = aliasesOpt; _recursiveAliasesOpt = recursiveAliasesOpt; + _mergedReferencesOpt = mergedReferences; } - private bool IsUninitialized => _aliasesOpt.IsDefault && _recursiveAliasesOpt.IsDefault; + private bool IsUninitialized => (_aliasesOpt.IsDefault && _recursiveAliasesOpt.IsDefault) || _mergedReferencesOpt.IsDefault; /// /// Aliases that should be applied to the referenced assembly. @@ -110,6 +114,15 @@ public ImmutableArray RecursiveAliasesOpt } } + public ImmutableArray MergedReferences + { + get + { + Debug.Assert(!IsUninitialized); + return _mergedReferencesOpt; + } + } + /// /// default() is considered skipped. /// @@ -425,12 +438,18 @@ protected ImmutableArray ResolveMetadataReferences( private static ResolvedReference GetResolvedReferenceAndFreePropertyMapEntry(MetadataReference reference, int index, MetadataImageKind kind, Dictionary propertyMapOpt) { ImmutableArray aliasesOpt, recursiveAliasesOpt; + var mergedReferences = ImmutableArray.Empty; MergedAliases mergedProperties; if (propertyMapOpt != null && propertyMapOpt.TryGetValue(reference, out mergedProperties)) { aliasesOpt = mergedProperties.AliasesOpt?.ToImmutableAndFree() ?? default(ImmutableArray); recursiveAliasesOpt = mergedProperties.RecursiveAliasesOpt?.ToImmutableAndFree() ?? default(ImmutableArray); + + if (mergedProperties.MergedReferencesOpt is object) + { + mergedReferences = mergedProperties.MergedReferencesOpt.ToImmutableAndFree(); + } } else if (reference.Properties.HasRecursiveAliases) { @@ -443,7 +462,7 @@ private static ResolvedReference GetResolvedReferenceAndFreePropertyMapEntry(Met recursiveAliasesOpt = default(ImmutableArray); } - return new ResolvedReference(index, kind, aliasesOpt, recursiveAliasesOpt); + return new ResolvedReference(index, kind, aliasesOpt, recursiveAliasesOpt, mergedReferences); } /// diff --git a/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.State.cs b/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.State.cs index 1a0026b06cf05..fc24cd346b7e6 100644 --- a/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.State.cs +++ b/src/Compilers/Core/Portable/ReferenceManager/CommonReferenceManager.State.cs @@ -170,13 +170,20 @@ internal partial class CommonReferenceManager : C private ImmutableArray _lazyReferencedAssemblies; /// - /// Assemblies referenced directly by the source module of the compilation. + /// Aliases used by assemblies referenced directly by the source module of the compilation. /// /// /// Aliases [i] are of an assembly [i]. /// private ImmutableArray> _lazyAliasesOfReferencedAssemblies; + /// + /// A map capturing s that were "merged" to a single referenced assembly + /// associated with a key in the map. + /// The keys are a subset of keys from . + /// + private ImmutableDictionary> _lazyMergedAssemblyReferencesMap; + /// /// Unified assemblies referenced directly by the source module of the compilation. /// @@ -311,6 +318,15 @@ internal ImmutableArray> AliasesOfReferencedAssemblies } } + internal ImmutableDictionary> MergedAssemblyReferencesMap + { + get + { + AssertBound(); + return _lazyMergedAssemblyReferencesMap; + } + } + internal ImmutableArray> UnifiedAssemblies { get @@ -340,6 +356,7 @@ internal void AssertUnbound() Debug.Assert(_lazyReferencedModulesReferences.IsDefault); Debug.Assert(_lazyReferencedAssemblies.IsDefault); Debug.Assert(_lazyAliasesOfReferencedAssemblies.IsDefault); + Debug.Assert(_lazyMergedAssemblyReferencesMap == null); Debug.Assert(_lazyUnifiedAssemblies.IsDefault); Debug.Assert(_lazyCorLibraryOpt == null); } @@ -359,6 +376,7 @@ internal void AssertBound() Debug.Assert(!_lazyReferencedModulesReferences.IsDefault); Debug.Assert(!_lazyReferencedAssemblies.IsDefault); Debug.Assert(!_lazyAliasesOfReferencedAssemblies.IsDefault); + Debug.Assert(_lazyMergedAssemblyReferencesMap != null); Debug.Assert(!_lazyUnifiedAssemblies.IsDefault); // lazyCorLibrary is null if the compilation is corlib @@ -396,7 +414,8 @@ internal void InitializeNoLock( ImmutableArray> referencedModulesReferences, ImmutableArray referencedAssemblies, ImmutableArray> aliasesOfReferencedAssemblies, - ImmutableArray> unifiedAssemblies) + ImmutableArray> unifiedAssemblies, + Dictionary> mergedAssemblyReferencesMapOpt) { AssertUnbound(); @@ -417,6 +436,7 @@ internal void InitializeNoLock( _lazyReferencedModulesReferences = referencedModulesReferences; _lazyReferencedAssemblies = referencedAssemblies; _lazyAliasesOfReferencedAssemblies = aliasesOfReferencedAssemblies; + _lazyMergedAssemblyReferencesMap = mergedAssemblyReferencesMapOpt?.ToImmutableDictionary() ?? ImmutableDictionary>.Empty; _lazyUnifiedAssemblies = unifiedAssemblies; _lazyHasCircularReference = containsCircularReferences.ToThreeState(); @@ -441,13 +461,16 @@ protected static void BuildReferencedAssembliesAndModulesMaps( bool supersedeLowerVersions, out Dictionary referencedAssembliesMap, out Dictionary referencedModulesMap, - out ImmutableArray> aliasesOfReferencedAssemblies) + out ImmutableArray> aliasesOfReferencedAssemblies, + out Dictionary> mergedAssemblyReferencesMapOpt) { referencedAssembliesMap = new Dictionary(referenceMap.Length); referencedModulesMap = new Dictionary(referencedModuleCount); var aliasesOfReferencedAssembliesBuilder = ArrayBuilder>.GetInstance(referenceMap.Length - referencedModuleCount); bool hasRecursiveAliases = false; + mergedAssemblyReferencesMapOpt = null; + for (int i = 0; i < referenceMap.Length; i++) { if (referenceMap[i].IsSkipped) @@ -467,9 +490,15 @@ protected static void BuildReferencedAssembliesAndModulesMaps( int assemblyIndex = referenceMap[i].Index; Debug.Assert(aliasesOfReferencedAssembliesBuilder.Count == assemblyIndex); - referencedAssembliesMap.Add(references[i], assemblyIndex); + MetadataReference reference = references[i]; + referencedAssembliesMap.Add(reference, assemblyIndex); aliasesOfReferencedAssembliesBuilder.Add(referenceMap[i].AliasesOpt); + if (!referenceMap[i].MergedReferences.IsEmpty) + { + (mergedAssemblyReferencesMapOpt ??= new Dictionary>()).Add(reference, referenceMap[i].MergedReferences); + } + hasRecursiveAliases |= !referenceMap[i].RecursiveAliasesOpt.IsDefault; } } diff --git a/src/Compilers/Core/Portable/ReferenceManager/MergedAliases.cs b/src/Compilers/Core/Portable/ReferenceManager/MergedAliases.cs index 75a35d3ac2b26..c73f3cf0c044f 100644 --- a/src/Compilers/Core/Portable/ReferenceManager/MergedAliases.cs +++ b/src/Compilers/Core/Portable/ReferenceManager/MergedAliases.cs @@ -10,6 +10,7 @@ internal sealed class MergedAliases { public ArrayBuilder AliasesOpt; public ArrayBuilder RecursiveAliasesOpt; + public ArrayBuilder MergedReferencesOpt; /// /// Adds aliases of a specified reference to the merged set of aliases. @@ -52,6 +53,8 @@ internal void Merge(MetadataReference reference) Merge( aliases: reference.Properties.HasRecursiveAliases ? RecursiveAliasesOpt : AliasesOpt, newAliases: reference.Properties.Aliases); + + (MergedReferencesOpt ??= ArrayBuilder.GetInstance()).Add(reference); } internal static void Merge(ArrayBuilder aliases, ImmutableArray newAliases) diff --git a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs index bde71e2d77383..ad41dd65053c7 100644 --- a/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/CSharpTestBase.cs @@ -1063,20 +1063,31 @@ private static void VerifyUsedAssemblyReferences(Func createC var compileDiagnostics = comp.GetDiagnostics(); var emitDiagnostics = comp.GetEmitDiagnostics(); + var resolvedReferences = comp.References.Where(r => r.Properties.Kind == MetadataImageKind.Assembly); + if (!compileDiagnostics.Any(d => d.DefaultSeverity == DiagnosticSeverity.Error) && - comp.References.Where(r => r.Properties.Kind == MetadataImageKind.Assembly).Count() > used.Length) + !resolvedReferences.Any(r => r.Properties.HasRecursiveAliases)) { - if (!compileDiagnostics.Any(d => d.Code == (int)ErrorCode.HDN_UnusedExternAlias || d.Code == (int)ErrorCode.HDN_UnusedUsingDirective)) + if (resolvedReferences.Count() > used.Length) + { + AssertSubset(used, resolvedReferences); + + if (!compileDiagnostics.Any(d => d.Code == (int)ErrorCode.HDN_UnusedExternAlias || d.Code == (int)ErrorCode.HDN_UnusedUsingDirective)) + { + var comp2 = comp.RemoveAllReferences().AddReferences(used.Concat(comp.References.Where(r => r.Properties.Kind == MetadataImageKind.Module))); + comp2.GetEmitDiagnostics().Where(d => shouldCompare(d)).Verify( + emitDiagnostics.Where(d => shouldCompare(d)). + Select(d => new DiagnosticDescription(d, errorCodeOnly: false, includeDefaultSeverity: false, includeEffectiveSeverity: false)).ToArray()); + } + } + else { - var comp2 = comp.RemoveAllReferences().AddReferences(used.Concat(comp.References.Where(r => r.Properties.Kind == MetadataImageKind.Module))); - comp2.GetEmitDiagnostics().Where(d => shouldCompare(d)).Verify( - emitDiagnostics.Where(d => shouldCompare(d)). - Select(d => new DiagnosticDescription(d, errorCodeOnly: false, includeDefaultSeverity: false, includeEffectiveSeverity: false)).ToArray()); + AssertEx.Equal(resolvedReferences, used); } } else { - AssertEx.Equal(comp.References.Where(r => r.Properties.Kind == MetadataImageKind.Assembly && comp.GetAssemblyOrModuleSymbol(r) is object), used); + AssertSubset(used, resolvedReferences); } static bool shouldCompare(Diagnostic d) @@ -1087,6 +1098,14 @@ static bool shouldCompare(Diagnostic d) d.Code != (int)ErrorCode.WRN_MultiplePredefTypes && d.Code != (int)ErrorCode.WRN_SameFullNameThisAggNs; } + + static void AssertSubset(ImmutableArray used, IEnumerable resolvedReferences) + { + foreach (var reference in used) + { + Assert.Contains(reference, resolvedReferences); + } + } } #endif diff --git a/src/Compilers/VisualBasic/Portable/Symbols/ReferenceManager.vb b/src/Compilers/VisualBasic/Portable/Symbols/ReferenceManager.vb index 18448444288fe..136076dcf4ca4 100644 --- a/src/Compilers/VisualBasic/Portable/Symbols/ReferenceManager.vb +++ b/src/Compilers/VisualBasic/Portable/Symbols/ReferenceManager.vb @@ -351,6 +351,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim referencedAssembliesMap As Dictionary(Of MetadataReference, Integer) = Nothing Dim referencedModulesMap As Dictionary(Of MetadataReference, Integer) = Nothing Dim aliasesOfReferencedAssemblies As ImmutableArray(Of ImmutableArray(Of String)) = Nothing + Dim mergedAssemblyReferencesMapOpt As Dictionary(Of MetadataReference, ImmutableArray(Of MetadataReference)) = Nothing BuildReferencedAssembliesAndModulesMaps( bindingResult, @@ -362,7 +363,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic supersedeLowerVersions, referencedAssembliesMap, referencedModulesMap, - aliasesOfReferencedAssemblies) + aliasesOfReferencedAssemblies, + mergedAssemblyReferencesMapOpt) ' Create AssemblySymbols for assemblies that can't use any existing symbols. Dim newSymbols As New List(Of Integer) @@ -443,7 +445,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic moduleReferences, assemblySymbol.SourceModule.GetReferencedAssemblySymbols(), aliasesOfReferencedAssemblies, - assemblySymbol.SourceModule.GetUnifiedAssemblies()) + assemblySymbol.SourceModule.GetUnifiedAssemblies(), + mergedAssemblyReferencesMapOpt) ' Make sure that the given compilation holds on this instance of reference manager. Debug.Assert(compilation._referenceManager Is Me OrElse hasCircularReference)