// Returns the name and sets link if one could be found (or null if not) // First checks for "href" attribute and then checks for "cref" private string GetRefNameAndLink(XElement element, out string link) { // Check for href XAttribute hrefAttribute = element.Attribute("href"); if (hrefAttribute != null) { link = $"<a href=\"{hrefAttribute.Value}\">{element.Value}</a>"; return(element.Value); } // Check for cref string cref = element.Attribute("cref")?.Value; if (cref != null) { IDocument crefDoc; ISymbol crefSymbol = DocumentationCommentId.GetFirstSymbolForDeclarationId(cref, _compilation); if (crefSymbol != null && _symbolToDocument.TryGetValue(crefSymbol, out crefDoc)) { string name = crefDoc.String(CodeAnalysisKeys.DisplayName); link = $"<code><a href=\"{_context.GetLink(crefDoc.FilePath(Keys.WritePath))}\">{WebUtility.HtmlEncode(name)}</a></code>"; return(name); } } link = null; return(cref?.Substring(cref.IndexOf(':') + 1) ?? string.Empty); }
private async Task <ImmutableArray <UnusedSymbolInfo> > AnalyzeProject(Project project, CancellationToken cancellationToken) { WriteLine($"Analyze '{project.Name}'", Verbosity.Minimal); Compilation compilation = await project.GetCompilationAsync(cancellationToken); INamedTypeSymbol generatedCodeAttribute = compilation.GetTypeByMetadataName("System.CodeDom.Compiler.GeneratedCodeAttribute"); ImmutableHashSet <ISymbol> ignoredSymbols = Options.IgnoredSymbols .Select(f => DocumentationCommentId.GetFirstSymbolForDeclarationId(f, compilation)) .Where(f => f != null) .ToImmutableHashSet(); return(await UnusedSymbolFinder.FindUnusedSymbolsAsync(project, compilation, Predicate, ignoredSymbols, cancellationToken : cancellationToken).ConfigureAwait(false)); bool Predicate(ISymbol symbol) { return((UnusedSymbolKinds & GetUnusedSymbolKinds(symbol)) != 0 && IsVisible(symbol) && (!Options.IgnoreObsolete || !symbol.HasAttribute(MetadataNames.System_ObsoleteAttribute)) && (Options.IncludeGeneratedCode || !GeneratedCodeUtility.IsGeneratedCode(symbol, generatedCodeAttribute, MefWorkspaceServices.Default.GetService <ISyntaxFactsService>(project.Language).IsComment, cancellationToken))); } }
bool ParseDocSection(XElement item) { switch (item.Name.ToString()) { case "summary": // in case when there is more than one summary, make the behavior the same as VS if (_Summary == null) { _Summary = item; } break; case "remarks": _Remarks = item; break; case "returns": _Returns = item; break; case "param": (_Parameters ?? (_Parameters = new List <XElement>())).Add(item); break; case "typeparam": (_TypeParameters ?? (_TypeParameters = new List <XElement>())).Add(item); break; case "exception": (_Exceptions ?? (_Exceptions = new List <XElement>())).Add(item); break; case "example": (_Examples ?? (_Examples = new List <XElement>())).Add(item); break; case "seealso": (_SeeAlsos ?? (_SeeAlsos = new List <XElement>())).Add(item); break; case "see": (_Sees ?? (_Sees = new List <XElement>())).Add(item); break; case "preliminary": _Preliminary = true; break; case "inheritdoc": if (Config.Instance.QuickInfoOptions.MatchFlags(QuickInfoOptions.DocumentationFromInheritDoc)) { var cref = item.Attribute("cref"); if (cref != null && String.IsNullOrEmpty(cref.Value) == false) { var s = DocumentationCommentId.GetFirstSymbolForDeclarationId(cref.Value, _Compilation); if (s != null) { _ExplicitInheritDoc = new XmlDoc(s, _Compilation); } } } break; default: return(false); } return(true); }
private void CheckDeclarationId <TSymbol>(string expectedId, Compilation compilation, Func <TSymbol, bool> test) where TSymbol : ISymbol { var symbol = DocumentationCommentId.GetFirstSymbolForDeclarationId(expectedId, compilation); Assert.Equal(true, symbol is TSymbol); Assert.Equal(true, test((TSymbol)symbol)); }
private static TSymbol CheckDeclarationId <TSymbol>(string expectedId, Compilation compilation, Func <TSymbol, bool> test) where TSymbol : ISymbol { var symbol = DocumentationCommentId.GetFirstSymbolForDeclarationId(expectedId, compilation); Assert.True(symbol is TSymbol); Assert.True(test((TSymbol)symbol)); return((TSymbol)symbol); }
private static void CheckDeclarationId(string expectedId, INamespaceOrTypeSymbol symbol, Compilation compilation) { var id = DocumentationCommentId.CreateDeclarationId(symbol); Assert.Equal(expectedId, id); var sym = DocumentationCommentId.GetFirstSymbolForDeclarationId(id, compilation); Assert.Equal(symbol, sym); }
internal void RenderXmlDocSymbol(string symbol, InlineCollection inlines, SymbolKind symbolKind) { switch (symbolKind) { case SymbolKind.Parameter: inlines.Add(symbol.Render(false, _SymbolFormatter.Parameter == null, _SymbolFormatter.Parameter)); return; case SymbolKind.TypeParameter: inlines.Add(symbol.Render(_SymbolFormatter.TypeParameter == null, false, _SymbolFormatter.TypeParameter)); return; case SymbolKind.DynamicType: // highlight keywords inlines.Add(symbol.Render(_SymbolFormatter.Keyword)); return; } var s = DocumentationCommentId.GetFirstSymbolForDeclarationId(symbol, _Compilation); if (s != null) { _SymbolFormatter.Format(inlines, s, null, false); return; } if (symbol.Length > 2 && symbol[1] == ':') { switch (symbol[0]) { case 'T': inlines.Add(symbol.Substring(2).Render(false, true, _SymbolFormatter.Class)); return; case 'M': inlines.Add(symbol.Substring(2).Render(false, true, _SymbolFormatter.Method)); return; case 'P': inlines.Add(symbol.Substring(2).Render(false, true, _SymbolFormatter.Property)); return; case 'F': inlines.Add(symbol.Substring(2).Render(false, true, _SymbolFormatter.Field)); return; case 'E': inlines.Add(symbol.Substring(2).Render(false, true, _SymbolFormatter.Delegate)); return; case '!': inlines.Add(symbol.Substring(2).Render(true, true, null)); return; } } inlines.Add(symbol); }
protected override string GetDocumentationForSymbol(string documentationMemberID, CultureInfo preferredCulture, CancellationToken cancellationToken = default) { var symbol = DocumentationCommentId.GetFirstSymbolForDeclarationId(documentationMemberID, _compilation); if (symbol != null) { return(symbol.GetDocumentationCommentXml(preferredCulture, cancellationToken: cancellationToken)); } return(string.Empty); }
internal ISymbol GetFirstSymbolForDeclarationId(string id) { if (Compilations.Length == 1) return DocumentationCommentId.GetFirstSymbolForDeclarationId(id, Compilations[0]); foreach (Compilation compilation in Compilations) { ISymbol symbol = DocumentationCommentId.GetFirstSymbolForDeclarationId(id, compilation); if (symbol != null) return symbol; } return null; }
private static void AppendTextFromTagWithNameAttribute(FormatterState state, XElement element, Compilation compilation) { var nameAttribute = element.Attribute("name"); if (nameAttribute == null) { return; } if (compilation != null && state.TryAppendSymbol(DocumentationCommentId.GetFirstSymbolForDeclarationId(nameAttribute.Value, compilation))) { return; } else { state.AppendString(TrimCrefPrefix(nameAttribute.Value)); } }
private IEnumerable <SymbolDisplayPart> RefToParts(string elementName, string refValue) { var semanticModel = _provider.SemanticModel; if (!(semanticModel is null)) { var symbol = DocumentationCommentId.GetFirstSymbolForDeclarationId(refValue, semanticModel.Compilation); if (!(symbol is null)) { return(symbol.ToMinimalDisplayParts(semanticModel, _provider._position, _crefFormat)); } if (TryProcessRef(elementName, refValue, out var part)) { return(part.Enumerate()); } } return(_provider.CreatePart(SymbolDisplayPartKind.Text, TrimRefPrefix(refValue)).Enumerate()); }
internal static IEnumerable <SymbolDisplayPart> CrefToSymbolDisplayParts(string crefValue, int position, SemanticModel semanticModel, SymbolDisplayFormat format = null) { // first try to parse the symbol if (semanticModel != null) { var symbol = DocumentationCommentId.GetFirstSymbolForDeclarationId(crefValue, semanticModel.Compilation); if (symbol != null) { if (symbol.IsConstructor()) { format = format.WithMemberOptions(SymbolDisplayMemberOptions.IncludeParameters | SymbolDisplayMemberOptions.IncludeExplicitInterface); } return(symbol.ToMinimalDisplayParts(semanticModel, position, format)); } } // if any of that fails fall back to just displaying the raw text return(SpecializedCollections.SingletonEnumerable(new SymbolDisplayPart(kind: SymbolDisplayPartKind.Text, symbol: null, text: TrimCrefPrefix(crefValue)))); }
private static void AppendTextFromSeeTag(FormatterState state, XElement element, Compilation compilation) { var crefAttribute = element.Attribute("cref"); if (crefAttribute == null) { return; } var crefValue = crefAttribute.Value; if (compilation != null && state.TryAppendSymbol(DocumentationCommentId.GetFirstSymbolForDeclarationId(crefValue, compilation))) { return; } else { state.AppendString(TrimCrefPrefix(crefValue)); } }
private void BuildXmlDocumentation(ISymbol symbol, Compilation compilation, _VSOBJDESCOPTIONS options) { var documentationComment = symbol.GetDocumentationComment(expandIncludes: true, cancellationToken: CancellationToken.None); if (documentationComment == null) { return; } var formattingService = _project.LanguageServices.GetService <IDocumentationCommentFormattingService>(); if (formattingService == null) { return; } var emittedDocs = false; if (documentationComment.SummaryText != null) { AddLineBreak(); AddName(ServicesVSResources.Library_Summary); AddLineBreak(); AddText(formattingService.Format(documentationComment.SummaryText, compilation)); emittedDocs = true; } if (documentationComment.TypeParameterNames.Length > 0) { if (emittedDocs) { AddLineBreak(); } AddLineBreak(); AddName(ServicesVSResources.Library_TypeParameters); foreach (var typeParameterName in documentationComment.TypeParameterNames) { AddLineBreak(); var typeParameterText = documentationComment.GetTypeParameterText(typeParameterName); if (typeParameterText != null) { AddParam(typeParameterName); AddText(": "); AddText(formattingService.Format(typeParameterText, compilation)); emittedDocs = true; } } } if (documentationComment.ParameterNames.Length > 0) { if (emittedDocs) { AddLineBreak(); } AddLineBreak(); AddName(ServicesVSResources.Library_Parameters); foreach (var parameterName in documentationComment.ParameterNames) { AddLineBreak(); var parameterText = documentationComment.GetParameterText(parameterName); if (parameterText != null) { AddParam(parameterName); AddText(": "); AddText(formattingService.Format(parameterText, compilation)); emittedDocs = true; } } } if (ShowReturnsDocumentation(symbol) && documentationComment.ReturnsText != null) { if (emittedDocs) { AddLineBreak(); } AddLineBreak(); AddName(ServicesVSResources.Library_Returns); AddLineBreak(); AddText(formattingService.Format(documentationComment.ReturnsText, compilation)); emittedDocs = true; } if (documentationComment.RemarksText != null) { if (emittedDocs) { AddLineBreak(); } AddLineBreak(); AddName(ServicesVSResources.Library_Remarks); AddLineBreak(); AddText(formattingService.Format(documentationComment.RemarksText, compilation)); emittedDocs = true; } if (documentationComment.ExceptionTypes.Length > 0) { if (emittedDocs) { AddLineBreak(); } AddLineBreak(); AddName(ServicesVSResources.Library_Exceptions); foreach (var exceptionType in documentationComment.ExceptionTypes) { var exceptionTypeSymbol = DocumentationCommentId.GetFirstSymbolForDeclarationId(exceptionType, compilation) as INamedTypeSymbol; if (exceptionTypeSymbol != null) { AddLineBreak(); var exceptionTexts = documentationComment.GetExceptionTexts(exceptionType); if (exceptionTexts.Length == 0) { AddTypeLink(exceptionTypeSymbol, LinkFlags.None); } else { foreach (var exceptionText in exceptionTexts) { AddTypeLink(exceptionTypeSymbol, LinkFlags.None); AddText(": "); AddText(formattingService.Format(exceptionText, compilation)); } } emittedDocs = true; } } } }
// Can be removed if https://github.com/dotnet/roslyn/issues/67 gets resolved // Modeled after Sandcastle implementation: http://tunnelvisionlabs.github.io/SHFB/docs-master/XMLCommentsGuide/html/86453FFB-B978-4A2A-9EB5-70E118CA8073.htm private void ProcessInheritDoc(XElement root, ISymbol currentSymbol, List <XElement> inheritDocElements, HashSet <string> inheritedSymbolCommentIds) { if (inheritDocElements.Count > 0) { // Gather the documents (first in the list takes precedence) List <ISymbol> inheritedSymbols = new List <ISymbol>(); foreach (XElement inheritDocElement in inheritDocElements) { // Remove from the parent inheritDocElement.Remove(); // Locate the appropriate symbol string inheritDocElementCref = inheritDocElement.Attribute("cref")?.Value; if (inheritDocElementCref == null && inheritedSymbolCommentIds.Add(currentSymbol.GetDocumentationCommentId())) { INamedTypeSymbol currentTypeSymbol = currentSymbol as INamedTypeSymbol; IMethodSymbol currentMethodSymbol = currentSymbol as IMethodSymbol; IPropertySymbol currentPropertySymbol = currentSymbol as IPropertySymbol; IEventSymbol currentEventSymbol = currentSymbol as IEventSymbol; if (currentTypeSymbol != null) { // Types and interfaces, inherit from all base types List <INamedTypeSymbol> baseTypeSymbols = AnalyzeSymbolVisitor.GetBaseTypes(currentTypeSymbol) .Where(x => inheritedSymbolCommentIds.Add(x.GetDocumentationCommentId())) .ToList(); if (baseTypeSymbols.Count > 0) { inheritedSymbols.AddRange(baseTypeSymbols); } // Then inherit from all interfaces List <INamedTypeSymbol> interfaceSymbols = currentTypeSymbol.AllInterfaces .Where(x => inheritedSymbolCommentIds.Add(x.GetDocumentationCommentId())) .ToList(); if (interfaceSymbols.Count > 0) { inheritedSymbols.AddRange(interfaceSymbols); } } else if (currentMethodSymbol != null && currentMethodSymbol.Name == currentMethodSymbol.ContainingType.Name) { // Constructor, check base type constructors for the same signature string signature = AnalyzeSymbolVisitor.GetFullName(currentMethodSymbol); signature = signature.Substring(signature.IndexOf('(')); foreach (INamedTypeSymbol baseTypeSymbol in AnalyzeSymbolVisitor.GetBaseTypes(currentMethodSymbol.ContainingType)) { foreach (IMethodSymbol constructorSymbol in baseTypeSymbol.Constructors.Where(x => !x.IsImplicitlyDeclared)) { string constructorSignature = AnalyzeSymbolVisitor.GetFullName(constructorSymbol); constructorSignature = constructorSignature.Substring(constructorSignature.IndexOf('(')); if (signature == constructorSignature && inheritedSymbolCommentIds.Add(constructorSymbol.GetDocumentationCommentId())) { inheritedSymbols.Add(constructorSymbol); } } } } else if (currentMethodSymbol != null) { PopulateInheritedMemberSymbols(currentMethodSymbol, x => x.OverriddenMethod, inheritedSymbolCommentIds, inheritedSymbols); } else if (currentPropertySymbol != null) { PopulateInheritedMemberSymbols(currentPropertySymbol, x => x.OverriddenProperty, inheritedSymbolCommentIds, inheritedSymbols); } else if (currentEventSymbol != null) { PopulateInheritedMemberSymbols(currentEventSymbol, x => x.OverriddenEvent, inheritedSymbolCommentIds, inheritedSymbols); } } else if (inheritDocElementCref != null) { // Explicit cref if (inheritedSymbolCommentIds.Add(inheritDocElementCref)) { ISymbol inheritedSymbol = DocumentationCommentId.GetFirstSymbolForDeclarationId(inheritDocElementCref, _compilation); if (inheritedSymbol != null) { inheritedSymbols.Add(inheritedSymbol); } } } } // Add the inherited comments foreach (ISymbol inheritedSymbol in inheritedSymbols) { string inheritedXml = inheritedSymbol.GetDocumentationCommentXml(expandIncludes: true); if (!string.IsNullOrEmpty(inheritedXml)) { XElement inheritedRoot = GetRootElement(inheritedXml); if (inheritedRoot != null) { // Inherit elements other than <inheritdoc> List <XElement> inheritedInheritDocElements = new List <XElement>(); foreach (XElement inheritedElement in inheritedRoot.Elements()) { if (inheritedElement.Name == "inheritdoc") { inheritedInheritDocElements.Add(inheritedElement); } else { string inheritedElementCref = inheritedElement.Attribute("cref")?.Value; string inheritedElementName = inheritedElement.Attribute("name")?.Value; bool inherit = true; foreach (XElement rootElement in root.Elements(inheritedElement.Name)) { if (inheritedElementCref == null && inheritedElementName == null) { // Don't inherit if the name is the same and there's no distinguishing attributes inherit = false; break; } if (inheritedElementCref != null && inheritedElementCref == rootElement.Attribute("cref")?.Value) { // Don't inherit if the cref attribute is the same inherit = false; break; } if (inheritedElementName != null && inheritedElementName == rootElement.Attribute("name")?.Value) { // Don't inherit if the name attribute is the same inherit = false; break; } } if (inherit) { root.Add(inheritedElement); } } } // Recursively inherit <inheritdoc> if (inheritedInheritDocElements.Count > 0) { ProcessInheritDoc(root, inheritedSymbol, inheritedInheritDocElements, inheritedSymbolCommentIds); } } } } } }
internal ISymbol GetFirstSymbolForDeclarationId(string id) { return(DocumentationCommentId.GetFirstSymbolForDeclarationId(id, Compilation)); }