dotnet / docfx

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

[Bug] Custom namespace filter rules not working anymore #8427

Open Krusty93 opened 1 year ago

Krusty93 commented 1 year ago

Describe the bug I use DocFx to document a specific namespace in my dotnet project.

To achieve my goal I have a filterConfig.yml file where I exclude everything but what I need:

apiRules:
- exclude:
      hasAttribute:
          uid: Newtonsoft.Json.JsonIgnoreAttribute
- exclude:
      uid: .*
      type: Method
- exclude:
      uid: .*
      type: Field
- exclude:
      uid: .*
      type: Interface
- include:
      uidRegex: ^MyApp\.Domain\.Models
- exclude:
      uidRegex: ^MyApp\.Domain

But unfortunately, the produced documentation is empty because the last exclude rule is removing everything.

I cloned the source code to attach the debugger and I saw that the class SymbolFilter (method IncludeApi) is correctly validating my rules, but then it checks for the ContainingSymbol as well. And being the Models a child of the Domain namespace, this obviously breaks my rules invalidating the include one.

This didn't happen with the v2.61.0 and according to the documentation this approach shall work.

To Reproduce Create a dotnet console app MyApp and two classes in two different namespaces:

  1. MyApp.Domain.Models.Class1
  2. MyApp.Domain.Class2

Run docfx init --quiet

Fill the metadata property of the docfx.json file as follow:

  "metadata": [
    {
      "src": [
        {
          "files": [
            "**.csproj"
          ],
          "exclude": [
            "**/bin/**",
            "**/obj/**",
            "_site/**",
          ],
          "src": "../"
        }
      ],
      "dest": "api",
      "filter": "filterConfig.yml",
      "includePrivateMembers": false,
      "disableGitFeatures": false,
      "disableDefaultFilter": false
    }
  ],

And create in the same folder a filterConfig.yml file:

apiRules:
- include:
      uidRegex: ^ConsoleApp1\.Domain\.Models
- exclude:
      uidRegex: ^ConsoleApp1\.Domain

Run the serve command. Generated documentation will be empty

Expected behavior Generated documentation shall contain documentation for the ConsoleApp1.Domain.Models namespace (Class1).

To have a test, perform the same steps as above but with the 2.61.0 version installed.

Context:

{
  "metadata": [
    {
      "src": [
        {
          "files": [
            "**/ConsoleApp1.csproj"
          ],
          "exclude": [
            "**/bin/**",
            "**/obj/**",
            "_site/**"
          ],
          "src": "../"
        }
      ],
      "dest": "api",
      "filter": "filterConfig.yml",
      "includePrivateMembers": false,
      "disableGitFeatures": false,
      "disableDefaultFilter": false
    }
  ],
  "build": {
    "content": [
      {
        "files": [
          "api/**.yml",
          "api/index.md"
        ]
      },
      {
        "files": [
          "articles/**.md",
          "articles/**/toc.yml",
          "toc.yml",
          "*.md"
        ]
      }
    ],
    "resource": [
      {
        "files": [
          "images/**"
        ]
      }
    ],
    "overwrite": [
      {
        "files": [
          "apidoc/**.md"
        ],
        "exclude": [
          "obj/**",
          "_site/**"
        ]
      }
    ],
    "globalMetadata": {
      "_appTitle": "",
      "_appFooter": "",
      "_gitUrlPattern": "vso",
      "_enableNewTab": true,
      "_disableContribution": true
    },
    "dest": "_site",
    "globalMetadataFiles": [],
    "fileMetadataFiles": [],
    "template": [
      "default"
    ],
    "postProcessors": [],
    "noLangKeyword": false,
    "keepFileLink": false,
    "disableGitFeatures": false
  }
}
apiRules:
- exclude:
      hasAttribute:
          uid: Newtonsoft.Json.JsonIgnoreAttribute
- exclude:
      uid: .*
      type: Method
- exclude:
      uid: .*
      type: Field
- exclude:
      uid: .*
      type: Interface
- include:
      uidRegex: ^ConsoleApp1\.Domain\.Models
- exclude:
      uidRegex: ^ConsoleApp1\.Domain
