|
1 | 1 | // Licensed to the .NET Foundation under one or more agreements.
|
2 | 2 | // The .NET Foundation licenses this file to you under the MIT license.
|
3 | 3 |
|
| 4 | +using System; |
4 | 5 | using System.Collections.Concurrent;
|
5 | 6 | using System.Diagnostics;
|
6 | 7 | using System.Diagnostics.CodeAnalysis;
|
@@ -511,14 +512,16 @@ private Dictionary<string, AttributeSyntax> CollectAttributeNodes(MemberDeclarat
|
511 | 512 | {
|
512 | 513 | foreach (AttributeSyntax attributeNode in attributeListNode.Attributes)
|
513 | 514 | {
|
514 |
| - if(model.GetSymbolInfo(attributeNode).Symbol is ISymbol attributeSymbol && |
515 |
| - !_attributeSymbolFilter.Include(attributeSymbol.ContainingType)) |
| 515 | + IMethodSymbol? constructor = model.GetSymbolInfo(attributeNode).Symbol as IMethodSymbol; |
| 516 | + if (constructor is null || !_attributeSymbolFilter.Include(constructor.ContainingType)) |
516 | 517 | {
|
517 |
| - // The attributes decorating an API are actually calls to their constructor method, but the attribute filter needs type docIDs |
| 518 | + // The attributes decorating an API are actually calls to their |
| 519 | + // constructor method, but the attribute filter needs type docIDs. |
518 | 520 | continue;
|
519 | 521 | }
|
520 | 522 | var realAttributeNode = attributeNode.WithArgumentList(attributeNode.ArgumentList);
|
521 |
| - if (!dictionary.TryAdd(GetAttributeDocId(realAttributeNode, model), realAttributeNode)) |
| 523 | + |
| 524 | + if (!dictionary.TryAdd(GetAttributeDocId(constructor, attributeNode, model), realAttributeNode)) |
522 | 525 | {
|
523 | 526 | _log.LogWarning(string.Format(Resources.AttributeAlreadyExists, realAttributeNode.ToFullString(), GetDocId(memberNode, model)));
|
524 | 527 | }
|
@@ -647,27 +650,31 @@ private static IEnumerable<T> GetMembersOfType<T>(SyntaxNode node) where T : Mem
|
647 | 650 | .Where(n => n is T m && IsEnumMemberOrHasPublicOrProtectedModifierOrIsDestructor(m))
|
648 | 651 | .Cast<T>();
|
649 | 652 |
|
650 |
| - private string GetAttributeDocId(AttributeSyntax attribute, SemanticModel model) |
| 653 | + private string GetAttributeDocId(IMethodSymbol attributeConstructorSymbol, AttributeSyntax attribute, SemanticModel model) |
651 | 654 | {
|
| 655 | + Debug.Assert(_attributeSymbolFilter.Include(attributeConstructorSymbol.ContainingType)); |
| 656 | + |
652 | 657 | string? attributeDocId = null;
|
| 658 | + |
653 | 659 | if (attribute.ArgumentList is AttributeArgumentListSyntax list && list.Arguments.Any())
|
654 | 660 | {
|
655 |
| - // Workaround to ensure that repeated attributes with different arguments are all included |
| 661 | + // If the constructor has arguments, it might be added multiple times on top of an API, |
| 662 | + // so we need to get the unique full string which includes the actual value of the arguments. |
656 | 663 | attributeDocId = attribute.ToFullString();
|
657 | 664 | }
|
658 |
| - else if (model.GetSymbolInfo(attribute).Symbol is IMethodSymbol constructor) |
| 665 | + else |
659 | 666 | {
|
660 | 667 | try
|
661 | 668 | {
|
662 |
| - attributeDocId = constructor.ContainingType.GetDocumentationCommentId(); |
| 669 | + attributeDocId = attributeConstructorSymbol.ContainingType.GetDocumentationCommentId(); |
663 | 670 | }
|
664 | 671 | catch (Exception e)
|
665 | 672 | {
|
666 |
| - _log.LogWarning(e.Message); |
| 673 | + _log.LogWarning($"Could not retrieve docId of the attribute constructor of {attribute.ToFullString()}: {e.Message} "); |
667 | 674 | }
|
668 | 675 | }
|
669 |
| - // Workaround because some rare cases of attributes in WinForms like System.Windows.Markup.ContentPropertyAttribute |
670 |
| - // cannot be found in the current model. |
| 676 | + |
| 677 | + // Temporary workaround because some rare cases of attributes in WinForms cannot be found in the current model. |
671 | 678 | attributeDocId ??= attribute.ToFullString();
|
672 | 679 |
|
673 | 680 | return attributeDocId;
|
|
0 commit comments