예제 #1
0
 /// <summary>
 /// Performs validation of options compatibilities and generates diagnostics if needed
 /// </summary>
 protected abstract void ValidateOptions(ArrayBuilder <Diagnostic> builder);
        /// <summary>
        /// For the given set of AssemblyData objects, do the following:
        ///    1) Resolve references from each assembly against other assemblies in the set.
        ///    2) Choose suitable AssemblySymbol instance for each AssemblyData object.
        ///
        /// The first element (index==0) of the assemblies array represents the assembly being built.
        /// One can think about the rest of the items in assemblies array as assembly references given to the compiler to
        /// build executable for the assembly being built.
        /// </summary>
        ///
        /// <param name="explicitAssemblies">
        /// An array of <see cref="AssemblyData"/> objects describing assemblies, for which this method should
        /// resolve references and find suitable AssemblySymbols. The first slot contains the assembly being built.
        /// </param>
        /// <param name="resolverOpt">
        /// Reference resolver used to look up missing assemblies.
        /// </param>
        /// <param name="importOptions">
        /// Import options applied to implicitly resolved references.
        /// </param>
        /// <param name="allAssemblies">
        /// Updated array <paramref name="explicitAssemblies"/> with resolved implicitly referenced assemblies appended.
        /// </param>
        /// <param name="implicitlyResolvedReferences">
        /// Implicitly resolved references.
        /// </param>
        /// <param name="implicitlyResolvedReferenceMap">
        /// Maps indices of implicitly resolved references to the corresponding indices of resolved assemblies in <paramref name="allAssemblies"/> (explicit + implicit).
        /// </param>
        /// <param name="resolutionDiagnostics">
        /// Any diagnostics reported while resolving missing assemblies.
        /// </param>
        /// <param name="hasCircularReference">
        /// True if the assembly being compiled is indirectly referenced through some of its own references.
        /// </param>
        /// <param name="corLibraryIndex">
        /// The definition index of the COR library.
        /// </param>
        /// <return>
        /// An array of <see cref="BoundInputAssembly"/> structures describing the result. It has the same amount of items as
        /// the input assemblies array, <see cref="BoundInputAssembly"/> for each input AssemblyData object resides
        /// at the same position.
        ///
        /// Each <see cref="BoundInputAssembly"/> contains the following data:
        ///
        /// -    Suitable AssemblySymbol instance for the corresponding assembly,
        ///     null reference if none is available/found. Always null for the first element, which corresponds to the assembly being built.
        ///
        /// -    Result of resolving assembly references of the corresponding assembly
        ///     against provided set of assembly definitions. Essentially, this is an array returned by
        ///     <see cref="AssemblyData.BindAssemblyReferences(ImmutableArray{AssemblyData}, AssemblyIdentityComparer)"/> method.
        /// </return>
        protected BoundInputAssembly[] Bind(
            ImmutableArray <AssemblyData> explicitAssemblies,
            MetadataReferenceResolver resolverOpt,
            MetadataImportOptions importOptions,
            out ImmutableArray <AssemblyData> allAssemblies,
            out ImmutableArray <MetadataReference> implicitlyResolvedReferences,
            out ImmutableArray <ResolvedReference> implicitlyResolvedReferenceMap,
            DiagnosticBag resolutionDiagnostics,
            out bool hasCircularReference,
            out int corLibraryIndex)
        {
            Debug.Assert(explicitAssemblies[0] is AssemblyDataForAssemblyBeingBuilt);

            var referenceBindings = ArrayBuilder <AssemblyReferenceBinding[]> .GetInstance();

            try
            {
                // Based on assembly identity, for each assembly,
                // bind its references against the other assemblies we have.
                for (int i = 0; i < explicitAssemblies.Length; i++)
                {
                    referenceBindings.Add(explicitAssemblies[i].BindAssemblyReferences(explicitAssemblies, IdentityComparer));
                }

                if (resolverOpt?.ResolveMissingAssemblies == true)
                {
                    ResolveAndBindMissingAssemblies(explicitAssemblies, resolverOpt, importOptions, referenceBindings, out allAssemblies, out implicitlyResolvedReferences, out implicitlyResolvedReferenceMap, resolutionDiagnostics);
                }
                else
                {
                    allAssemblies = explicitAssemblies;
                    implicitlyResolvedReferences   = ImmutableArray <MetadataReference> .Empty;
                    implicitlyResolvedReferenceMap = ImmutableArray <ResolvedReference> .Empty;
                }

                // implicitly resolved missing assemblies were added to both referenceBindings and assemblies:
                Debug.Assert(referenceBindings.Count == allAssemblies.Length);

                hasCircularReference = CheckCircularReference(referenceBindings);
                corLibraryIndex      = IndexOfCorLibrary(allAssemblies);

                // For each assembly, locate AssemblySymbol with similar reference resolution
                // What does similar mean?
                // Similar means:
                // 1) The same references are resolved against the assemblies that we are given
                //   (or were found duiring implicit assembly resolution).
                // 2) The same assembly is used as the COR library.

                var boundInputs = new BoundInputAssembly[referenceBindings.Count];
                for (int i = 0; i < referenceBindings.Count; i++)
                {
                    boundInputs[i].ReferenceBinding = referenceBindings[i];
                }

                var candidateInputAssemblySymbols = new TAssemblySymbol[allAssemblies.Length];

                // If any assembly from assemblies array refers back to assemblyBeingBuilt,
                // we know that we cannot reuse symbols for any assemblies containing NoPia
                // local types. Because we cannot reuse symbols for assembly referring back
                // to assemblyBeingBuilt.
                if (!hasCircularReference)
                {
                    // Deal with assemblies containing NoPia local types.
                    if (ReuseAssemblySymbolsWithNoPiaLocalTypes(boundInputs, candidateInputAssemblySymbols, allAssemblies, corLibraryIndex))
                    {
                        return(boundInputs);
                    }
                }

                // NoPia shortcut either didn't apply or failed, go through general process
                // of matching candidates.

                ReuseAssemblySymbols(boundInputs, candidateInputAssemblySymbols, allAssemblies, corLibraryIndex);

                return(boundInputs);
            }
            finally
            {
                referenceBindings.Free();
            }
        }
