예제 #1
0
        protected override async Task <Document> AddAssemblyInfoRegionAsync(Document document, ISymbol symbol, CancellationToken cancellationToken)
        {
            string assemblyInfo = MetadataAsSourceHelpers.GetAssemblyInfo(symbol.ContainingAssembly);
            var    compilation  = await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);

            string assemblyPath = MetadataAsSourceHelpers.GetAssemblyDisplay(compilation, symbol.ContainingAssembly);

            var regionTrivia = SyntaxFactory.RegionDirectiveTrivia(true)
                               .WithTrailingTrivia(new[] { SyntaxFactory.Space, SyntaxFactory.PreprocessingMessage(assemblyInfo) });

            var oldRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var newRoot = oldRoot.WithLeadingTrivia(new[]
            {
                SyntaxFactory.Trivia(regionTrivia),
                SyntaxFactory.CarriageReturnLineFeed,
                SyntaxFactory.Comment("// " + assemblyPath),
                SyntaxFactory.CarriageReturnLineFeed,
                SyntaxFactory.Trivia(SyntaxFactory.EndRegionDirectiveTrivia(true)),
                SyntaxFactory.CarriageReturnLineFeed,
                SyntaxFactory.CarriageReturnLineFeed
            });

            return(document.WithSyntaxRoot(newRoot));
        }
        private static async Task <Document> AddAssemblyInfoRegionAsync(Document document, ISymbol symbol, CancellationToken cancellationToken)
        {
            var assemblyInfo = MetadataAsSourceHelpers.GetAssemblyInfo(symbol.ContainingAssembly);
            var compilation  = await document.Project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false);

            var assemblyPath = MetadataAsSourceHelpers.GetAssemblyDisplay(compilation, symbol.ContainingAssembly);

            var regionTrivia = SyntaxFactory.RegionDirectiveTrivia(true)
                               .WithTrailingTrivia(new[] { SyntaxFactory.Space, SyntaxFactory.PreprocessingMessage(assemblyInfo) });

            var oldRoot = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

            var newRoot = oldRoot.WithLeadingTrivia(new[]
            {
                SyntaxFactory.Trivia(regionTrivia),
                SyntaxFactory.CarriageReturnLineFeed,
                SyntaxFactory.Comment("// " + assemblyPath),
                SyntaxFactory.CarriageReturnLineFeed,
                SyntaxFactory.Comment($"// Decompiled with ICSharpCode.Decompiler {s_decompilerVersion.FileVersion}"),
                SyntaxFactory.CarriageReturnLineFeed,
                SyntaxFactory.Trivia(SyntaxFactory.EndRegionDirectiveTrivia(true)),
                SyntaxFactory.CarriageReturnLineFeed,
                SyntaxFactory.CarriageReturnLineFeed
            });

            return(document.WithSyntaxRoot(newRoot));
        }
예제 #3
0
        private async Task <Location> RelocateSymbol_NoLock(MetadataAsSourceGeneratedFileInfo fileInfo, SymbolKey symbolId, CancellationToken cancellationToken)
        {
            // We need to relocate the symbol in the already existing file. If the file is open, we can just
            // reuse that workspace. Otherwise, we have to go spin up a temporary project to do the binding.
            if (_openedDocumentIds.TryGetValue(fileInfo, out var openDocumentId))
            {
                // Awesome, it's already open. Let's try to grab a document for it
                var document = _workspace.CurrentSolution.GetDocument(openDocumentId);

                return(await MetadataAsSourceHelpers.GetLocationInGeneratedSourceAsync(symbolId, document, cancellationToken).ConfigureAwait(false));
            }

            // Annoying case: the file is still on disk. Only real option here is to spin up a fake project to go and bind in.
            var temporaryProjectInfoAndDocumentId = fileInfo.GetProjectInfoAndDocumentId(_workspace, loadFileFromDisk: true);
            var temporaryDocument = _workspace.CurrentSolution.AddProject(temporaryProjectInfoAndDocumentId.Item1)
                                    .GetDocument(temporaryProjectInfoAndDocumentId.Item2);

            return(await MetadataAsSourceHelpers.GetLocationInGeneratedSourceAsync(symbolId, temporaryDocument, cancellationToken).ConfigureAwait(false));
        }
