internal class AzureRoutingAttributesSearchVisitor()

in PluginsAndFeatures/azure-toolkit-for-rider/src/dotnet/ReSharper.Azure/Azure.Psi/Endpoints/Attributes/AzureRoutingAttributesSearchVisitor.cs [19:118]


internal class AzureRoutingAttributesSearchVisitor(
  Dictionary<IDeclaredElement, IAzureAttributeRoutingSource> attributeClassToRoutingSourceMap,
  ISearchDomain searchDomain)
  : SearchDomainVisitorParallel(searchDomain, NullProgressIndicator.Create())
{
  private readonly HashSet<string> _possibleNames = GetPossibleNames(attributeClassToRoutingSourceMap);
  private readonly SynchronizedList<IAzureRoutingAttribute> _result = [];

  public IAzureRoutingAttribute[] GetResult()
  {
    return _result.AsEnumerable().ToArray();
  }

  protected override bool ProcessSourceFile(IPsiSourceFile sourceFile)
  {
    var file = (ICSharpFile?)sourceFile.GetDominantPsiFile<CSharpLanguage>();
    if (file is not null)
    {
      ProcessScope(file);
    }

    return true;
  }

  protected override bool ProcessAssembly(IPsiAssembly assembly) => throw new InvalidOperationException();
  protected override bool ProcessElement(ITreeNode element) => throw new InvalidOperationException();

  private static HashSet<string> GetPossibleNames(
    Dictionary<IDeclaredElement, IAzureAttributeRoutingSource> attributeClasses)
  {
    var names = new HashSet<string>();

    foreach (var (declaredElement, _) in attributeClasses)
    {
      var shortName = declaredElement.ShortName;
      names.Add(shortName);
      names.Add(AttributeUtil.TrimAttributeSuffix(shortName));
    }

    return names;
  }

  private void ProcessScope(ICSharpTypeAndNamespaceHolderDeclaration holderDeclaration)
  {
    foreach (var typeDeclaration in holderDeclaration.TypeDeclarationsEnumerable)
    {
      if (typeDeclaration is not ((IClassDeclaration or IRecordDeclaration { IsStruct: false })
          and IClassLikeDeclaration classLikeDeclaration)) continue;

      Interruption.Current.CheckAndThrow();

      foreach (var methodDeclaration in classLikeDeclaration.MethodDeclarationsEnumerable)
      {
        ProcessMethod(methodDeclaration);
      }
    }

    foreach (var namespaceDeclaration in holderDeclaration.NamespaceDeclarationsEnumerable)
    {
      ProcessScope(namespaceDeclaration);
    }
  }

  private void ProcessMethod(IMethodDeclaration methodDeclaration)
  {
    Interruption.Current.CheckAndThrow();

    foreach (var attribute in methodDeclaration.AttributesEnumerable)
    {
      ProcessAttribute(attribute);
    }
    
    foreach (var parameterDeclaration in methodDeclaration.ParameterDeclarationsEnumerable)
    {
      Interruption.Current.CheckAndThrow();

      foreach (var attribute in parameterDeclaration.AttributesEnumerable)
      {
        ProcessAttribute(attribute);
      }
    }
  }

  private void ProcessAttribute(IAttribute attribute)
  {
    var typeReference = attribute.TypeReference;
    if (typeReference is null) return;

    if (!_possibleNames.Contains(typeReference.GetName())) return;

    var declaredElement = typeReference.Resolve().DeclaredElement;
    if (declaredElement is null ||
        !attributeClassToRoutingSourceMap.TryGetValue(declaredElement, out var attributeRoutingSource)) return;

    var routingAttribute = attributeRoutingSource.CreateRoutingAttribute(attribute);
    if (routingAttribute is null) return;

    _result.Add(routingAttribute);
  }
}