예제 #3
0
        // Expects correct arguments.
        internal CompilationOptions(
            OutputKind outputKind,
            bool reportSuppressedDiagnostics,
            string moduleName,
            string mainTypeName,
            string scriptClassName,
            string cryptoKeyContainer,
            string cryptoKeyFile,
            ImmutableArray <byte> cryptoPublicKey,
            bool?delaySign,
            bool publicSign,
            OptimizationLevel optimizationLevel,
            bool checkOverflow,
            Platform platform,
            ReportDiagnostic generalDiagnosticOption,
            int warningLevel,
            ImmutableDictionary <string, ReportDiagnostic> specificDiagnosticOptions,
            bool concurrentBuild,
            bool deterministic,
            DateTime currentLocalTime,
            bool debugPlusMode,
            XmlReferenceResolver xmlReferenceResolver,
            SourceReferenceResolver sourceReferenceResolver,
            MetadataReferenceResolver metadataReferenceResolver,
            AssemblyIdentityComparer assemblyIdentityComparer,
            StrongNameProvider strongNameProvider,
            MetadataImportOptions metadataImportOptions,
            bool referencesSupersedeLowerVersions)
        {
            this.OutputKind                       = outputKind;
            this.ModuleName                       = moduleName;
            this.MainTypeName                     = mainTypeName;
            this.ScriptClassName                  = scriptClassName ?? WellKnownMemberNames.DefaultScriptClassName;
            this.CryptoKeyContainer               = cryptoKeyContainer;
            this.CryptoKeyFile                    = string.IsNullOrEmpty(cryptoKeyFile) ? null : cryptoKeyFile;
            this.CryptoPublicKey                  = cryptoPublicKey.NullToEmpty();
            this.DelaySign                        = delaySign;
            this.CheckOverflow                    = checkOverflow;
            this.Platform                         = platform;
            this.GeneralDiagnosticOption          = generalDiagnosticOption;
            this.WarningLevel                     = warningLevel;
            this.SpecificDiagnosticOptions        = specificDiagnosticOptions;
            this.ReportSuppressedDiagnostics      = reportSuppressedDiagnostics;
            this.OptimizationLevel                = optimizationLevel;
            this.ConcurrentBuild                  = concurrentBuild;
            this.Deterministic                    = deterministic;
            this.CurrentLocalTime                 = currentLocalTime;
            this.DebugPlusMode                    = debugPlusMode;
            this.XmlReferenceResolver             = xmlReferenceResolver;
            this.SourceReferenceResolver          = sourceReferenceResolver;
            this.MetadataReferenceResolver        = metadataReferenceResolver;
            this.StrongNameProvider               = strongNameProvider;
            this.AssemblyIdentityComparer         = assemblyIdentityComparer ?? AssemblyIdentityComparer.Default;
            this.MetadataImportOptions            = metadataImportOptions;
            this.ReferencesSupersedeLowerVersions = referencesSupersedeLowerVersions;
            this.PublicSign                       = publicSign;

            _lazyErrors = new Lazy <ImmutableArray <Diagnostic> >(() =>
            {
                var builder = ArrayBuilder <Diagnostic> .GetInstance();
                ValidateOptions(builder);
                return(builder.ToImmutableAndFree());
            });
        }