예제 #4
0
        internal static DefinitionItem CreateMetadataDefinition(
            ImmutableArray <string> tags,
            ImmutableArray <TaggedText> displayParts,
            ImmutableArray <TaggedText> nameDisplayParts,
            Solution solution,
            ISymbol symbol,
            ImmutableDictionary <string, string>?properties = null,
            bool displayIfNoReferences = true)
        {
            properties ??= ImmutableDictionary <string, string> .Empty;

            var symbolKey = symbol.GetSymbolKey().ToString();

            var projectId = solution.GetOriginatingProjectId(symbol);

            Contract.ThrowIfNull(projectId);

            properties = properties.Add(MetadataSymbolKey, symbolKey)
                         .Add(MetadataSymbolOriginatingProjectIdGuid, projectId.Id.ToString())
                         .Add(MetadataSymbolOriginatingProjectIdDebugName, projectId.DebugName ?? "");

            // Find the highest level containing type to show as the "file name". For metadata locations
            // that come from embedded source or SourceLink this could be wrong, as there is no reason
            // to assume a type is defined in a filename that matches, but its _way_ too expensive
            // to try to find the right answer. For metadata-as-source locations though, it will be the same
            // as the synthesized filename, so will make sense in the majority of cases.
            var containingTypeName = MetadataAsSourceHelpers.GetTopLevelContainingNamedType(symbol).Name;

            properties = properties.Add(AbstractReferenceFinder.ContainingTypeInfoPropertyName, containingTypeName);

            var originationParts = GetOriginationParts(symbol);

            return(new DefaultDefinitionItem(
                       tags, displayParts, nameDisplayParts, originationParts,
                       sourceSpans: ImmutableArray <DocumentSpan> .Empty,
                       properties: properties,
                       displayableProperties: ImmutableDictionary <string, string> .Empty,
                       displayIfNoReferences: displayIfNoReferences));
        }
