diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/AddType.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/AddType.cs index bbd536aada..4e6fc67643 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/AddType.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/AddType.cs @@ -616,7 +616,7 @@ namespace Microsoft.PowerShell.Commands // These dictionaries prevent reloading already loaded and unchanged code. // We don't worry about unbounded growing of the cache because in .Net Core 2.0 we can not unload assemblies. // TODO: review if we will be able to unload assemblies after migrating to .Net Core 2.1. - private static readonly Dictionary s_sourceTypesCache = new Dictionary(); + private static readonly HashSet s_sourceTypesCache = new HashSet(); private static readonly Dictionary s_sourceAssemblyCache = new Dictionary(); private static readonly string s_defaultSdkDirectory = Utils.DefaultPowerShellAppBase; @@ -1042,7 +1042,7 @@ namespace Microsoft.PowerShell.Commands private void CheckDuplicateTypes(Compilation compilation, out ConcurrentBag newTypes) { - AllNamedTypeSymbolsVisitor visitor = new AllNamedTypeSymbolsVisitor(_syntaxTreesHash); + AllNamedTypeSymbolsVisitor visitor = new AllNamedTypeSymbolsVisitor(); visitor.Visit(compilation.Assembly.GlobalNamespace); foreach (var symbolName in visitor.DuplicateSymbols) @@ -1074,16 +1074,9 @@ namespace Microsoft.PowerShell.Commands // Visit symbols in all namespaces and collect duplicates. private class AllNamedTypeSymbolsVisitor : SymbolVisitor { - private readonly int _hash; - public readonly ConcurrentBag DuplicateSymbols = new ConcurrentBag(); public readonly ConcurrentBag UniqueSymbols = new ConcurrentBag(); - public AllNamedTypeSymbolsVisitor(int hash) - { - _hash = hash; - } - public override void VisitNamespace(INamespaceSymbol symbol) { // Main cycle. @@ -1099,12 +1092,9 @@ namespace Microsoft.PowerShell.Commands // It is namespace-fully-qualified name var symbolFullName = symbol.ToString(); - if (s_sourceTypesCache.TryGetValue(symbolFullName, out int hash)) + if (s_sourceTypesCache.TryGetValue(symbolFullName, out _)) { - if (hash == _hash) - { - DuplicateSymbols.Add(symbolFullName); - } + DuplicateSymbols.Add(symbolFullName); } else { @@ -1117,7 +1107,7 @@ namespace Microsoft.PowerShell.Commands { foreach (var typeName in newTypes) { - s_sourceTypesCache.Add(typeName, _syntaxTreesHash); + s_sourceTypesCache.Add(typeName); } } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Add-Type.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Add-Type.Tests.ps1 index 20ed4b0afa..6d0fadbc69 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/Add-Type.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Add-Type.Tests.ps1 @@ -1,5 +1,6 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. + Describe "Add-Type" -Tags "CI" { BeforeAll { $guid = [Guid]::NewGuid().ToString().Replace("-","") @@ -65,7 +66,7 @@ Describe "Add-Type" -Tags "CI" { $code = @" using System.Management.Automation; [System.Management.Automation.Cmdlet("Get", "Thing$guid", ConfirmImpact = System.Management.Automation.ConfirmImpact.High, SupportsPaging = true)] -public class AttributeTest$guid : PSCmdlet +public class SMAAttributeTest$guid : PSCmdlet { protected override void EndProcessing() @@ -207,6 +208,15 @@ public class AttributeTest$guid : PSCmdlet { Add-Type -CompilerOptions "/platform:anycpuERROR" -Language CSharp -MemberDefinition "public static string TestString() { return ""}" -Name "TestType1" -Namespace "TestNS" -ErrorAction Stop } | Should -Throw -ErrorId "SOURCE_CODE_ERROR,Microsoft.PowerShell.Commands.AddTypeCommand" } + It "Throw if the type already exists" { + Add-Type -TypeDefinition "public class Foo$guid {}" + + # The cmdlet writes TYPE_ALREADY_EXISTS for every duplicated type and then terminates with COMPILER_ERRORS. + # So here we check 2 errors. + { Add-Type -TypeDefinition "public class Foo$guid { public int Bar {get {return 42;} }" -ErrorAction SilentlyContinue } | Should -Throw -ErrorId "COMPILER_ERRORS,Microsoft.PowerShell.Commands.AddTypeCommand" + $error[1].FullyQualifiedErrorId | Should -BeExactly "TYPE_ALREADY_EXISTS,Microsoft.PowerShell.Commands.AddTypeCommand" + } + It "OutputType parameter requires that the OutputAssembly parameter be specified." { $code = "public static string TestString() {}" { Add-Type -TypeDefinition $code -OutputType Library } | Should -Throw -ErrorId "OUTPUTTYPE_REQUIRES_ASSEMBLY,Microsoft.PowerShell.Commands.AddTypeCommand"