.NET SDK:
 Version:   7.0.103
 Commit:    276c71d299

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.22000
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\7.0.103\

Host:
  Version:      7.0.3
  Architecture: x64
  Commit:       0a2bda10e8

.NET SDKs installed:
  3.1.426 [C:\Program Files\dotnet\sdk]
  5.0.302 [C:\Program Files\dotnet\sdk]
  5.0.403 [C:\Program Files\dotnet\sdk]
  5.0.408 [C:\Program Files\dotnet\sdk]
  6.0.202 [C:\Program Files\dotnet\sdk]
  6.0.203 [C:\Program Files\dotnet\sdk]
  6.0.309 [C:\Program Files\dotnet\sdk]
  7.0.103 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 3.1.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.21 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.10 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 6.0.14 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.AspNetCore.App 7.0.3 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 3.1.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.21 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.10 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 6.0.14 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.NETCore.App 7.0.3 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 3.1.17 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.21 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 3.1.32 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.8 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.9 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.10 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.12 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 5.0.17 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.4 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 6.0.14 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
  Microsoft.WindowsDesktop.App 7.0.3 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Other architectures found:
  arm64 [C:\Program Files\dotnet]
    registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\arm64\InstallLocation]
  x86   [C:\Program Files (x86)\dotnet]
    registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]

Environment variables:
  Not set

global.json file:
  Not found

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

Download .NET:
  https://aka.ms/dotnet/download

Additional context Add any other context about the problem here.

OscarAbraham commented 1 year ago

I think they changed how namespace filters work so one can exclude all child namespaces with a single rule. My guess is that if you change the order of your filter rules and put the rule to include ConsoleApp1.Domain.Models, it should work.

Krusty93 commented 1 year ago

@OscarAbraham thanks for feedback. However, I tried reversing the apiRules as you suggested , but it still doesn't work

apiRules:
- exclude:
      uidRegex: ^ConsoleApp1\.Domain
- include:
      uidRegex: ^ConsoleApp1\.Domain\.Models
OscarAbraham commented 1 year ago

@Krusty93 Sorry. I don't know why I thought it'd work.

Krusty93 commented 1 year ago

@Krusty93 Sorry. I don't know why I thought it'd work.

No problem

In my opinion, this is the line that introduced the bug in PR #8408:

public bool IncludeApi(ISymbol symbol)
{
    return IsSymbolAccessible(symbol) && IncludeApiCore(symbol);

    bool IncludeApiCore(ISymbol symbol)
    {
        return _cache.GetOrAdd(symbol, _ => _options.IncludeApi?.Invoke(_) switch
        {
            SymbolIncludeState.Include => true,
            SymbolIncludeState.Exclude => false,
            _ => IncludeApiDefault(symbol),
        });
    }

    bool IncludeApiDefault(ISymbol symbol)
    {
        if (_filterRule is not null && !_filterRule.CanVisitApi(RoslynFilterData.GetSymbolFilterData(symbol)))
            return false;

        return symbol.ContainingSymbol is null || IncludeApiCore(symbol.ContainingSymbol);
    }
}

CanVisitApi returns true because the apiRules has an include rule on the ConsoleApp1.Domain.Models folder; however, the method is called again recursively passing the ContainingSymbol as argumen that has the value of ConsoleApp1.Domain. This value is not accepted, so it returns false. For this reason, the flow ends up excluding the "more specific" namespace, being in contrast with what the documentation says.

Lagann311 commented 1 year ago

I'm encountering the same problem, the include doesn't seem to work at all.

osre77 commented 12 months ago

I just ran into the same issue with DocFx V2.71.0 My use case is that I only want to document things in a specific Namespace. In order to do so I have to 1st exclude everything, and then include the namespace I want (at least I understand the documentation like that). This is what I came up with:

apiRules:
- include:
    uidRegex: ^Company\.Product\.Component\.
    #type: Namespace
- exclude:
    uidRegex: .*
    #type: Namespace

I tried it with the type: Namespace and without, I changed the order, ... But I get everything unfiltered (I I reverse the order), or I get nothing with the warning: No .NET API detected for...

filzrev commented 6 months ago

This issue is expected to be fixed in latest version of docfx. (By #9666)