dotnet / docfx

Static site generator for .NET API documentation.
https://dotnet.github.io/docfx/
MIT License
3.95k stars 841 forks source link

[Bug] .NET API bug: duplicate key in GenerateNestedTocStructure #9507

Open sliekens opened 7 months ago

sliekens commented 7 months ago

Describe the bug My docfx build started failing because of a duplicate dictionary key error when generating a nested ToC structure. This started happening after I updated PolySharp in my project to 1.14.0.

To Reproduce

git clone https://github.com/sliekens/gw2sdk.git
cd gw2sdk
git reset --hard c62c6f5167b6dbee83dbc73b1bf6d1a414fa945d
git clean -df
dotnet tool restore
dotnet docfx

Expected behavior No crash

Context (please complete the following information):

{
  "metadata": [
    {
      "src": [
        {
          "src": "GW2SDK",
          "files": [
            "GW2SDK.csproj"
          ]
        }
      ],
      "dest": "docs/api",
      "outputFormat": "mref",
      "includePrivateMembers": false,
      "disableGitFeatures": false,
      "disableDefaultFilter": false,
      "noRestore": false,
      "namespaceLayout": "nested",
      "memberLayout": "separatePages",
      "enumSortOrder": "declaringOrder",
      "allowCompilationErrors": false
    }
  ],
  "build": {
    "content": [
      {
        "src": "docs",
        "files": [
          "index.md",
          "toc.yml"
        ]
      },
      {
        "src": "docs/guide",
        "dest": "guide",
        "files": [
          "**.md",
          "**/toc.yml"
        ]
      },
      {
        "src": "docs/api",
        "dest": "api",
        "files": [
          "**.yml",
          "index.md"
        ]
      }
    ],
    "resource": [
      {
        "src": "docs",
        "files": [
          "images/**"
        ]
      }
    ],
    "output": "artifacts",
    "fileMetadataFiles": [],
    "postProcessors": [],
    "keepFileLink": false,
    "disableGitFeatures": false,
    "template": [
      "default",
      "docs/templates/singulinkfx"
    ],
    "globalMetadata": {
      "_appTitle": "GW2SDK",
      "_appName": "GW2SDK",
      "_appLogoPath": "images/logo-50x50.png",
      "_appFaviconPath": "images/logo-50x50.png",
      "_appFooter": "Built with docfx, theme created by Singulink",
      "_copyrightFooter": "© Steven Liekens. All rights reserved.",
      "_enableSearch": true,
      "_enableNewTab": true
    }
  }
}
Creating output...
ArgumentException: An item with the same key has already been added. Key: System
  at bool TryInsert(TKey key, TValue value, InsertionBehavior behavior)                                                                                                                                     
  at void Add(TKey key, TValue value)                                                                                                                                                                       
  at MetadataItem GenerateNestedTocStructure(IEnumerable<KeyValuePair<string, MetadataItem>> namespaces, Dictionary<string, ReferenceItem> allReferences) in YamlMetadataResolver.cs:105                    
  at MetadataItem GenerateNestedToc(IEnumerable<KeyValuePair<string, MetadataItem>> namespaces, Dictionary<string, ReferenceItem> allReferences) in YamlMetadataResolver.cs:162                             
  at MetadataItem GenerateToc(Dictionary<string, MetadataItem> allMembers, Dictionary<string, ReferenceItem> allReferences, NamespaceLayout namespaceLayout) in YamlMetadataResolver.cs:56                  
  at MetadataModel ResolveMetadata(Dictionary<string, MetadataItem> allMembers, Dictionary<string, ReferenceItem> allReferences, NamespaceLayout namespaceLayout) in YamlMetadataResolver.cs:33             
  at void <CreateManagedReference>g__ResolveAndExportYamlMetadata|2(Dictionary<string, MetadataItem> allMembers, Dictionary<string, ReferenceItem> allReferences) in DotnetApiCatalog.ManagedReference.cs:51
  at void CreateManagedReference((ValueTuple<IAssemblySymbol, Compilation> symbol) assemblies, ExtractMetadataConfig config, DotnetApiOptions options) in DotnetApiCatalog.ManagedReference.cs:45           
  at async Task <Exec>g__Build|4_0(ExtractMetadataConfig config, DotnetApiOptions options) in DotnetApiCatalog.cs:123                                                                                       
  at async Task Exec(MetadataJsonConfig config, DotnetApiOptions options, string configDirectory, string outputDirectory) in DotnetApiCatalog.cs:75                                                         
  at void <Execute>b__0() in DefaultCommand.cs:38                                                                                                                                                           
  at int Run(LogOptions options, Action run) in CommandHelper.cs:44                                                                                                                                         
  at int Execute(CommandContext context, Options options) in DefaultCommand.cs:30                                                                                                                           
  at Task<int> Execute(CommandContext context, CommandSettings settings) in CommandOfT.cs:40                                                                                                                
  at Task<int> Execute(CommandTree leaf, CommandTree tree, CommandContext context, ITypeResolver resolver, IConfiguration configuration) in CommandExecutor.cs:144                                          
  at async Task<int> Execute(IConfiguration configuration, IEnumerable<string> args) in CommandExecutor.cs:83                                                                                               
  at async Task<int> RunAsync(IEnumerable<string> args) in CommandApp.cs:84  