예제 #4
0
        public AnalyzerConfigOptionsResult GetOptionsForSourcePath(string sourcePath)
        {
            if (sourcePath == null)
            {
                throw new ArgumentNullException(nameof(sourcePath));
            }

            var treeOptionsBuilder     = _treeOptionsPool.Allocate();
            var analyzerOptionsBuilder = _analyzerOptionsPool.Allocate();
            var diagnosticBuilder      = ArrayBuilder <Diagnostic> .GetInstance();

            var sectionKey = _sectionKeyPool.Allocate();

            var normalizedPath = PathUtilities.NormalizeWithForwardSlash(sourcePath);

            // The editorconfig paths are sorted from shortest to longest, so matches
            // are resolved from most nested to least nested, where last setting wins
            for (int analyzerConfigIndex = 0; analyzerConfigIndex < _analyzerConfigs.Length; analyzerConfigIndex++)
            {
                var config = _analyzerConfigs[analyzerConfigIndex];

                if (normalizedPath.StartsWith(config.NormalizedDirectory, StringComparison.Ordinal))
                {
                    // If this config is a root config, then clear earlier options since they don't apply
                    // to this source file.
                    if (config.IsRoot)
                    {
                        analyzerOptionsBuilder.Clear();
                        treeOptionsBuilder.Clear();
                        diagnosticBuilder.Clear();
                    }

                    int dirLength = config.NormalizedDirectory.Length;
                    // Leave '/' if the normalized directory ends with a '/'. This can happen if
                    // we're in a root directory (e.g. '/' or 'Z:/'). The section matching
                    // always expects that the relative path start with a '/'.
                    if (config.NormalizedDirectory[dirLength - 1] == '/')
                    {
                        dirLength--;
                    }
                    string relativePath = normalizedPath.Substring(dirLength);

                    ImmutableArray <SectionNameMatcher?> matchers = _analyzerMatchers[analyzerConfigIndex];
                    for (int sectionIndex = 0; sectionIndex < matchers.Length; sectionIndex++)
                    {
                        if (matchers[sectionIndex]?.IsMatch(relativePath) == true)
                        {
                            var section = config.NamedSections[sectionIndex];
                            addOptions(
                                section,
                                treeOptionsBuilder,
                                analyzerOptionsBuilder,
                                diagnosticBuilder,
                                config.PathToFile,
                                _diagnosticIdCache);
                            sectionKey.Add(section);
                        }
                    }
                }
            }

            // Try to avoid creating extra dictionaries if we've already seen an options result with the
            // exact same options
            if (!_optionsCache.TryGetValue(sectionKey, out var result))
            {
                result = new AnalyzerConfigOptionsResult(
                    treeOptionsBuilder.Count > 0 ? treeOptionsBuilder.ToImmutable() : SyntaxTree.EmptyDiagnosticOptions,
                    analyzerOptionsBuilder.Count > 0 ? analyzerOptionsBuilder.ToImmutable() : AnalyzerConfigOptions.EmptyDictionary,
                    diagnosticBuilder.ToImmutableAndFree());
                if (_optionsCache.TryAdd(sectionKey, result))
                {
                    // Release the pooled object to be used as a key
                    _sectionKeyPool.ForgetTrackedObject(sectionKey);
                }
                else
                {
                    freeKey(sectionKey, _sectionKeyPool);
                }
            }
            else
            {
                freeKey(sectionKey, _sectionKeyPool);
            }

            treeOptionsBuilder.Clear();
            analyzerOptionsBuilder.Clear();
            _treeOptionsPool.Free(treeOptionsBuilder);
            _analyzerOptionsPool.Free(analyzerOptionsBuilder);

            return(result);
        private static void UpdateBindingsOfAssemblyBeingBuilt(ArrayBuilder <AssemblyReferenceBinding[]> referenceBindings, int explicitAssemblyCount, ArrayBuilder <AssemblyData> implicitAssemblies)
        {
            var referenceBindingsOfAssemblyBeingBuilt = referenceBindings[0];

            // add implicitly resolved assemblies to the bindings of the assembly being built:
            var bindingsOfAssemblyBeingBuilt = ArrayBuilder <AssemblyReferenceBinding> .GetInstance(referenceBindingsOfAssemblyBeingBuilt.Length + implicitAssemblies.Count);

            // add bindings for explicitly specified assemblies (-1 for the assembly being built):
            bindingsOfAssemblyBeingBuilt.AddRange(referenceBindingsOfAssemblyBeingBuilt, explicitAssemblyCount - 1);

            // add bindings for implicitly resolved assemblies:
            for (int i = 0; i < implicitAssemblies.Count; i++)
            {
                bindingsOfAssemblyBeingBuilt.Add(new AssemblyReferenceBinding(implicitAssemblies[i].Identity, explicitAssemblyCount + i));
            }

            // add bindings for assemblies referenced by modules added to the compilation:
            bindingsOfAssemblyBeingBuilt.AddRange(referenceBindingsOfAssemblyBeingBuilt, explicitAssemblyCount - 1, referenceBindingsOfAssemblyBeingBuilt.Length - explicitAssemblyCount + 1);

            referenceBindings[0] = bindingsOfAssemblyBeingBuilt.ToArrayAndFree();
        }
예제 #6
0
        internal static void CheckAssemblyOrModuleName(string name, CommonMessageProvider messageProvider, int code, ArrayBuilder <Diagnostic> builder)
        {
            string errorArgumentResourceId = GetAssemblyOrModuleNameErrorArgumentResourceName(name);

            if (errorArgumentResourceId != null)
            {
                builder.Add(
                    messageProvider.CreateDiagnostic(code, Location.None,
                                                     new CodeAnalysisResourcesLocalizableErrorArgument(errorArgumentResourceId)));
            }
        }
예제 #7
0
 public void AddRange(ArrayBuilder <T> items)
 {
     _builder.AddRange(items._builder);
 }
예제 #8
0
 /// <summary>
 /// Takes a node and returns a set of declarations that overlap the node's span.
 /// </summary>
 internal abstract void ComputeDeclarationsInNode(SyntaxNode node, bool getSymbol, ArrayBuilder<DeclarationInfo> builder, CancellationToken cancellationToken, int? levelsToCompute = null);
예제 #9
0
        internal void ResolveAnalyzersFromArguments(
            string language,
            List <DiagnosticInfo> diagnostics,
            CommonMessageProvider messageProvider,
            IAnalyzerAssemblyLoader analyzerLoader,
            bool skipAnalyzers,
            out ImmutableArray <DiagnosticAnalyzer> analyzers,
            out ImmutableArray <ISourceGenerator> generators)
        {
            var analyzerBuilder  = ImmutableArray.CreateBuilder <DiagnosticAnalyzer>();
            var generatorBuilder = ImmutableArray.CreateBuilder <ISourceGenerator>();

            EventHandler <AnalyzerLoadFailureEventArgs> errorHandler = (o, e) =>
            {
                var analyzerReference = o as AnalyzerFileReference;
                RoslynDebug.Assert(analyzerReference is object);
                DiagnosticInfo?diagnostic;
                switch (e.ErrorCode)
                {
                case AnalyzerLoadFailureEventArgs.FailureErrorCode.UnableToLoadAnalyzer:
                    diagnostic = new DiagnosticInfo(messageProvider, messageProvider.WRN_UnableToLoadAnalyzer, analyzerReference.FullPath, e.Message);
                    break;

                case AnalyzerLoadFailureEventArgs.FailureErrorCode.UnableToCreateAnalyzer:
                    diagnostic = new DiagnosticInfo(messageProvider, messageProvider.WRN_AnalyzerCannotBeCreated, e.TypeName ?? "", analyzerReference.FullPath, e.Message);
                    break;

                case AnalyzerLoadFailureEventArgs.FailureErrorCode.NoAnalyzers:
                    diagnostic = new DiagnosticInfo(messageProvider, messageProvider.WRN_NoAnalyzerInAssembly, analyzerReference.FullPath);
                    break;

                case AnalyzerLoadFailureEventArgs.FailureErrorCode.None:
                default:
                    return;
                }

                // Filter this diagnostic based on the compilation options so that /nowarn and /warnaserror etc. take effect.
                diagnostic = messageProvider.FilterDiagnosticInfo(diagnostic, this.CompilationOptions);

                if (diagnostic != null)
                {
                    diagnostics.Add(diagnostic);
                }
            };

            var resolvedReferences = ArrayBuilder <AnalyzerFileReference> .GetInstance();

            foreach (var reference in AnalyzerReferences)
            {
                var resolvedReference = ResolveAnalyzerReference(reference, analyzerLoader);
                if (resolvedReference != null)
                {
                    resolvedReferences.Add(resolvedReference);

                    // register the reference to the analyzer loader:
                    analyzerLoader.AddDependencyLocation(resolvedReference.FullPath);
                }
                else
                {
                    diagnostics.Add(new DiagnosticInfo(messageProvider, messageProvider.ERR_MetadataFileNotFound, reference.FilePath));
                }
            }

            // All analyzer references are registered now, we can start loading them:
            foreach (var resolvedReference in resolvedReferences)
            {
                resolvedReference.AnalyzerLoadFailed += errorHandler;
                if (!skipAnalyzers)
                {
                    resolvedReference.AddAnalyzers(analyzerBuilder, language);
                }
                resolvedReference.AddGenerators(generatorBuilder, language);
                resolvedReference.AnalyzerLoadFailed -= errorHandler;
            }

            resolvedReferences.Free();

            generators = generatorBuilder.ToImmutable();
            analyzers  = analyzerBuilder.ToImmutable();
        }
        protected void GetCompilationReferences(
            TCompilation compilation,
            DiagnosticBag diagnostics,
            out ImmutableArray <MetadataReference> references,
            out IDictionary <ValueTuple <string, string>, MetadataReference> boundReferenceDirectives,
            out ImmutableArray <Location> referenceDirectiveLocations)
        {
            boundReferenceDirectives = null;

            ArrayBuilder <MetadataReference> referencesBuilder = ArrayBuilder <MetadataReference> .GetInstance();

            ArrayBuilder <Location> referenceDirectiveLocationsBuilder = null;

            try
            {
                foreach (var referenceDirective in compilation.ReferenceDirectives)
                {
                    if (compilation.Options.MetadataReferenceResolver == null)
                    {
                        diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_MetadataReferencesNotSupported, referenceDirective.Location));
                        break;
                    }

                    // we already successfully bound #r with the same value:
                    if (boundReferenceDirectives != null && boundReferenceDirectives.ContainsKey(ValueTuple.Create(referenceDirective.Location.SourceTree.FilePath, referenceDirective.File)))
                    {
                        continue;
                    }

                    MetadataReference boundReference = ResolveReferenceDirective(referenceDirective.File, referenceDirective.Location, compilation);
                    if (boundReference == null)
                    {
                        diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_MetadataFileNotFound, referenceDirective.Location, referenceDirective.File));
                        continue;
                    }

                    if (boundReferenceDirectives == null)
                    {
                        boundReferenceDirectives           = new Dictionary <ValueTuple <string, string>, MetadataReference>();
                        referenceDirectiveLocationsBuilder = ArrayBuilder <Location> .GetInstance();
                    }

                    referencesBuilder.Add(boundReference);
                    referenceDirectiveLocationsBuilder.Add(referenceDirective.Location);
                    boundReferenceDirectives.Add(ValueTuple.Create(referenceDirective.Location.SourceTree.FilePath, referenceDirective.File), boundReference);
                }

                // add external reference at the end, so that they are processed first:
                referencesBuilder.AddRange(compilation.ExternalReferences);

                // Add all explicit references of the previous script compilation.
                var previousScriptCompilation = compilation.ScriptCompilationInfo?.PreviousScriptCompilation;
                if (previousScriptCompilation != null)
                {
                    referencesBuilder.AddRange(previousScriptCompilation.GetBoundReferenceManager().ExplicitReferences);
                }

                if (boundReferenceDirectives == null)
                {
                    // no directive references resolved successfully:
                    boundReferenceDirectives = SpecializedCollections.EmptyDictionary <ValueTuple <string, string>, MetadataReference>();
                }

                references = referencesBuilder.ToImmutable();
                referenceDirectiveLocations = referenceDirectiveLocationsBuilder?.ToImmutableAndFree() ?? ImmutableArray <Location> .Empty;
            }
            finally
            {
                // Put this in a finally because we have tests that (intentionally) cause ResolveReferenceDirective to throw and
                // we don't want to clutter the test output with leak reports.
                referencesBuilder.Free();
            }
        }
