Beispiel #1
0
            public async Task <SymbolTreeInfo> TryGetMetadataSymbolTreeInfoAsync(
                Solution solution,
                PortableExecutableReference reference,
                CancellationToken cancellationToken)
            {
                var checksum = SymbolTreeInfo.GetMetadataChecksum(solution, reference, cancellationToken);

                var key = GetReferenceKey(reference);

                if (key != null)
                {
                    if (_metadataPathToInfo.TryGetValue(key, out var metadataInfo) &&
                        metadataInfo.SymbolTreeInfo.Checksum == checksum)
                    {
                        return(metadataInfo.SymbolTreeInfo);
                    }
                }

                // If we didn't have it in our cache, see if we can load it from disk.
                // Note: pass 'loadOnly' so we only attempt to load from disk, not to actually
                // try to create the metadata.
                var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(
                    solution, reference, checksum, loadOnly : true, cancellationToken : cancellationToken).ConfigureAwait(false);

                return(info);
            }
Beispiel #2
0
            private async Task UpdateReferenceAsync(
                Project project, PortableExecutableReference reference, CancellationToken cancellationToken)
            {
                var key = GetReferenceKey(reference);

                if (key == null)
                {
                    return;
                }

                var checksum = SymbolTreeInfo.GetMetadataChecksum(project.Solution, reference, cancellationToken);

                if (!_metadataPathToInfo.TryGetValue(key, out var metadataInfo) ||
                    metadataInfo.SymbolTreeInfo.Checksum != checksum)
                {
                    var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(
                        project.Solution, reference, checksum, loadOnly : false, cancellationToken : cancellationToken).ConfigureAwait(false);

                    Contract.ThrowIfNull(info);
                    Contract.ThrowIfTrue(info.Checksum != checksum, "If we computed a SymbolTreeInfo, then its checksum much match our checksum.");

                    // Note, getting the info may fail (for example, bogus metadata).  That's ok.
                    // We still want to cache that result so that don't try to continuously produce
                    // this info over and over again.
                    metadataInfo             = new MetadataInfo(info, metadataInfo.ReferencingProjects ?? new HashSet <ProjectId>());
                    _metadataPathToInfo[key] = metadataInfo;
                }

                // Keep track that this dll is referenced by this project.
                metadataInfo.ReferencingProjects.Add(project.Id);
            }
            private async Task <ImmutableArray <IMethodSymbol>?> GetExtensionMethodSymbolsFromPeReferenceAsync(
                PortableExecutableReference peReference,
                bool forceIndexCreation,
                CancellationToken cancellationToken)
            {
                var index = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(
                    Solution, peReference, loadOnly : !forceIndexCreation, cancellationToken).ConfigureAwait(false);

                if (index == null)
                {
                    // Returns null to indicate index not ready
                    return(null);
                }

                if (!(index.ContainsExtensionMethod && _originatingSemanticModel.Compilation.GetAssemblyOrModuleSymbol(peReference) is IAssemblySymbol assembly))
                {
                    return(ImmutableArray <IMethodSymbol> .Empty);
                }

                var filter           = CreateAggregatedFilter(index);
                var internalsVisible = _originatingSemanticModel.Compilation.Assembly.IsSameAssemblyOrHasFriendAccessTo(assembly);

                var matchingMethodSymbols = GetPotentialMatchingSymbolsFromAssembly(assembly, filter, internalsVisible, cancellationToken);

                return(GetExtensionMethodsForSymbolsFromSameCompilation(matchingMethodSymbols, cancellationToken));
            }