.NET SDK:
 Version:           8.0.100
 Commit:            57efcf1350
 Workload version:  8.0.100-manifests.6c33ef20

Runtime Environment:
 OS Name:     debian
 OS Version:  12
 OS Platform: Linux
 RID:         linux-x64
 Base Path:   /usr/share/dotnet/sdk/8.0.100/

.NET workloads installed:
 Workload version: 8.0.100-manifests.6c33ef20
There are no installed workloads to display.

Host:
  Version:      8.0.0
  Architecture: x64
  Commit:       5535e31a71

.NET SDKs installed:
  8.0.100 [/usr/share/dotnet/sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 8.0.0 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 7.0.14 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
  Microsoft.NETCore.App 8.0.0 [/usr/share/dotnet/shared/Microsoft.NETCore.App]

Other architectures found:
  None

Environment variables:
  DOTNET_ROOT       [/usr/share/dotnet]

global.json file:
  /workspaces/gw2sdk/global.json

Learn more:
  https://aka.ms/dotnet/info

Download .NET:
  https://aka.ms/dotnet/download
sliekens commented 7 months ago

A few minutes of debugging tells me that the problem occurs because I have a (generated) type in the System namespace, which causes a conflict in GenerateNestedTocStructure because allReferences already contains "System", so there is a duplicate key error when trying to add the namespace to namespacedItems and again to allReferences.

xakep139 commented 2 months ago

We have a similar problem, but the namespace-specific:

ArgumentException: An item with the same key has already been added. Key: <...>.HttpClient
  at bool TryInsert(TKey key, TValue value, InsertionBehavior behavior)
  at void Add(TKey key, TValue value)
  at MetadataItem GenerateNestedTocStructure(IEnumerable<KeyValuePair<string, MetadataItem>> namespaces, Dictionary<string, ReferenceItem> allReferences) in YamlMetadataResolver.cs:105
  at MetadataItem GenerateNestedToc(IEnumerable<KeyValuePair<string, MetadataItem>> namespaces, Dictionary<string, ReferenceItem> allReferences) in YamlMetadataResolver.cs:162
  at MetadataItem GenerateToc(Dictionary<string, MetadataItem> allMembers, Dictionary<string, ReferenceItem> allReferences, NamespaceLayout namespaceLayout) in YamlMetadataResolver.cs:56
  at MetadataModel ResolveMetadata(Dictionary<string, MetadataItem> allMembers, Dictionary<string, ReferenceItem> allReferences, NamespaceLayout namespaceLayout) in YamlMetadataResolver.cs:33
  at void <CreateManagedReference>g__ResolveAndExportYamlMetadata|2(Dictionary<string, MetadataItem> allMembers, Dictionary<string, ReferenceItem> allReferences) in DotnetApiCatalog.ManagedReference.cs: 
     51
  at void CreateManagedReference((ValueTuple<IAssemblySymbol, Compilation> symbol) assemblies, ExtractMetadataConfig config, DotnetApiOptions options) in DotnetApiCatalog.ManagedReference.cs:45
  at async Task <Exec>g__Build|5_0(ExtractMetadataConfig config, DotnetApiOptions options) in DotnetApiCatalog.cs:120
  at async Task Exec(MetadataJsonConfig config, DotnetApiOptions options, string configDirectory, string outputDirectory) in DotnetApiCatalog.cs:72
  at void <Execute>b__0() in MetadataCommand.cs:18
  at int Run(LogOptions options, Action run) in CommandHelper.cs:48
  at int Execute(CommandContext context, MetadataCommandOptions options) in MetadataCommand.cs:14
  at Task<int> Execute(CommandContext context, CommandSettings settings) in CommandOfT.cs:40
  at Task<int> Execute(CommandTree leaf, CommandTree tree, CommandContext context, ITypeResolver resolver, IConfiguration configuration) in CommandExecutor.cs:144
  at async Task<int> Execute(IConfiguration configuration, IEnumerable<string> args) in CommandExecutor.cs:83
  at async Task<int> RunAsync(IEnumerable<string> args) in CommandApp.cs:84

I replaced the actual namespace with <...>. In our case we have a couple of assemblies that offer different extensions for HttpClient, but share the same namespace. With "namespaceLayout": "flattened" we don't have any issues.

trb5016 commented 1 month ago

A few minutes of debugging tells me that the problem occurs because I have a (generated) type in the System namespace, which causes a conflict in GenerateNestedTocStructure because allReferences already contains "System", so there is a duplicate key error when trying to add the namespace to namespacedItems and again to allReferences.

Just chiming in to say that I have pretty much the same issue - an extension method in the System namespace residing in my own assembly prompts the same error. (Whether I should be doing that or not is a separate question...)

filzrev commented 3 weeks ago

It seems original reported issue can be resolved by updating PolySharp to PolySharp 1.14.1

If there are another issue that relating to namespace and "namespaceLayout": "nested". Please provide minimal reproduceable project files.