예제 #11
0
 /// <summary>
 /// Gets the <see cref="DeclarationInfo"/> for all the declarations whose span overlaps with the given <paramref name="span"/>.
 /// </summary>
 /// <param name="span">Span to get declarations.</param>
 /// <param name="getSymbol">Flag indicating whether <see cref="DeclarationInfo.DeclaredSymbol"/> should be computed for the returned declaration infos.
 /// If false, then <see cref="DeclarationInfo.DeclaredSymbol"/> is always null.</param>
 /// <param name="builder">Builder to add declarations.</param>
 /// <param name="cancellationToken">Cancellation token.</param>
 internal abstract void ComputeDeclarationsInSpan(TextSpan span, bool getSymbol, ArrayBuilder<DeclarationInfo> builder, CancellationToken cancellationToken);
        /// <remarks>
        /// Caller is responsible for freeing any allocated ArrayBuilders.
        /// </remarks>
        private static void AddModule(PEModule module, int referenceIndex, ResolvedReference[] referenceMap, ref ArrayBuilder <PEModule> modules)
        {
            if (modules == null)
            {
                modules = ArrayBuilder <PEModule> .GetInstance();
            }

            referenceMap[referenceIndex] = new ResolvedReference(modules.Count, MetadataImageKind.Module);
            modules.Add(module);
        }
 /// <remarks>
 /// Caller is responsible for freeing any allocated ArrayBuilders.
 /// </remarks>
 private static void AddAssembly(AssemblyData data, int referenceIndex, ResolvedReference[] referenceMap, ArrayBuilder <AssemblyData> assemblies)
 {
     // aliases will be filled in later:
     referenceMap[referenceIndex] = new ResolvedReference(assemblies.Count, MetadataImageKind.Assembly);
     assemblies.Add(data);
 }
        /// <summary>
        /// Resolves given metadata references to assemblies and modules.
        /// </summary>
        /// <param name="compilation">The compilation whose references are being resolved.</param>
        /// <param name="assemblyReferencesBySimpleName">
        /// Used to filter out assemblies that have the same strong or weak identity.
        /// Maps simple name to a list of identities. The highest version of each name is the first.
        /// </param>
        /// <param name="references">List where to store resolved references. References from #r directives will follow references passed to the compilation constructor.</param>
        /// <param name="boundReferenceDirectiveMap">Maps #r values to successfully resolved metadata references. Does not contain values that failed to resolve.</param>
        /// <param name="boundReferenceDirectives">Unique metadata references resolved from #r directives.</param>
        /// <param name="assemblies">List where to store information about resolved assemblies to.</param>
        /// <param name="modules">List where to store information about resolved modules to.</param>
        /// <param name="diagnostics">Diagnostic bag where to report resolution errors.</param>
        /// <returns>
        /// Maps index to <paramref name="references"/> to an index of a resolved assembly or module in <paramref name="assemblies"/> or <paramref name="modules"/>, respectively.
        ///</returns>
        protected ImmutableArray <ResolvedReference> ResolveMetadataReferences(
            TCompilation compilation,
            [Out] Dictionary <string, List <ReferencedAssemblyIdentity> > assemblyReferencesBySimpleName,
            out ImmutableArray <MetadataReference> references,
            out IDictionary <ValueTuple <string, string>, MetadataReference> boundReferenceDirectiveMap,
            out ImmutableArray <MetadataReference> boundReferenceDirectives,
            out ImmutableArray <AssemblyData> assemblies,
            out ImmutableArray <PEModule> modules,
            DiagnosticBag diagnostics)
        {
            // Locations of all #r directives in the order they are listed in the references list.
            ImmutableArray <Location> referenceDirectiveLocations;

            GetCompilationReferences(compilation, diagnostics, out references, out boundReferenceDirectiveMap, out referenceDirectiveLocations);

            // References originating from #r directives precede references supplied as arguments of the compilation.
            int referenceCount          = references.Length;
            int referenceDirectiveCount = (referenceDirectiveLocations != null ? referenceDirectiveLocations.Length : 0);

            var referenceMap = new ResolvedReference[referenceCount];

            // Maps references that were added to the reference set (i.e. not filtered out as duplicates) to a set of names that
            // can be used to alias these references. Duplicate assemblies contribute their aliases into this set.
            Dictionary <MetadataReference, MergedAliases> lazyAliasMap = null;

            // Used to filter out duplicate references that reference the same file (resolve to the same full normalized path).
            var boundReferences = new Dictionary <MetadataReference, MetadataReference>(MetadataReferenceEqualityComparer.Instance);

            ArrayBuilder <MetadataReference> uniqueDirectiveReferences = (referenceDirectiveLocations != null) ? ArrayBuilder <MetadataReference> .GetInstance() : null;

            var assembliesBuilder = ArrayBuilder <AssemblyData> .GetInstance();

            ArrayBuilder <PEModule> lazyModulesBuilder = null;

            bool supersedeLowerVersions = compilation.IsSubmission;

            // When duplicate references with conflicting EmbedInteropTypes flag are encountered,
            // VB uses the flag from the last one, C# reports an error. We need to enumerate in reverse order
            // so that we find the one that matters first.
            for (int referenceIndex = referenceCount - 1; referenceIndex >= 0; referenceIndex--)
            {
                var boundReference = references[referenceIndex];
                if (boundReference == null)
                {
                    continue;
                }

                // add bound reference if it doesn't exist yet, merging aliases:
                MetadataReference existingReference;
                if (boundReferences.TryGetValue(boundReference, out existingReference))
                {
                    // merge properties of compilation-based references if the underlying compilations are the same
                    if ((object)boundReference != existingReference)
                    {
                        MergeReferenceProperties(existingReference, boundReference, diagnostics, ref lazyAliasMap);
                    }

                    continue;
                }

                boundReferences.Add(boundReference, boundReference);

                Location location;
                if (referenceIndex < referenceDirectiveCount)
                {
                    location = referenceDirectiveLocations[referenceIndex];
                    uniqueDirectiveReferences.Add(boundReference);
                }
                else
                {
                    location = Location.None;
                }

                // compilation reference

                var compilationReference = boundReference as CompilationReference;
                if (compilationReference != null)
                {
                    switch (compilationReference.Properties.Kind)
                    {
                    case MetadataImageKind.Assembly:
                        existingReference = TryAddAssembly(
                            compilationReference.Compilation.Assembly.Identity,
                            boundReference,
                            -assembliesBuilder.Count - 1,
                            diagnostics,
                            location,
                            assemblyReferencesBySimpleName,
                            supersedeLowerVersions);

                        if (existingReference != null)
                        {
                            MergeReferenceProperties(existingReference, boundReference, diagnostics, ref lazyAliasMap);
                            continue;
                        }

                        // Note, if SourceAssemblySymbol hasn't been created for
                        // compilationAssembly.Compilation yet, we want this to happen
                        // right now. Conveniently, this constructor will trigger creation of the
                        // SourceAssemblySymbol.
                        var asmData = CreateAssemblyDataForCompilation(compilationReference);
                        AddAssembly(asmData, referenceIndex, referenceMap, assembliesBuilder);
                        break;

                    default:
                        throw ExceptionUtilities.UnexpectedValue(compilationReference.Properties.Kind);
                    }

                    continue;
                }

                // PE reference

                var      peReference = (PortableExecutableReference)boundReference;
                Metadata metadata    = GetMetadata(peReference, MessageProvider, location, diagnostics);
                Debug.Assert(metadata != null || diagnostics.HasAnyErrors());

                if (metadata != null)
                {
                    Debug.Assert(metadata != null);

                    switch (peReference.Properties.Kind)
                    {
                    case MetadataImageKind.Assembly:
                        var assemblyMetadata = (AssemblyMetadata)metadata;
                        WeakList <IAssemblySymbol> cachedSymbols = assemblyMetadata.CachedSymbols;

                        if (assemblyMetadata.IsValidAssembly())
                        {
                            PEAssembly assembly = assemblyMetadata.GetAssembly();
                            existingReference = TryAddAssembly(
                                assembly.Identity,
                                peReference,
                                -assembliesBuilder.Count - 1,
                                diagnostics,
                                location,
                                assemblyReferencesBySimpleName,
                                supersedeLowerVersions);

                            if (existingReference != null)
                            {
                                MergeReferenceProperties(existingReference, boundReference, diagnostics, ref lazyAliasMap);
                                continue;
                            }

                            var asmData = CreateAssemblyDataForFile(
                                assembly,
                                cachedSymbols,
                                peReference.DocumentationProvider,
                                SimpleAssemblyName,
                                compilation.Options.MetadataImportOptions,
                                peReference.Properties.EmbedInteropTypes);

                            AddAssembly(asmData, referenceIndex, referenceMap, assembliesBuilder);
                        }
                        else
                        {
                            diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_MetadataFileNotAssembly, location, peReference.Display));
                        }

                        // asmData keeps strong ref after this point
                        GC.KeepAlive(assemblyMetadata);
                        break;

                    case MetadataImageKind.Module:
                        var moduleMetadata = (ModuleMetadata)metadata;
                        if (moduleMetadata.Module.IsLinkedModule)
                        {
                            // We don't support netmodules since some checks in the compiler need information from the full PE image
                            // (Machine, Bit32Required, PE image hash).
                            if (!moduleMetadata.Module.IsEntireImageAvailable)
                            {
                                diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_LinkedNetmoduleMetadataMustProvideFullPEImage, location, peReference.Display));
                            }

                            AddModule(moduleMetadata.Module, referenceIndex, referenceMap, ref lazyModulesBuilder);
                        }
                        else
                        {
                            diagnostics.Add(MessageProvider.CreateDiagnostic(MessageProvider.ERR_MetadataFileNotModule, location, peReference.Display));
                        }
                        break;

                    default:
                        throw ExceptionUtilities.UnexpectedValue(peReference.Properties.Kind);
                    }
                }
            }

            if (uniqueDirectiveReferences != null)
            {
                uniqueDirectiveReferences.ReverseContents();
                boundReferenceDirectives = uniqueDirectiveReferences.ToImmutableAndFree();
            }
            else
            {
                boundReferenceDirectives = ImmutableArray <MetadataReference> .Empty;
            }

            // We enumerated references in reverse order in the above code
            // and thus assemblies and modules in the builders are reversed.
            // Fix up all the indices and reverse the builder content now to get
            // the ordering matching the references.
            //
            // Also fills in aliases.

            for (int i = 0; i < referenceMap.Length; i++)
            {
                if (!referenceMap[i].IsSkipped)
                {
                    int count = (referenceMap[i].Kind == MetadataImageKind.Assembly) ? assembliesBuilder.Count : lazyModulesBuilder?.Count ?? 0;

                    int reversedIndex = count - 1 - referenceMap[i].Index;
                    referenceMap[i] = GetResolvedReferenceAndFreePropertyMapEntry(references[i], reversedIndex, referenceMap[i].Kind, lazyAliasMap);
                }
            }

            assembliesBuilder.ReverseContents();
            assemblies = assembliesBuilder.ToImmutableAndFree();

            if (lazyModulesBuilder == null)
            {
                modules = ImmutableArray <PEModule> .Empty;
            }
            else
            {
                lazyModulesBuilder.ReverseContents();
                modules = lazyModulesBuilder.ToImmutableAndFree();
            }

            return(ImmutableArray.CreateRange(referenceMap));
        }