Beispiel #4
0
            private async Task UpdateReferenceAsync(
                Project project, Compilation compilation, PortableExecutableReference reference, CancellationToken cancellationToken)
            {
                var key = GetReferenceKey(reference);

                if (key == null)
                {
                    return;
                }

                DateTime lastWriteTime;

                if (!TryGetLastWriteTime(key, out lastWriteTime))
                {
                    // Couldn't get the write time.  Just ignore this reference.
                    return;
                }

                MetadataInfo metadataInfo;

                if (!_metadataPathToInfo.TryGetValue(key, out metadataInfo) || metadataInfo.TimeStamp == lastWriteTime)
                {
                    var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(
                        project.Solution, reference, loadOnly : false, cancellationToken : cancellationToken).ConfigureAwait(false);

                    metadataInfo = new MetadataInfo(lastWriteTime, info, metadataInfo.ReferencingProjects ?? new HashSet <ProjectId>());
                    _metadataPathToInfo.AddOrUpdate(key, metadataInfo, (_1, _2) => metadataInfo);
                }

                // Keep track that this dll is referenced by this project.
                metadataInfo.ReferencingProjects.Add(project.Id);
            }
            public async Task <SymbolTreeInfo> TryGetMetadataSymbolTreeInfoAsync(
                Solution solution,
                PortableExecutableReference reference,
                CancellationToken cancellationToken)
            {
                var metadataId = SymbolTreeInfo.GetMetadataIdNoThrow(reference);

                if (metadataId == null)
                {
                    return(null);
                }

                var checksum = SymbolTreeInfo.GetMetadataChecksum(solution, reference, cancellationToken);

                // See if the last value produced matches what the caller is asking for.  If so, return that.
                if (_metadataIdToInfo.TryGetValue(metadataId, out var metadataInfo) &&
                    metadataInfo.SymbolTreeInfo.Checksum == checksum)
                {
                    return(metadataInfo.SymbolTreeInfo);
                }

                // If we didn't have it in our cache, see if we can load it from disk.
                // Note: pass 'loadOnly' so we only attempt to load from disk, not to actually
                // try to create the metadata.
                var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(
                    solution, reference, checksum, loadOnly : true, cancellationToken).ConfigureAwait(false);

                return(info);
            }
Beispiel #6
0
        private static async Task <GetIndicesResult> TryGetIndicesAsync(
            Project currentProject,
            bool forceIndexCreation,
            CancellationToken cancellationToken)
        {
            var solution           = currentProject.Solution;
            var cacheService       = GetCacheService(solution.Workspace);
            var graph              = currentProject.Solution.GetProjectDependencyGraph();
            var relevantProjectIds = graph.GetProjectsThatThisProjectTransitivelyDependsOn(currentProject.Id)
                                     .Concat(currentProject.Id);

            using var syntaxDisposer = ArrayBuilder <CacheEntry> .GetInstance(out var syntaxBuilder);

            using var symbolDisposer = ArrayBuilder <SymbolTreeInfo> .GetInstance(out var symbolBuilder);

            foreach (var projectId in relevantProjectIds)
            {
                var project = solution.GetProject(projectId);
                if (project == null || !project.SupportsCompilation)
                {
                    continue;
                }

                // By default, don't trigger index creation except for documents in current project.
                var loadOnly   = !forceIndexCreation && projectId != currentProject.Id;
                var cacheEntry = await GetCacheEntryAsync(project, loadOnly, cacheService, cancellationToken).ConfigureAwait(false);

                if (cacheEntry == null)
                {
                    // Don't provide anything if we don't have all the required SyntaxTreeIndex created.
                    return(GetIndicesResult.NoneResult);
                }

                syntaxBuilder.Add(cacheEntry.Value);
            }

            // Search through all direct PE references.
            foreach (var peReference in currentProject.MetadataReferences.OfType <PortableExecutableReference>())
            {
                var info = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(
                    solution, peReference, loadOnly : !forceIndexCreation, cancellationToken).ConfigureAwait(false);

                if (info == null)
                {
                    // Don't provide anything if we don't have all the required SymbolTreeInfo created.
                    return(GetIndicesResult.NoneResult);
                }

                if (info.ContainsExtensionMethod)
                {
                    symbolBuilder.Add(info);
                }
            }

            var syntaxIndices = syntaxBuilder.ToImmutable();
            var symbolInfos   = symbolBuilder.ToImmutable();

            return(new GetIndicesResult(hasResult: true, syntaxIndices, symbolInfos));
        }