예제 #5
0
        public async Task <MetadataAsSourceFile> GetGeneratedFileAsync(Project project, ISymbol symbol, bool allowDecompilation, CancellationToken cancellationToken = default)
        {
            if (project == null)
            {
                throw new ArgumentNullException(nameof(project));
            }

            if (symbol == null)
            {
                throw new ArgumentNullException(nameof(symbol));
            }

            if (symbol.Kind == SymbolKind.Namespace)
            {
                throw new ArgumentException(EditorFeaturesResources.symbol_cannot_be_a_namespace, nameof(symbol));
            }

            symbol = symbol.GetOriginalUnreducedDefinition();

            MetadataAsSourceGeneratedFileInfo fileInfo;
            Location navigateLocation  = null;
            var      topLevelNamedType = MetadataAsSourceHelpers.GetTopLevelContainingNamedType(symbol);
            var      symbolId          = SymbolKey.Create(symbol, cancellationToken);

            using (await _gate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false))
            {
                InitializeWorkspace(project);

                var infoKey = await GetUniqueDocumentKey(project, topLevelNamedType, cancellationToken).ConfigureAwait(false);

                fileInfo = _keyToInformation.GetOrAdd(infoKey, _ => new MetadataAsSourceGeneratedFileInfo(GetRootPathWithGuid_NoLock(), project, topLevelNamedType));

                _generatedFilenameToInformation[fileInfo.TemporaryFilePath] = fileInfo;

                if (!File.Exists(fileInfo.TemporaryFilePath))
                {
                    // We need to generate this. First, we'll need a temporary project to do the generation into. We
                    // avoid loading the actual file from disk since it doesn't exist yet.
                    var temporaryProjectInfoAndDocumentId = fileInfo.GetProjectInfoAndDocumentId(_workspace, loadFileFromDisk: false);
                    var temporaryDocument = _workspace.CurrentSolution.AddProject(temporaryProjectInfoAndDocumentId.Item1)
                                            .GetDocument(temporaryProjectInfoAndDocumentId.Item2);

                    var useDecompiler = allowDecompilation;
                    if (useDecompiler)
                    {
                        try
                        {
                            temporaryDocument = await DecompileSymbolAsync(temporaryDocument, symbol, cancellationToken).ConfigureAwait(false);
                        }
                        catch (Exception e) when(FatalError.ReportWithoutCrashUnlessCanceled(e))
                        {
                            useDecompiler = false;
                        }
                    }

                    if (!useDecompiler)
                    {
                        var sourceFromMetadataService = temporaryDocument.Project.LanguageServices.GetService <IMetadataAsSourceService>();
                        temporaryDocument = await sourceFromMetadataService.AddSourceToAsync(temporaryDocument, symbol, cancellationToken).ConfigureAwait(false);
                    }

                    // We have the content, so write it out to disk
                    var text = await temporaryDocument.GetTextAsync(cancellationToken).ConfigureAwait(false);

                    // Create the directory. It's possible a parallel deletion is happening in another process, so we may have
                    // to retry this a few times.
                    var directoryToCreate = Path.GetDirectoryName(fileInfo.TemporaryFilePath);
                    while (!Directory.Exists(directoryToCreate))
                    {
                        try
                        {
                            Directory.CreateDirectory(directoryToCreate);
                        }
                        catch (DirectoryNotFoundException)
                        {
                        }
                        catch (UnauthorizedAccessException)
                        {
                        }
                    }

                    using (var textWriter = new StreamWriter(fileInfo.TemporaryFilePath, append: false, encoding: fileInfo.Encoding))
                    {
                        text.Write(textWriter);
                    }

                    // Mark read-only
                    new FileInfo(fileInfo.TemporaryFilePath).IsReadOnly = true;

                    // Locate the target in the thing we just created
                    navigateLocation = await MetadataAsSourceHelpers.GetLocationInGeneratedSourceAsync(symbolId, temporaryDocument, cancellationToken).ConfigureAwait(false);
                }

                // If we don't have a location yet, then that means we're re-using an existing file. In this case, we'll want to relocate the symbol.
                if (navigateLocation == null)
                {
                    navigateLocation = await RelocateSymbol_NoLock(fileInfo, symbolId, cancellationToken).ConfigureAwait(false);
                }
            }

            var documentName = string.Format(
                "{0} [{1}]",
                topLevelNamedType.Name,
                EditorFeaturesResources.from_metadata);

            var documentTooltip = topLevelNamedType.ToDisplayString(new SymbolDisplayFormat(typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces));

            return(new MetadataAsSourceFile(fileInfo.TemporaryFilePath, navigateLocation, documentName, documentTooltip));
        }
예제 #6
0
        public static Task <Location> GetLocationInGeneratedSourceAsync(ISymbol symbol, Document generatedDocument, CancellationToken cancellationToken)
        {
            var symbolKey = SymbolKey.Create(symbol, cancellationToken);

            return(MetadataAsSourceHelpers.GetLocationInGeneratedSourceAsync(symbolKey, generatedDocument, cancellationToken));
        }
예제 #7
0
 public static string GetAssemblyDisplay(Compilation compilation, IAssemblySymbol assemblySymbol)
 => MetadataAsSourceHelpers.GetAssemblyDisplay(compilation, assemblySymbol);
예제 #8
0
 public static string GetAssemblyInfo(IAssemblySymbol assemblySymbol)
 => MetadataAsSourceHelpers.GetAssemblyInfo(assemblySymbol);