예제 #15
0
            /// <summary>
            /// Decodes a type name.  A type name is a string which is terminated by the end of the string or one of the
            /// delimiters '+', ',', '[', ']'. '+' separates nested classes. '[' and ']'
            /// enclosed generic type arguments.  ',' separates types.
            /// </summary>
            internal AssemblyQualifiedTypeName DecodeTypeName(bool isTypeArgument = false, bool isTypeArgumentWithAssemblyName = false)
            {
                Debug.Assert(!isTypeArgumentWithAssemblyName || isTypeArgument);

                string topLevelType = null;
                ArrayBuilder <string> nestedTypesBuilder = null;

                AssemblyQualifiedTypeName[] typeArguments = null;
                int pointerCount = 0;
                ArrayBuilder <int> arrayRanksBuilder = null;
                string             assemblyName      = null;
                bool decodingTopLevelType            = true;
                bool isGenericTypeName = false;

                var           pooledStrBuilder = PooledStringBuilder.GetInstance();
                StringBuilder typeNameBuilder  = pooledStrBuilder.Builder;

                while (!EndOfInput)
                {
                    int i = _input.IndexOfAny(s_typeNameDelimiters, _offset);
                    if (i >= 0)
                    {
                        char c = _input[i];

                        // Found name, which could be a generic name with arity.
                        // Generic type parameter count, if any, are handled in DecodeGenericName.
                        string decodedString = DecodeGenericName(i);
                        Debug.Assert(decodedString != null);

                        // Type name is generic if the decoded name of the top level type OR any of the outer types of a nested type had the '`' character.
                        isGenericTypeName = isGenericTypeName || decodedString.IndexOf(GenericTypeNameManglingChar) >= 0;
                        typeNameBuilder.Append(decodedString);

                        switch (c)
                        {
                        case '*':
                            if (arrayRanksBuilder != null)
                            {
                                // Error case, array shape must be specified at the end of the type name.
                                // Process as a regular character and continue.
                                typeNameBuilder.Append(c);
                            }
                            else
                            {
                                pointerCount++;
                            }

                            Advance();
                            break;

                        case '+':
                            if (arrayRanksBuilder != null || pointerCount > 0)
                            {
                                // Error case, array shape must be specified at the end of the type name.
                                // Process as a regular character and continue.
                                typeNameBuilder.Append(c);
                            }
                            else
                            {
                                // Type followed by nested type. Handle nested class separator and collect the nested types.
                                HandleDecodedTypeName(typeNameBuilder.ToString(), decodingTopLevelType, ref topLevelType, ref nestedTypesBuilder);
                                typeNameBuilder.Clear();
                                decodingTopLevelType = false;
                            }

                            Advance();
                            break;

                        case '[':
                            // Is type followed by generic type arguments?
                            if (isGenericTypeName && typeArguments == null)
                            {
                                Advance();
                                if (arrayRanksBuilder != null || pointerCount > 0)
                                {
                                    // Error case, array shape must be specified at the end of the type name.
                                    // Process as a regular character and continue.
                                    typeNameBuilder.Append(c);
                                }
                                else
                                {
                                    // Decode type arguments.
                                    typeArguments = DecodeTypeArguments();
                                }
                            }
                            else
                            {
                                // Decode array shape.
                                DecodeArrayShape(typeNameBuilder, ref arrayRanksBuilder);
                            }

                            break;

                        case ']':
                            if (isTypeArgument)
                            {
                                // End of type arguments.  This occurs when the last type argument is a type in the
                                // current assembly.
                                goto ExitDecodeTypeName;
                            }
                            else
                            {
                                // Error case, process as a regular character and continue.
                                typeNameBuilder.Append(c);
                                Advance();
                                break;
                            }

                        case ',':
                            // A comma may separate a type name from its assembly name or a type argument from
                            // another type argument.
                            // If processing non-type argument or a type argument with assembly name,
                            // process the characters after the comma as an assembly name.
                            if (!isTypeArgument || isTypeArgumentWithAssemblyName)
                            {
                                Advance();
                                if (!EndOfInput && Char.IsWhiteSpace(Current))
                                {
                                    Advance();
                                }

                                assemblyName = DecodeAssemblyName(isTypeArgumentWithAssemblyName);
                            }
                            goto ExitDecodeTypeName;

                        default:
                            throw ExceptionUtilities.UnexpectedValue(c);
                        }
                    }
                    else
                    {
                        typeNameBuilder.Append(DecodeGenericName(_input.Length));
                        goto ExitDecodeTypeName;
                    }
                }

ExitDecodeTypeName:
                HandleDecodedTypeName(typeNameBuilder.ToString(), decodingTopLevelType, ref topLevelType, ref nestedTypesBuilder);
                pooledStrBuilder.Free();

                return(new AssemblyQualifiedTypeName(
                           topLevelType,
                           nestedTypesBuilder?.ToArrayAndFree(),
                           typeArguments,
                           pointerCount,
                           arrayRanksBuilder?.ToArrayAndFree(),
                           assemblyName));
            }