Beispiel #7
0
                static async Task UpdateReferenceAsync(
                    ConcurrentDictionary <MetadataId, MetadataInfo> metadataIdToInfo,
                    Project project,
                    PortableExecutableReference reference,
                    CancellationToken cancellationToken
                    )
                {
                    var metadataId = SymbolTreeInfo.GetMetadataIdNoThrow(reference);

                    if (metadataId == null)
                    {
                        return;
                    }

                    // 🐉 PERF: GetMetadataChecksum indirectly uses a ConditionalWeakTable. This call is intentionally
                    // placed before the first 'await' of this asynchronous method to ensure it executes in the
                    // synchronous portion of the caller. https://dev.azure.com/devdiv/DevDiv/_workitems/edit/1270250
                    var checksum = SymbolTreeInfo.GetMetadataChecksum(
                        project.Solution,
                        reference,
                        cancellationToken
                        );

                    if (
                        !metadataIdToInfo.TryGetValue(metadataId, out var metadataInfo) ||
                        metadataInfo.SymbolTreeInfo.Checksum != checksum
                        )
                    {
                        var info = await SymbolTreeInfo
                                   .GetInfoForMetadataReferenceAsync(
                            project.Solution,
                            reference,
                            checksum,
                            loadOnly : false,
                            cancellationToken : cancellationToken
                            )
                                   .ConfigureAwait(false);

                        Contract.ThrowIfNull(info);
                        Contract.ThrowIfTrue(
                            info.Checksum != checksum,
                            "If we computed a SymbolTreeInfo, then its checksum much match our checksum."
                            );

                        // Note, getting the info may fail (for example, bogus metadata).  That's ok.
                        // We still want to cache that result so that don't try to continuously produce
                        // this info over and over again.
                        metadataInfo = new MetadataInfo(
                            info,
                            metadataInfo.ReferencingProjects ?? new HashSet <ProjectId>()
                            );
                        metadataIdToInfo[metadataId] = metadataInfo;
                    }
                    // Keep track that this dll is referenced by this project.
                    lock (metadataInfo.ReferencingProjects)
                    {
                        metadataInfo.ReferencingProjects.Add(project.Id);
                    }
                }
            public static async ValueTask UpdateCacheAsync(Project project, CancellationToken cancellationToken)
            {
                cancellationToken.ThrowIfCancellationRequested();
                var cacheService = GetCacheService(project);

                foreach (var relevantProject in GetAllRelevantProjects(project))
                {
                    await GetUpToDateCacheEntryAsync(relevantProject, cacheService, cancellationToken).ConfigureAwait(false);
                }

                foreach (var peReference in GetAllRelevantPeReferences(project))
                {
                    await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(project.Solution, peReference, loadOnly : false, cancellationToken).ConfigureAwait(false);
                }
            }
            public Task PopulateIndicesAsync(CancellationToken cancellationToken)
            {
                using var _ = ArrayBuilder <Task> .GetInstance(out var tasks);

                foreach (var project in GetAllRelevantProjects())
                {
                    tasks.Add(Task.Run(()
                                       => GetCacheEntryAsync(project, loadOnly: false, _cacheService, cancellationToken), cancellationToken));
                }

                foreach (var peReference in GetAllRelevantPeReferences())
                {
                    tasks.Add(Task.Run(()
                                       => SymbolTreeInfo.GetInfoForMetadataReferenceAsync(Solution, peReference, loadOnly: false, cancellationToken), cancellationToken));
                }

                return(Task.WhenAll(tasks.ToImmutable()));
            }
            private async ValueTask <ImmutableArray <IMethodSymbol>?> GetExtensionMethodSymbolsFromPeReferenceAsync(
                PortableExecutableReference peReference,
                bool forceCacheCreation,
                CancellationToken cancellationToken)
            {
                SymbolTreeInfo?symbolInfo;

                if (forceCacheCreation)
                {
                    symbolInfo = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(
                        _originatingDocument.Project.Solution, peReference, loadOnly : false, cancellationToken).ConfigureAwait(false);
                }
                else
                {
                    var cachedInfoTask = SymbolTreeInfo.TryGetCachedInfoForMetadataReferenceIgnoreChecksumAsync(peReference, cancellationToken);
                    if (cachedInfoTask.IsCompleted)
                    {
                        // Use cached data if available, even checksum doesn't match. We will update the cache in the background.
                        symbolInfo = await cachedInfoTask.ConfigureAwait(false);
                    }
                    else
                    {
                        // No cached data immediately available, returns null to indicate index not ready
                        return(null);
                    }
                }

                if (symbolInfo is null ||
                    !symbolInfo.ContainsExtensionMethod ||
                    _originatingSemanticModel.Compilation.GetAssemblyOrModuleSymbol(peReference) is not IAssemblySymbol assembly)
                {
                    return(ImmutableArray <IMethodSymbol> .Empty);
                }

                var filter           = CreateAggregatedFilter(symbolInfo);
                var internalsVisible = _originatingSemanticModel.Compilation.Assembly.IsSameAssemblyOrHasFriendAccessTo(assembly);

                var matchingMethodSymbols = GetPotentialMatchingSymbolsFromAssembly(assembly, filter, internalsVisible, cancellationToken);

                return(GetExtensionMethodsForSymbolsFromSameCompilation(matchingMethodSymbols, cancellationToken));
            }