예제 #16
0
        public ImmutableArray <DynamicAnalysisSpan> GetSpans(BlobHandle handle)
        {
            if (handle.IsNil)
            {
                return(ImmutableArray <DynamicAnalysisSpan> .Empty);
            }

            var builder = ArrayBuilder <DynamicAnalysisSpan> .GetInstance();

            var reader = GetBlobReader(handle);

            // header:
            int    documentRowId       = ReadDocumentRowId(ref reader);
            int    previousStartLine   = -1;
            ushort previousStartColumn = 0;

            // records:
            while (reader.RemainingBytes > 0)
            {
                ReadDeltaLinesAndColumns(ref reader, out var deltaLines, out var deltaColumns);

                // document:
                if (deltaLines == 0 && deltaColumns == 0)
                {
                    documentRowId = ReadDocumentRowId(ref reader);
                    continue;
                }

                int    startLine;
                ushort startColumn;

                // delta Start Line & Column:
                if (previousStartLine < 0)
                {
                    Debug.Assert(previousStartColumn == 0);

                    startLine   = ReadLine(ref reader);
                    startColumn = ReadColumn(ref reader);
                }
                else
                {
                    startLine   = AddLines(previousStartLine, reader.ReadCompressedSignedInteger());
                    startColumn = AddColumns(
                        previousStartColumn,
                        reader.ReadCompressedSignedInteger()
                        );
                }

                previousStartLine   = startLine;
                previousStartColumn = startColumn;

                int endLine          = AddLines(startLine, deltaLines);
                int endColumn        = AddColumns(startColumn, deltaColumns);
                var linePositionSpan = new DynamicAnalysisSpan(
                    documentRowId,
                    startLine,
                    startColumn,
                    endLine,
                    endColumn
                    );
                builder.Add(linePositionSpan);
            }

            return(builder.ToImmutableAndFree());
        }
예제 #17
0
            private static void HandleDecodedTypeName(string decodedTypeName, bool decodingTopLevelType, ref string topLevelType, ref ArrayBuilder <string> nestedTypesBuilder)
            {
                if (decodedTypeName.Length != 0)
                {
                    if (decodingTopLevelType)
                    {
                        Debug.Assert(topLevelType == null);
                        topLevelType = decodedTypeName;
                    }
                    else
                    {
                        if (nestedTypesBuilder == null)
                        {
                            nestedTypesBuilder = ArrayBuilder <string> .GetInstance();
                        }

                        nestedTypesBuilder.Add(decodedTypeName);
                    }
                }
            }
예제 #18
0
        public DynamicAnalysisDataReader(byte *buffer, int size)
        {
            var reader = new BlobReader(buffer, size);

            // header:
            if (
                reader.ReadByte() != 'D' ||
                reader.ReadByte() != 'A' ||
                reader.ReadByte() != 'M' ||
                reader.ReadByte() != 'D'
                )
            {
                throw new BadImageFormatException();
            }

            // version
            byte major = reader.ReadByte();
            byte minor = reader.ReadByte();

            if (major != 0 || minor < 1 || minor > 2)
            {
                throw new NotSupportedException();
            }

            // table sizes:
            int documentRowCount   = reader.ReadInt32();
            int methodSpanRowCount = reader.ReadInt32();

            // blob heap sizes:
            int stringHeapSize     = (minor == 1) ? reader.ReadInt32() : 0;
            int userStringHeapSize = (minor == 1) ? reader.ReadInt32() : 0;
            int guidHeapSize       = reader.ReadInt32();
            int blobHeapSize       = reader.ReadInt32();

            // TODO: check size ranges

            bool isBlobHeapSmall = blobHeapSize <= ushort.MaxValue;
            bool isGuidHeapSmall = guidHeapSize / GuidSize <= ushort.MaxValue;

            var documentsBuilder = ArrayBuilder <DynamicAnalysisDocument> .GetInstance(
                documentRowCount
                );

            for (int i = 0; i < documentRowCount; i++)
            {
                var name          = MetadataTokens.BlobHandle(ReadReference(ref reader, isBlobHeapSmall));
                var hashAlgorithm = MetadataTokens.GuidHandle(
                    ReadReference(ref reader, isGuidHeapSmall)
                    );
                var hash = MetadataTokens.BlobHandle(ReadReference(ref reader, isBlobHeapSmall));

                documentsBuilder.Add(new DynamicAnalysisDocument(name, hashAlgorithm, hash));
            }

            Documents = documentsBuilder.ToImmutableAndFree();

            var methodsBuilder = ArrayBuilder <DynamicAnalysisMethod> .GetInstance(
                methodSpanRowCount
                );

            for (int i = 0; i < methodSpanRowCount; i++)
            {
                methodsBuilder.Add(
                    new DynamicAnalysisMethod(
                        MetadataTokens.BlobHandle(ReadReference(ref reader, isBlobHeapSmall))
                        )
                    );
            }

            Methods = methodsBuilder.ToImmutableAndFree();

            int stringHeapOffset     = reader.Offset;
            int userStringHeapOffset = stringHeapOffset + stringHeapSize;
            int guidHeapOffset       = userStringHeapOffset + userStringHeapSize;
            int blobHeapOffset       = guidHeapOffset + guidHeapSize;

            if (reader.Length != blobHeapOffset + blobHeapSize)
            {
                throw new BadImageFormatException();
            }

            _guidHeapBlob = new Blob(buffer + guidHeapOffset, guidHeapSize);
            _blobHeapBlob = new Blob(buffer + blobHeapOffset, blobHeapSize);
        }