Beispiel #11
0
            static async Task PopulateIndicesAsync(Project currentProject, CancellationToken cancellationToken)
            {
                var solution     = currentProject.Solution;
                var cacheService = GetCacheService(solution.Workspace);

                using var _ = ArrayBuilder <Task> .GetInstance(out var tasks);

                foreach (var project in GetAllRelevantProjects(currentProject))
                {
                    tasks.Add(Task.Run(()
                                       => GetCacheEntryAsync(project, loadOnly: false, cacheService, cancellationToken), cancellationToken));
                }

                foreach (var peReference in GetAllRelevantPeReferences(currentProject))
                {
                    tasks.Add(Task.Run(()
                                       => SymbolTreeInfo.GetInfoForMetadataReferenceAsync(solution, peReference, loadOnly: false, cancellationToken), cancellationToken));
                }

                await Task.WhenAll(tasks.ToImmutable()).ConfigureAwait(false);
            }
Beispiel #12
0
        private static async Task FindImmediateMatchingMetadataTypesInMetadataReferenceAsync(
            SymbolAndProjectIdSet metadataTypes,
            Project project,
            Func <SymbolAndProjectIdSet, INamedTypeSymbol, bool> metadataTypeMatches,
            Compilation compilation,
            PortableExecutableReference reference,
            SymbolAndProjectIdSet result,
            CancellationToken cancellationToken)
        {
            // We store an index in SymbolTreeInfo of the *simple* metadata type name
            // to the names of the all the types that either immediately derive or
            // implement that type.  Because the mapping is from the simple name
            // we might get false positives.  But that's fine as we still use
            // 'metadataTypeMatches' to make sure the match is correct.
            var symbolTreeInfo = await SymbolTreeInfo.GetInfoForMetadataReferenceAsync(
                project.Solution, reference, loadOnly : false, cancellationToken : cancellationToken).ConfigureAwait(false);

            // For each type we care about, see if we can find any derived types
            // in this index.
            foreach (var metadataType in metadataTypes)
            {
                var baseTypeName = metadataType.Symbol.Name;

                // For each derived type we find, see if we can map that back
                // to an actual symbol.  Then check if that symbol actually fits
                // our criteria.
                foreach (var derivedType in symbolTreeInfo.GetDerivedMetadataTypes(baseTypeName, compilation, cancellationToken))
                {
                    if (derivedType != null && derivedType.Locations.Any(s_isInMetadata))
                    {
                        if (metadataTypeMatches(metadataTypes, derivedType))
                        {
                            result.Add(SymbolAndProjectId.Create(derivedType, project.Id));
                        }
                    }
                }
            }
        }
Beispiel #13
0
            /// <summary>
            /// Force create all relevant indices
            /// </summary>
            public static Task PopulateIndicesAsync(Project?project, IImportCompletionCacheService <CacheEntry, object> cacheService, CancellationToken cancellationToken)
            {
                if (project is null)
                {
                    return(Task.CompletedTask);
                }

                using var _ = ArrayBuilder <Task> .GetInstance(out var tasks);

                foreach (var relevantProject in GetAllRelevantProjects(project))
                {
                    tasks.Add(Task.Run(()
                                       => GetCacheEntryAsync(relevantProject, loadOnly: false, cacheService, cancellationToken), cancellationToken));
                }

                foreach (var peReference in GetAllRelevantPeReferences(project))
                {
                    tasks.Add(Task.Run(()
                                       => SymbolTreeInfo.GetInfoForMetadataReferenceAsync(project.Solution, peReference, loadOnly: false, cancellationToken).AsTask(), cancellationToken));
                }

                return(Task.WhenAll(tasks.ToImmutable()));
            }