예제 #19
0
 public DebuggerProxy(ArrayBuilder <T> builder)
 {
     _builder = builder;
 }
        private void ResolveAndBindMissingAssemblies(
            ImmutableArray <AssemblyData> explicitAssemblies,
            MetadataReferenceResolver resolver,
            MetadataImportOptions importOptions,
            [In, Out] ArrayBuilder <AssemblyReferenceBinding[]> referenceBindings,
            out ImmutableArray <AssemblyData> allAssemblies,
            out ImmutableArray <MetadataReference> metadataReferences,
            out ImmutableArray <ResolvedReference> resolvedReferences,
            DiagnosticBag resolutionDiagnostics)
        {
            Debug.Assert(explicitAssemblies[0] is AssemblyDataForAssemblyBeingBuilt);

            var implicitAssemblies = ArrayBuilder <AssemblyData> .GetInstance();

            // tracks identities we already asked the resolver to resolve:
            var requestedIdentities = PooledHashSet <AssemblyIdentity> .GetInstance();

            // reference bindings of implicit assemblies, used to calculate a fixed point:
            var referenceBindingsToProcess = ArrayBuilder <AssemblyReferenceBinding[]> .GetInstance();

            var metadataReferencesBuilder = ArrayBuilder <MetadataReference> .GetInstance();

            Dictionary <string, List <ReferencedAssemblyIdentity> > lazyResolvedReferencesBySimpleName = null;
            Dictionary <MetadataReference, ArrayBuilder <string> >  lazyAliasMap = null;

            try
            {
                // collect all missing identities, resolve the assemblies and bind their references against explicit definitions:
                referenceBindingsToProcess.AddRange(referenceBindings);

                while (referenceBindingsToProcess.Count > 0)
                {
                    foreach (var binding in referenceBindingsToProcess.Pop())
                    {
                        // only attempt to resolve unbound references (regardless of version difference of the bound ones)
                        if (binding.IsBound)
                        {
                            continue;
                        }

                        if (!requestedIdentities.Add(binding.ReferenceIdentity))
                        {
                            continue;
                        }

                        var peReference = resolver.ResolveMissingAssembly(binding.ReferenceIdentity);
                        if (peReference == null)
                        {
                            continue;
                        }

                        var data = ResolveMissingAssembly(binding.ReferenceIdentity, peReference, importOptions, resolutionDiagnostics);
                        if (data == null)
                        {
                            continue;
                        }

                        // The resolver may return different version than we asked for, so it may happen that
                        // it returns the same identity for two different input identities (e.g. if a higher version
                        // of an assembly is available than what the assemblies reference: "A, v1" -> "A, v3" and "A, v2" -> "A, v3").
                        // If such case occurs merge the properties (aliases) of the resulting references in the same way we do
                        // during initial explicit references resolution.

                        var existingReference = TryAddAssembly(data.Identity, peReference, resolutionDiagnostics, Location.None, ref lazyResolvedReferencesBySimpleName);
                        if (existingReference != null)
                        {
                            MergeReferenceProperties(existingReference, peReference, resolutionDiagnostics, ref lazyAliasMap);
                            continue;
                        }

                        metadataReferencesBuilder.Add(peReference);
                        implicitAssemblies.Add(data);

                        var referenceBinding = data.BindAssemblyReferences(explicitAssemblies, IdentityComparer);
                        referenceBindings.Add(referenceBinding);
                        referenceBindingsToProcess.Push(referenceBinding);
                    }
                }

                if (implicitAssemblies.Count == 0)
                {
                    Debug.Assert(lazyAliasMap == null);
                    Debug.Assert(lazyResolvedReferencesBySimpleName == null);

                    resolvedReferences = ImmutableArray <ResolvedReference> .Empty;
                    metadataReferences = ImmutableArray <MetadataReference> .Empty;
                    allAssemblies      = explicitAssemblies;
                    return;
                }

                // Rebind assembly references that were initially missing. All bindings established above
                // are against explicitly specified references.

                // NB: includes the assembly being built:
                int explicitAssemblyCount = explicitAssemblies.Length;
                allAssemblies = explicitAssemblies.AddRange(implicitAssemblies);

                for (int bindingsIndex = 0; bindingsIndex < referenceBindings.Count; bindingsIndex++)
                {
                    var referenceBinding = referenceBindings[bindingsIndex];

                    for (int i = 0; i < referenceBinding.Length; i++)
                    {
                        var binding = referenceBinding[i];

                        // We don't rebind references bound to a non-matching version of a reference that was explicitly
                        // specified, even if we have a better version now.
                        if (binding.IsBound)
                        {
                            continue;
                        }

                        // We only need to resolve against implicitly resolved assemblies,
                        // since we already resolved against explicitly specified ones.
                        referenceBinding[i] = ResolveReferencedAssembly(
                            binding.ReferenceIdentity,
                            allAssemblies,
                            explicitAssemblyCount,
                            IdentityComparer);
                    }
                }

                UpdateBindingsOfAssemblyBeingBuilt(referenceBindings, explicitAssemblyCount, implicitAssemblies);

                metadataReferences = metadataReferencesBuilder.ToImmutable();
                resolvedReferences = ToResolvedAssemblyReferences(metadataReferences, lazyAliasMap, explicitAssemblyCount);
            }
            finally
            {
                implicitAssemblies.Free();
                requestedIdentities.Free();
                referenceBindingsToProcess.Free();
                metadataReferencesBuilder.Free();
            }
        }
예제 #21
0
 public void AddRange <U>(ArrayBuilder <U> items) where U : T
 {
     _builder.AddRange(items._builder);
 }
예제 #22
0
        private static async Task <ImmutableArray <TextChange> > AddDocumentMergeChangesAsync(
            Document oldDocument,
            Document newDocument,
            List <TextChange> cumulativeChanges,
            List <UnmergedDocumentChanges> unmergedChanges,
            LinkedFileGroupSessionInfo groupSessionInfo,
            IDocumentTextDifferencingService textDiffService,
            CancellationToken cancellationToken)
        {
            var unmergedDocumentChanges   = new List <TextChange>();
            var successfullyMergedChanges = ArrayBuilder <TextChange> .GetInstance();

            var cumulativeChangeIndex = 0;

            var textchanges = await textDiffService.GetTextChangesAsync(oldDocument, newDocument, cancellationToken).ConfigureAwait(false);

            foreach (var change in textchanges)
            {
                while (cumulativeChangeIndex < cumulativeChanges.Count && cumulativeChanges[cumulativeChangeIndex].Span.End < change.Span.Start)
                {
                    // Existing change that does not overlap with the current change in consideration
                    successfullyMergedChanges.Add(cumulativeChanges[cumulativeChangeIndex]);
                    cumulativeChangeIndex++;

                    groupSessionInfo.IsolatedDiffs++;
                }

                if (cumulativeChangeIndex < cumulativeChanges.Count)
                {
                    var cumulativeChange = cumulativeChanges[cumulativeChangeIndex];
                    if (!cumulativeChange.Span.IntersectsWith(change.Span))
                    {
                        // The current change in consideration does not intersect with any existing change
                        successfullyMergedChanges.Add(change);

                        groupSessionInfo.IsolatedDiffs++;
                    }
                    else
                    {
                        if (change.Span != cumulativeChange.Span || change.NewText != cumulativeChange.NewText)
                        {
                            // The current change in consideration overlaps an existing change but
                            // the changes are not identical.
                            unmergedDocumentChanges.Add(change);

                            groupSessionInfo.OverlappingDistinctDiffs++;
                            if (change.Span == cumulativeChange.Span)
                            {
                                groupSessionInfo.OverlappingDistinctDiffsWithSameSpan++;
                                if (change.NewText.Contains(cumulativeChange.NewText) || cumulativeChange.NewText.Contains(change.NewText))
                                {
                                    groupSessionInfo.OverlappingDistinctDiffsWithSameSpanAndSubstringRelation++;
                                }
                            }
                        }
                        else
                        {
                            // The current change in consideration is identical to an existing change
                            successfullyMergedChanges.Add(change);
                            cumulativeChangeIndex++;

                            groupSessionInfo.IdenticalDiffs++;
                        }
                    }
                }
                else
                {
                    // The current change in consideration does not intersect with any existing change
                    successfullyMergedChanges.Add(change);

                    groupSessionInfo.IsolatedDiffs++;
                }
            }

            while (cumulativeChangeIndex < cumulativeChanges.Count)
            {
                // Existing change that does not overlap with the current change in consideration
                successfullyMergedChanges.Add(cumulativeChanges[cumulativeChangeIndex]);
                cumulativeChangeIndex++;
                groupSessionInfo.IsolatedDiffs++;
            }

            if (unmergedDocumentChanges.Any())
            {
                unmergedChanges.Add(new UnmergedDocumentChanges(
                                        unmergedDocumentChanges.AsEnumerable(),
                                        oldDocument.Project.Name,
                                        oldDocument.Id));
            }

            return(successfullyMergedChanges.ToImmutableAndFree());
        }