Example #1
0
        public async Task <ImmutableDictionary <Document, ImmutableArray <TextSpan> > > GetFixAllSpansAsync(
            Document document, TextSpan diagnosticSpan, FixAllScope fixAllScope, CancellationToken cancellationToken)
        {
            Contract.ThrowIfFalse(fixAllScope is FixAllScope.ContainingMember or FixAllScope.ContainingType);

            var decl = await GetContainingMemberOrTypeDeclarationAsync(document, fixAllScope, diagnosticSpan, cancellationToken).ConfigureAwait(false);

            if (decl == null)
            {
                return(await GetFixAllSpansIfWithinGlobalStatementAsync(document, diagnosticSpan, fixAllScope, cancellationToken).ConfigureAwait(false));
            }

            if (fixAllScope == FixAllScope.ContainingMember)
            {
                return(ImmutableDictionary.CreateRange(SpecializedCollections.SingletonEnumerable(
                                                           KeyValuePairUtil.Create(document, ImmutableArray.Create(decl.FullSpan)))));
            }
            else
            {
                var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);

                var symbol = semanticModel.GetDeclaredSymbol(decl, cancellationToken);
                if (symbol?.DeclaringSyntaxReferences.Length > 1)
                {
                    var syntaxFacts = document.GetRequiredLanguageService <ISyntaxFactsService>();
                    var builder     = PooledDictionary <Document, ArrayBuilder <TextSpan> > .GetInstance();

                    foreach (var syntaxRef in symbol.DeclaringSyntaxReferences)
                    {
                        var documentForLocation = document.Project.GetDocument(syntaxRef.SyntaxTree);
                        Contract.ThrowIfNull(documentForLocation);
                        var root = await syntaxRef.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false);

                        var partialDeclSpan = syntaxFacts.GetContainingTypeDeclaration(root, syntaxRef.Span.Start) !.FullSpan;
                        builder.MultiAdd(documentForLocation, partialDeclSpan);
                    }

                    return(builder.ToImmutableMultiDictionaryAndFree());
                }
                else
                {
                    return(ImmutableDictionary.CreateRange(SpecializedCollections.SingletonEnumerable(
                                                               KeyValuePairUtil.Create(document, ImmutableArray.Create(decl.FullSpan)))));
                }
            }
        }
Example #2
0
        /// <summary>
        /// Runs dataflow analysis for the given <paramref name="analyzer"/> on the given <paramref name="controlFlowGraph"/>.
        /// </summary>
        /// <param name="controlFlowGraph">Control flow graph on which to execute analysis.</param>
        /// <param name="analyzer">Dataflow analyzer.</param>
        /// <returns>Block analysis data at the end of the exit block.</returns>
        /// <remarks>
        /// Algorithm for this CFG walker has been forked from <see cref="ControlFlowGraphBuilder"/>'s internal
        /// implementation for basic block reachability computation: "MarkReachableBlocks",
        /// we should keep them in sync as much as possible.
        /// </remarks>
        public static TBlockAnalysisData Run(ControlFlowGraph controlFlowGraph, DataFlowAnalyzer <TBlockAnalysisData> analyzer, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            var blocks = controlFlowGraph.Blocks;
            var continueDispatchAfterFinally = PooledDictionary <ControlFlowRegion, bool> .GetInstance();

            var dispatchedExceptionsFromRegions = PooledHashSet <ControlFlowRegion> .GetInstance();

            int firstBlockOrdinal = 0;
            int lastBlockOrdinal  = blocks.Length - 1;

            var unreachableBlocksToVisit = ArrayBuilder <BasicBlock> .GetInstance();

            if (analyzer.AnalyzeUnreachableBlocks)
            {
                for (int i = firstBlockOrdinal; i <= lastBlockOrdinal; i++)
                {
                    if (!blocks[i].IsReachable)
                    {
                        unreachableBlocksToVisit.Add(blocks[i]);
                    }
                }
            }

            var initialAnalysisData = analyzer.GetCurrentAnalysisData(blocks[0]);

            if (initialAnalysisData.Equals(default(TBlockAnalysisData)))
            {
                initialAnalysisData = analyzer.GetEmptyAnalysisData();
            }

            var result = RunCore(blocks, analyzer, firstBlockOrdinal, lastBlockOrdinal,
                                 initialAnalysisData,
                                 unreachableBlocksToVisit,
                                 outOfRangeBlocksToVisit: null,
                                 continueDispatchAfterFinally,
                                 dispatchedExceptionsFromRegions,
                                 cancellationToken);

            Debug.Assert(unreachableBlocksToVisit.Count == 0);
            unreachableBlocksToVisit.Free();
            continueDispatchAfterFinally.Free();
            dispatchedExceptionsFromRegions.Free();
            return(result);
        }
        public static async Task <SyntaxNode> FixAllAsync(Document document, ImmutableArray <Diagnostic> diagnostics, CancellationToken cancellationToken)
        {
            var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);

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

            using var tokenToToken = PooledDictionary <SyntaxToken, SyntaxToken> .GetInstance();

            foreach (var diagnostic in diagnostics)
            {
                FixOne(root, text, tokenToToken, diagnostic, cancellationToken);
            }

            var newRoot = root.ReplaceTokens(tokenToToken.Keys, (t1, _) => tokenToToken[t1]);

            return(newRoot);
        }
            static async Task <ImmutableDictionary <Document, ImmutableArray <Diagnostic> > > GetSpanDiagnosticsAsync(
                FixAllContext fixAllContext,
                IEnumerable <KeyValuePair <Document, ImmutableArray <TextSpan> > > documentsAndSpans)
            {
                var builder = PooledDictionary <Document, ArrayBuilder <Diagnostic> > .GetInstance();

                foreach (var(document, spans) in documentsAndSpans)
                {
                    foreach (var span in spans)
                    {
                        var documentDiagnostics = await fixAllContext.GetDocumentSpanDiagnosticsAsync(document, span).ConfigureAwait(false);

                        builder.MultiAddRange(document, documentDiagnostics);
                    }
                }

                return(builder.ToImmutableMultiDictionaryAndFree());
            }
        public static AggregateCategorizedAnalyzerConfigOptions Create(AnalyzerConfigOptionsProvider analyzerConfigOptionsProvider, Compilation compilation, CompilationCategorizedAnalyzerConfigOptions additionalFileBasedOptions)
        {
            analyzerConfigOptionsProvider = analyzerConfigOptionsProvider ?? throw new ArgumentNullException(nameof(analyzerConfigOptionsProvider));
            additionalFileBasedOptions    = additionalFileBasedOptions ?? throw new ArgumentNullException(nameof(additionalFileBasedOptions));

            if (analyzerConfigOptionsProvider.IsEmpty() && additionalFileBasedOptions.IsEmpty)
            {
                return(Empty);
            }

            var perTreeOptionsBuilder = PooledDictionary <SyntaxTree, Lazy <SyntaxTreeCategorizedAnalyzerConfigOptions> > .GetInstance();

            foreach (var tree in compilation.SyntaxTrees)
            {
                perTreeOptionsBuilder.Add(tree, new Lazy <SyntaxTreeCategorizedAnalyzerConfigOptions>(() => Create(tree, analyzerConfigOptionsProvider)));
            }

            return(new AggregateCategorizedAnalyzerConfigOptions(perTreeOptionsBuilder.ToImmutableDictionaryAndFree(), additionalFileBasedOptions));
Example #6
0
        /// <summary>
        /// Substitute types, and return the results without duplicates, preserving the original order.
        /// </summary>
        internal void SubstituteConstraintTypesDistinctWithoutModifiers(
            ImmutableArray <TypeWithAnnotations> original,
            ArrayBuilder <TypeWithAnnotations> result,
            HashSet <TypeParameterSymbol> ignoreTypesDependentOnTypeParametersOpt)
        {
            if (original.Length == 0)
            {
                return;
            }
            else if (original.Length == 1)
            {
                var type = original[0];
                if (ignoreTypesDependentOnTypeParametersOpt == null ||
                    !type.Type.ContainsTypeParameters(ignoreTypesDependentOnTypeParametersOpt))
                {
                    result.Add(SubstituteType(type));
                }
            }
            else
            {
                var map = PooledDictionary <TypeSymbol, int> .GetInstance();

                foreach (var type in original)
                {
                    if (ignoreTypesDependentOnTypeParametersOpt == null ||
                        !type.Type.ContainsTypeParameters(ignoreTypesDependentOnTypeParametersOpt))
                    {
                        var substituted = SubstituteType(type);

                        if (!map.TryGetValue(substituted.Type, out int mergeWith))
                        {
                            map.Add(substituted.Type, result.Count);
                            result.Add(substituted);
                        }
                        else
                        {
                            result[mergeWith] = ConstraintsHelper.ConstraintWithMostSignificantNullability(result[mergeWith], substituted);
                        }
                    }
                }

                map.Free();
            }
        }
Example #7
0
        public static CompletionItem Create(string name, int arity, string containingNamespace, Glyph glyph, string genericTypeSuffix, CompletionItemFlags flags, string?symbolKeyData)
        {
            ImmutableDictionary <string, string>?properties = null;

            if (symbolKeyData != null || arity > 0)
            {
                var builder = PooledDictionary <string, string> .GetInstance();

                if (symbolKeyData != null)
                {
                    builder.Add(SymbolKeyData, symbolKeyData);
                }
                else
                {
                    // We don't need arity to recover symbol if we already have SymbolKeyData or it's 0.
                    // (but it still needed below to decide whether to show generic suffix)
                    builder.Add(TypeAritySuffixName, AbstractDeclaredSymbolInfoFactoryService.GetMetadataAritySuffix(arity));
                }

                properties = builder.ToImmutableDictionaryAndFree();
            }

            // Add tildes (ASCII: 126) to name and namespace as sort text:
            // 1. '~' before type name makes import items show after in-scope items
            // 2. ' ' before namespace makes types with identical type name but from different namespace all show up in the list,
            //    it also makes sure type with shorter name shows first, e.g. 'SomeType` before 'SomeTypeWithLongerName'.
            var sortTextBuilder = PooledStringBuilder.GetInstance();

            sortTextBuilder.Builder.AppendFormat(SortTextFormat, name, containingNamespace);

            var item = CompletionItem.Create(
                displayText: name,
                sortText: sortTextBuilder.ToStringAndFree(),
                properties: properties,
                tags: GlyphTags.GetTags(glyph),
                rules: CompletionItemRules.Default,
                displayTextPrefix: null,
                displayTextSuffix: arity == 0 ? string.Empty : genericTypeSuffix,
                inlineDescription: containingNamespace);

            item.Flags = flags;
            return(item);
        }
            public PropertySetDataFlowOperationVisitor(PropertySetAnalysisContext analysisContext)
                : base(analysisContext)
            {
                Debug.Assert(analysisContext.PointsToAnalysisResultOpt != null);

                this._hazardousUsageBuilder = ImmutableDictionary.CreateBuilder <(Location Location, IMethodSymbol Method), HazardousUsageEvaluationResult>();

                this._visitedLocalFunctions = ImmutableHashSet.CreateBuilder <IMethodSymbol>();

                this._visitedLambdas = ImmutableHashSet.CreateBuilder <IFlowAnonymousFunctionOperation>();

                this.WellKnownTypeProvider.TryGetTypeByMetadataName(analysisContext.TypeToTrackMetadataName, out this.TrackedTypeSymbol);
                Debug.Assert(this.TrackedTypeSymbol != null);

                if (this.DataFlowAnalysisContext.HazardousUsageEvaluators.TryGetInitializationHazardousUsageEvaluator(out _))
                {
                    this.TrackedFieldPropertyAssignmentsOpt = PooledDictionary <AnalysisEntity, TrackedAssignmentData> .GetInstance();
                }
            }
Example #9
0
        private static PooledDictionary<TNode, int> PredecessorCounts<TNode>(
            IEnumerable<TNode> nodes,
            Func<TNode, ImmutableArray<TNode>> successors,
            out ImmutableArray<TNode> allNodes)
        {
            var predecessorCounts = PooledDictionary<TNode, int>.GetInstance();
            var counted = PooledHashSet<TNode>.GetInstance();
            var toCount = ArrayBuilder<TNode>.GetInstance();
            var allNodesBuilder = ArrayBuilder<TNode>.GetInstance();
            toCount.AddRange(nodes);
            while (toCount.Count != 0)
            {
                var n = toCount.Pop();
                if (!counted.Add(n))
                {
                    continue;
                }

                allNodesBuilder.Add(n);
                if (!predecessorCounts.ContainsKey(n))
                {
                    predecessorCounts.Add(n, 0);
                }

                foreach (var succ in successors(n))
                {
                    toCount.Push(succ);
                    if (predecessorCounts.TryGetValue(succ, out int succPredecessorCount))
                    {
                        predecessorCounts[succ] = succPredecessorCount + 1;
                    }
                    else
                    {
                        predecessorCounts.Add(succ, 1);
                    }
                }
            }

            counted.Free();
            toCount.Free();
            allNodes = allNodesBuilder.ToImmutableAndFree();
            return predecessorCounts;
        }
        private static State CreateState(
            ImmutableArray <SyntaxTree> externalSyntaxTrees,
            string scriptClassName,
            SourceReferenceResolver resolver,
            Language language,
            bool isSubmission)
        {
            var treesBuilder = ArrayBuilder <SyntaxTree> .GetInstance();

            var ordinalMapBuilder = PooledDictionary <SyntaxTree, int> .GetInstance();

            var loadDirectiveMapBuilder = PooledDictionary <SyntaxTree, ImmutableArray <DeclarationLoadDirective> > .GetInstance();

            var loadedSyntaxTreeMapBuilder = PooledDictionary <string, SyntaxTree> .GetInstance();

            var declMapBuilder = PooledDictionary <SyntaxTree, Lazy <RootSingleDeclaration> > .GetInstance();

            var declTable = DeclarationTable.Empty;

            foreach (var tree in externalSyntaxTrees)
            {
                AppendAllSyntaxTrees(
                    treesBuilder,
                    tree,
                    scriptClassName,
                    resolver,
                    language,
                    isSubmission,
                    ordinalMapBuilder,
                    loadDirectiveMapBuilder,
                    loadedSyntaxTreeMapBuilder,
                    declMapBuilder,
                    ref declTable);
            }

            return(new State(
                       treesBuilder.ToImmutableAndFree(),
                       ordinalMapBuilder.ToImmutableDictionaryAndFree(),
                       loadDirectiveMapBuilder.ToImmutableDictionaryAndFree(),
                       loadedSyntaxTreeMapBuilder.ToImmutableDictionaryAndFree(),
                       declMapBuilder.ToImmutableDictionaryAndFree(),
                       declTable));
        }
Example #11
0
                public void TrackAssignmentWithAbstractLocation(
                    IAssignmentOperation assignmentOperation,
                    AbstractLocation abstractLocation)
                {
                    if (this.AbstractLocationsToAssignments == null)
                    {
                        this.AbstractLocationsToAssignments =
                            PooledDictionary <AbstractLocation, PooledHashSet <IAssignmentOperation> > .GetInstance();
                    }

                    if (!this.AbstractLocationsToAssignments.TryGetValue(
                            abstractLocation,
                            out PooledHashSet <IAssignmentOperation> assignments))
                    {
                        assignments = PooledHashSet <IAssignmentOperation> .GetInstance();

                        this.AbstractLocationsToAssignments.Add(abstractLocation, assignments);
                    }

                    assignments.Add(assignmentOperation);
                }
        public static DiagnosticAnalysisResult CreateFromBuild(Project project, ImmutableArray <DiagnosticData> diagnostics, IEnumerable <DocumentId> initialDocuments)
        {
            // we can't distinguish locals and non locals from build diagnostics nor determine right snapshot version for the build.
            // so we put everything in as semantic local with default version. this lets us to replace those to live diagnostics when needed easily.
            var version = VersionStamp.Default;

            var documentIds = ImmutableHashSet.CreateBuilder <DocumentId>();

            documentIds.AddRange(initialDocuments);

            var diagnosticsWithDocumentId = PooledDictionary <DocumentId, ArrayBuilder <DiagnosticData> > .GetInstance();

            var diagnosticsWithoutDocumentId = ArrayBuilder <DiagnosticData> .GetInstance();

            foreach (var data in diagnostics)
            {
                var documentId = data.DocumentId;
                if (documentId != null)
                {
                    documentIds.Add(documentId);
                    diagnosticsWithDocumentId.MultiAdd(documentId, data);
                }
                else
                {
                    diagnosticsWithoutDocumentId.Add(data);
                }
            }

            var result = new DiagnosticAnalysisResult(
                project.Id,
                version,
                documentIds: documentIds.ToImmutable(),
                syntaxLocals: ImmutableDictionary <DocumentId, ImmutableArray <DiagnosticData> > .Empty,
                semanticLocals: diagnosticsWithDocumentId.ToImmutableMultiDictionaryAndFree(),
                nonLocals: ImmutableDictionary <DocumentId, ImmutableArray <DiagnosticData> > .Empty,
                others: diagnosticsWithoutDocumentId.ToImmutableAndFree(),
                fromBuild: true);

            return(result);
        }
Example #13
0
        public BoundDecisionDag Rewrite(
            Func <
                BoundDecisionDagNode,
                Func <BoundDecisionDagNode, BoundDecisionDagNode>,
                BoundDecisionDagNode
                > makeReplacement
            )
        {
            // First, we topologically sort the nodes of the dag so that we can translate the nodes bottom-up.
            // This will avoid overflowing the compiler's runtime stack which would occur for a large switch
            // statement if we were using a recursive strategy.
            ImmutableArray <BoundDecisionDagNode> sortedNodes = this.TopologicallySortedNodes;

            // Cache simplified/translated replacement for each translated dag node. Since we always visit
            // a node's successors before the node, the replacement should always be in the cache when we need it.
            var replacement = PooledDictionary <
                BoundDecisionDagNode,
                BoundDecisionDagNode
                > .GetInstance();

            Func <BoundDecisionDagNode, BoundDecisionDagNode> getReplacementForChild = n =>
                                                                                       replacement[n];

            // Loop backwards through the topologically sorted nodes to translate them, so that we always visit a node after its successors
            for (int i = sortedNodes.Length - 1; i >= 0; i--)
            {
                BoundDecisionDagNode node = sortedNodes[i];
                Debug.Assert(!replacement.ContainsKey(node));
                BoundDecisionDagNode newNode = makeReplacement(node, getReplacementForChild);
                replacement.Add(node, newNode);
            }

            // Return the computed replacement root node
            var newRoot = replacement[this.RootNode];

            replacement.Free();
            return(this.Update(newRoot));
        }
        public async Task <ImmutableDictionary <string, ConfiguredProject> > GetActiveConfiguredProjectsMapAsync()
        {
            var builder = PooledDictionary <string, ConfiguredProject> .GetInstance();

            ActiveConfiguredObjects <ConfiguredProject> projects = await GetActiveConfiguredProjectsAsync();

            bool isCrossTargeting = projects.Objects.All(project => project.ProjectConfiguration.IsCrossTargeting());

            if (isCrossTargeting)
            {
                foreach (ConfiguredProject project in projects.Objects)
                {
                    string targetFramework = project.ProjectConfiguration.Dimensions[ConfigurationGeneral.TargetFrameworkProperty];
                    builder.Add(targetFramework, project);
                }
            }
            else
            {
                builder.Add(string.Empty, projects.Objects[0]);
            }

            return(builder.ToImmutableDictionaryAndFree());
        }
Example #15
0
        internal static void OrderAllDependencies(
            this SourceFieldSymbolWithSyntaxReference field,
            ArrayBuilder <FieldInfo> order,
            bool earlyDecodingWellKnownAttributes
            )
        {
            Debug.Assert(order.Count == 0);

            var graph = PooledDictionary <
                SourceFieldSymbolWithSyntaxReference,
                Node <SourceFieldSymbolWithSyntaxReference>
                > .GetInstance();

            CreateGraph(graph, field, earlyDecodingWellKnownAttributes);

            Debug.Assert(graph.Count >= 1);
            CheckGraph(graph);

#if DEBUG
            var fields = ArrayBuilder <SourceFieldSymbolWithSyntaxReference> .GetInstance();

            fields.AddRange(graph.Keys);
#endif

            OrderGraph(graph, order);

#if DEBUG
            // Verify all entries in the graph are in the ordered list.
            var map = new HashSet <SourceFieldSymbolWithSyntaxReference>(
                order.Select(o => o.Field).Distinct()
                );
            Debug.Assert(fields.All(f => map.Contains(f)));
            fields.Free();
#endif

            graph.Free();
        }
Example #16
0
#pragma warning restore

        public DataFlowAnalysisResultBuilder()
        {
            _info = PooledDictionary <BasicBlock, TAnalysisData?> .GetInstance();
        }
Example #17
0
        /// <summary>
        /// Starting with `this` state, produce a human-readable description of the state tables.
        /// This is very useful for debugging and optimizing the dag state construction.
        /// </summary>
        internal new string Dump()
        {
            var allStates          = this.TopologicallySortedNodes;
            var stateIdentifierMap = PooledDictionary <BoundDecisionDagNode, int> .GetInstance();

            for (int i = 0; i < allStates.Length; i++)
            {
                stateIdentifierMap.Add(allStates[i], i);
            }

            int nextTempNumber    = 0;
            var tempIdentifierMap = PooledDictionary <BoundDagEvaluation, int> .GetInstance();

            int tempIdentifier(BoundDagEvaluation e)
            {
                return((e == null) ? 0 : tempIdentifierMap.TryGetValue(e, out int value) ? value : tempIdentifierMap[e] = ++nextTempNumber);
            }

            string tempName(BoundDagTemp t)
            {
                return($"t{tempIdentifier(t.Source)}{(t.Index != 0 ? $".{t.Index.ToString()}" : "")}");
            }

            var resultBuilder = PooledStringBuilder.GetInstance();
            var result        = resultBuilder.Builder;

            foreach (var state in allStates)
            {
                result.AppendLine($"State " + stateIdentifierMap[state]);
                switch (state)
                {
                case BoundTestDecisionDagNode node:
                    result.AppendLine($"  Test: {dump(node.Test)}");
                    if (node.WhenTrue != null)
                    {
                        result.AppendLine($"  WhenTrue: {stateIdentifierMap[node.WhenTrue]}");
                    }

                    if (node.WhenFalse != null)
                    {
                        result.AppendLine($"  WhenFalse: {stateIdentifierMap[node.WhenFalse]}");
                    }
                    break;

                case BoundEvaluationDecisionDagNode node:
                    result.AppendLine($"  Test: {dump(node.Evaluation)}");
                    if (node.Next != null)
                    {
                        result.AppendLine($"  Next: {stateIdentifierMap[node.Next]}");
                    }
                    break;

                case BoundWhenDecisionDagNode node:
                    result.AppendLine($"  WhenClause: " + node.WhenExpression?.Syntax);
                    if (node.WhenTrue != null)
                    {
                        result.AppendLine($"  WhenTrue: {stateIdentifierMap[node.WhenTrue]}");
                    }

                    if (node.WhenFalse != null)
                    {
                        result.AppendLine($"  WhenFalse: {stateIdentifierMap[node.WhenFalse]}");
                    }
                    break;

                case BoundLeafDecisionDagNode node:
                    result.AppendLine($"  Case: " + node.Syntax);
                    break;

                default:
                    throw ExceptionUtilities.UnexpectedValue(state);
                }
            }

            stateIdentifierMap.Free();
            tempIdentifierMap.Free();
            return(resultBuilder.ToStringAndFree());

            string dump(BoundDagTest d)
            {
                switch (d)
                {
                case BoundDagTypeEvaluation a:
                    return($"t{tempIdentifier(a)}={a.Kind} {tempName(d.Input)} as {a.Type.ToString()}");

                case BoundDagPropertyEvaluation e:
                    return($"t{tempIdentifier(e)}={e.Kind} {tempName(d.Input)}.{e.Property.Name}");

                case BoundDagIndexEvaluation i:
                    return($"t{tempIdentifier(i)}={i.Kind} {tempName(d.Input)}[{i.Index}]");

                case BoundDagEvaluation e:
                    return($"t{tempIdentifier(e)}={e.Kind}, {tempName(d.Input)}");

                case BoundDagTypeTest b:
                    return($"?{d.Kind} {tempName(d.Input)} is {b.Type.ToString()}");

                case BoundDagValueTest v:
                    return($"?{d.Kind} {v.Value.ToString()} == {tempName(d.Input)}");

                case BoundDagNonNullTest t:
                    return($"?{d.Kind} {tempName(d.Input)} != null");

                case BoundDagExplicitNullTest t:
                    return($"?{d.Kind} {tempName(d.Input)} == null");

                default:
                    throw ExceptionUtilities.UnexpectedValue(d);
                }
            }
        }
        private static TaintedDataConfig Create(Compilation compilation)
        {
            WellKnownTypeProvider wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(compilation);

            using PooledDictionary <SinkKind, Lazy <TaintedDataSymbolMap <SourceInfo> > > sourceSymbolMapBuilder =
                      PooledDictionary <SinkKind, Lazy <TaintedDataSymbolMap <SourceInfo> > > .GetInstance();

            using PooledDictionary <SinkKind, Lazy <TaintedDataSymbolMap <SanitizerInfo> > > sanitizerSymbolMapBuilder =
                      PooledDictionary <SinkKind, Lazy <TaintedDataSymbolMap <SanitizerInfo> > > .GetInstance();

            using PooledDictionary <SinkKind, Lazy <TaintedDataSymbolMap <SinkInfo> > > sinkSymbolMapBuilder =
                      PooledDictionary <SinkKind, Lazy <TaintedDataSymbolMap <SinkInfo> > > .GetInstance();

            // For tainted data rules with the same set of sources, we'll reuse the same TaintedDataSymbolMap<SourceInfo> instance.
            // Same for sanitizers.
            using PooledDictionary <ImmutableHashSet <SourceInfo>, Lazy <TaintedDataSymbolMap <SourceInfo> > > sourcesToSymbolMap =
                      PooledDictionary <ImmutableHashSet <SourceInfo>, Lazy <TaintedDataSymbolMap <SourceInfo> > > .GetInstance();

            using PooledDictionary <ImmutableHashSet <SanitizerInfo>, Lazy <TaintedDataSymbolMap <SanitizerInfo> > > sanitizersToSymbolMap =
                      PooledDictionary <ImmutableHashSet <SanitizerInfo>, Lazy <TaintedDataSymbolMap <SanitizerInfo> > > .GetInstance();

            // Build a mapping of (sourceSet, sanitizerSet) -> (sinkKinds, sinkSet), so we'll reuse the same TaintedDataSymbolMap<SinkInfo> instance.
            using PooledDictionary <(ImmutableHashSet <SourceInfo> SourceInfos, ImmutableHashSet <SanitizerInfo> SanitizerInfos), (ImmutableHashSet <SinkKind> .Builder SinkKinds, ImmutableHashSet <SinkInfo> .Builder SinkInfos)> sourceSanitizersToSinks =
                      PooledDictionary <(ImmutableHashSet <SourceInfo> SourceInfos, ImmutableHashSet <SanitizerInfo> SanitizerInfos), (ImmutableHashSet <SinkKind> .Builder SinkKinds, ImmutableHashSet <SinkInfo> .Builder SinkInfos)> .GetInstance();

            // Using LazyThreadSafetyMode.ExecutionAndPublication to avoid instantiating multiple times.
            foreach (SinkKind sinkKind in Enum.GetValues(typeof(SinkKind)))
            {
                ImmutableHashSet <SourceInfo> sources = GetSourceInfos(sinkKind);
                if (!sourcesToSymbolMap.TryGetValue(sources, out Lazy <TaintedDataSymbolMap <SourceInfo> > lazySourceSymbolMap))
                {
                    lazySourceSymbolMap = new Lazy <TaintedDataSymbolMap <SourceInfo> >(
                        () => { return(new TaintedDataSymbolMap <SourceInfo>(wellKnownTypeProvider, sources)); },
                        LazyThreadSafetyMode.ExecutionAndPublication);
                    sourcesToSymbolMap.Add(sources, lazySourceSymbolMap);
                }

                sourceSymbolMapBuilder.Add(sinkKind, lazySourceSymbolMap);

                ImmutableHashSet <SanitizerInfo> sanitizers = GetSanitizerInfos(sinkKind);
                if (!sanitizersToSymbolMap.TryGetValue(sanitizers, out Lazy <TaintedDataSymbolMap <SanitizerInfo> > lazySanitizerSymbolMap))
                {
                    lazySanitizerSymbolMap = new Lazy <TaintedDataSymbolMap <SanitizerInfo> >(
                        () => { return(new TaintedDataSymbolMap <SanitizerInfo>(wellKnownTypeProvider, sanitizers)); },
                        LazyThreadSafetyMode.ExecutionAndPublication);
                    sanitizersToSymbolMap.Add(sanitizers, lazySanitizerSymbolMap);
                }

                sanitizerSymbolMapBuilder.Add(sinkKind, lazySanitizerSymbolMap);

                ImmutableHashSet <SinkInfo> sinks = GetSinkInfos(sinkKind);
                if (!sourceSanitizersToSinks.TryGetValue((sources, sanitizers), out (ImmutableHashSet <SinkKind> .Builder SinkKinds, ImmutableHashSet <SinkInfo> .Builder SinkInfos)sinksPair))
                {
                    sinksPair = (ImmutableHashSet.CreateBuilder <SinkKind>(), ImmutableHashSet.CreateBuilder <SinkInfo>());
                    sourceSanitizersToSinks.Add((sources, sanitizers), sinksPair);
                }

                sinksPair.SinkKinds.Add(sinkKind);
                sinksPair.SinkInfos.UnionWith(sinks);
            }

            foreach (KeyValuePair <(ImmutableHashSet <SourceInfo> SourceInfos, ImmutableHashSet <SanitizerInfo> SanitizerInfos), (ImmutableHashSet <SinkKind> .Builder SinkKinds, ImmutableHashSet <SinkInfo> .Builder SinkInfos)> kvp in sourceSanitizersToSinks)
            {
                ImmutableHashSet <SinkInfo>             sinks             = kvp.Value.SinkInfos.ToImmutable();
                Lazy <TaintedDataSymbolMap <SinkInfo> > lazySinkSymbolMap = new Lazy <TaintedDataSymbolMap <SinkInfo> >(
                    () => { return(new TaintedDataSymbolMap <SinkInfo>(wellKnownTypeProvider, sinks)); },
                    LazyThreadSafetyMode.ExecutionAndPublication);
                foreach (SinkKind sinkKind in kvp.Value.SinkKinds)
                {
                    sinkSymbolMapBuilder.Add(sinkKind, lazySinkSymbolMap);
                }
            }

            return(new TaintedDataConfig(
                       wellKnownTypeProvider,
                       sourceSymbolMapBuilder.ToImmutableDictionary(),
                       sanitizerSymbolMapBuilder.ToImmutableDictionary(),
                       sinkSymbolMapBuilder.ToImmutableDictionary()));
        }
        private void ResolveAndBindMissingAssemblies(
            TCompilation compilation,
            ImmutableArray <AssemblyData> explicitAssemblies,
            ImmutableArray <PEModule> explicitModules,
            ImmutableArray <MetadataReference> explicitReferences,
            ImmutableArray <ResolvedReference> explicitReferenceMap,
            MetadataReferenceResolver resolver,
            MetadataImportOptions importOptions,
            bool supersedeLowerVersions,
            [In, Out] ArrayBuilder <AssemblyReferenceBinding[]> referenceBindings,
            [In, Out] Dictionary <string, List <ReferencedAssemblyIdentity> > assemblyReferencesBySimpleName,
            out ImmutableArray <AssemblyData> allAssemblies,
            out ImmutableArray <MetadataReference> metadataReferences,
            out ImmutableArray <ResolvedReference> resolvedReferences,
            DiagnosticBag resolutionDiagnostics)
        {
            Debug.Assert(explicitAssemblies[0] is AssemblyDataForAssemblyBeingBuilt);
            Debug.Assert(referenceBindings.Count == explicitAssemblies.Length);
            Debug.Assert(explicitReferences.Length == explicitReferenceMap.Length);

            // -1 for assembly being built:
            int totalReferencedAssemblyCount = explicitAssemblies.Length - 1;

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

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

            PooledDictionary <AssemblyIdentity, PortableExecutableReference> previouslyResolvedAssembliesOpt = null;

            // Avoid resolving previously resolved missing references. If we call to the resolver again we would create new assembly symbols for them,
            // which would not match the previously created ones. As a result we would get duplicate PE types and conversion errors.
            var previousScriptCompilation = compilation.ScriptCompilationInfo?.PreviousScriptCompilation;

            if (previousScriptCompilation != null)
            {
                previouslyResolvedAssembliesOpt = PooledDictionary <AssemblyIdentity, PortableExecutableReference> .GetInstance();

                foreach (var entry in previousScriptCompilation.GetBoundReferenceManager().GetImplicitlyResolvedAssemblyReferences())
                {
                    previouslyResolvedAssembliesOpt.Add(entry.Key, entry.Value);
                }
            }

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

            Dictionary <MetadataReference, MergedAliases> lazyAliasMap = null;

            // metadata references and corresponding bindings of their references, used to calculate a fixed point:
            var referenceBindingsToProcess = ArrayBuilder <(MetadataReference, ArraySegment <AssemblyReferenceBinding>)> .GetInstance();

            // collect all missing identities, resolve the assemblies and bind their references against explicit definitions:
            GetInitialReferenceBindingsToProcess(explicitModules, explicitReferences, explicitReferenceMap, referenceBindings, totalReferencedAssemblyCount, referenceBindingsToProcess);

            // NB: includes the assembly being built:
            int explicitAssemblyCount = explicitAssemblies.Length;

            try
            {
                while (referenceBindingsToProcess.Count > 0)
                {
                    var referenceAndBindings = referenceBindingsToProcess.Pop();
                    var requestingReference  = referenceAndBindings.Item1;
                    var bindings             = referenceAndBindings.Item2;

                    foreach (var binding in bindings)
                    {
                        // only attempt to resolve unbound references (regardless of version difference of the bound ones)
                        if (binding.IsBound)
                        {
                            continue;
                        }

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

                        PortableExecutableReference resolvedReference;
                        if (previouslyResolvedAssembliesOpt == null || !previouslyResolvedAssembliesOpt.TryGetValue(binding.ReferenceIdentity, out resolvedReference))
                        {
                            resolvedReference = resolver.ResolveMissingAssembly(requestingReference, binding.ReferenceIdentity);
                            if (resolvedReference == null)
                            {
                                continue;
                            }
                        }

                        var data = ResolveMissingAssembly(binding.ReferenceIdentity, resolvedReference, 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.

                        // -1 for assembly being built:
                        int index = explicitAssemblyCount - 1 + metadataReferencesBuilder.Count;

                        var existingReference = TryAddAssembly(data.Identity, resolvedReference, index, resolutionDiagnostics, Location.None, assemblyReferencesBySimpleName, supersedeLowerVersions);
                        if (existingReference != null)
                        {
                            MergeReferenceProperties(existingReference, resolvedReference, resolutionDiagnostics, ref lazyAliasMap);
                            continue;
                        }

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

                        var referenceBinding = data.BindAssemblyReferences(explicitAssemblies, IdentityComparer);
                        referenceBindings.Add(referenceBinding);
                        referenceBindingsToProcess.Push((resolvedReference, new ArraySegment <AssemblyReferenceBinding>(referenceBinding)));
                    }
                }

                if (implicitAssemblies.Count == 0)
                {
                    Debug.Assert(lazyAliasMap == 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.

                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();
                previouslyResolvedAssembliesOpt?.Free();
            }
        }
Example #20
0
        private async Task FixAllValueAssignedIsUnusedDiagnosticsAsync(
            IOrderedEnumerable <Diagnostic> diagnostics,
            Document document,
            SemanticModel semanticModel,
            SyntaxNode root,
            SyntaxNode containingMemberDeclaration,
            UnusedValuePreference preference,
            bool removeAssignments,
            UniqueVariableNameGenerator nameGenerator,
            SyntaxEditor editor,
            ISyntaxFactsService syntaxFacts,
            CancellationToken cancellationToken)
        {
            // This method applies the code fix for diagnostics reported for unused value assignments to local/parameter.
            // The actual code fix depends on whether or not the right hand side of the assignment has side effects.
            // For example, if the right hand side is a constant or a reference to a local/parameter, then it has no side effects.
            // The lack of side effects is indicated by the "removeAssignments" parameter for this function.

            // If the right hand side has no side effects, then we can replace the assignments with variable declarations that have no initializer
            // or completely remove the statement.
            // If the right hand side does have side effects, we replace the identifier token for unused value assignment with
            // a new identifier token (either discard '_' or new unused local variable name).

            // For both the above cases, if the original diagnostic was reported on a local declaration, i.e. redundant initialization
            // at declaration, then we also add a new variable declaration statement without initializer for this local.

            var nodeReplacementMap = PooledDictionary <SyntaxNode, SyntaxNode> .GetInstance();

            var nodesToRemove = PooledHashSet <SyntaxNode> .GetInstance();

            var nodesToAdd = PooledHashSet <(TLocalDeclarationStatementSyntax declarationStatement, SyntaxNode node)> .GetInstance();

            // Indicates if the node's trivia was processed.
            var processedNodes = PooledHashSet <SyntaxNode> .GetInstance();

            var candidateDeclarationStatementsForRemoval = PooledHashSet <TLocalDeclarationStatementSyntax> .GetInstance();

            var hasAnyUnusedLocalAssignment = false;

            try
            {
                foreach (var(node, isUnusedLocalAssignment) in GetNodesToFix())
                {
                    hasAnyUnusedLocalAssignment |= isUnusedLocalAssignment;

                    var declaredLocal = semanticModel.GetDeclaredSymbol(node, cancellationToken) as ILocalSymbol;
                    if (declaredLocal == null && node.Parent is TCatchStatementSyntax)
                    {
                        declaredLocal = semanticModel.GetDeclaredSymbol(node.Parent, cancellationToken) as ILocalSymbol;
                    }

                    string newLocalNameOpt = null;
                    if (removeAssignments)
                    {
                        // Removable assignment or initialization, such that right hand side has no side effects.
                        if (declaredLocal != null)
                        {
                            // Redundant initialization.
                            // For example, "int a = 0;"
                            var variableDeclarator = node.FirstAncestorOrSelf <TVariableDeclaratorSyntax>();
                            Debug.Assert(variableDeclarator != null);
                            nodesToRemove.Add(variableDeclarator);

                            // Local declaration statement containing the declarator might be a candidate for removal if all its variables get marked for removal.
                            candidateDeclarationStatementsForRemoval.Add(variableDeclarator.GetAncestor <TLocalDeclarationStatementSyntax>());
                        }
                        else
                        {
                            // Redundant assignment or increment/decrement.
                            if (syntaxFacts.IsOperandOfIncrementOrDecrementExpression(node))
                            {
                                // For example, C# increment operation "a++;"
                                Debug.Assert(node.Parent.Parent is TExpressionStatementSyntax);
                                nodesToRemove.Add(node.Parent.Parent);
                            }
                            else
                            {
                                Debug.Assert(syntaxFacts.IsLeftSideOfAnyAssignment(node));

                                if (node.Parent is TStatementSyntax)
                                {
                                    // For example, VB simple assignment statement "a = 0"
                                    nodesToRemove.Add(node.Parent);
                                }
                                else if (node.Parent is TExpressionSyntax && node.Parent.Parent is TExpressionStatementSyntax)
                                {
                                    // For example, C# simple assignment statement "a = 0;"
                                    nodesToRemove.Add(node.Parent.Parent);
                                }
                                else
                                {
                                    // For example, C# nested assignment statement "a = b = 0;", where assignment to 'b' is redundant.
                                    // We replace the node with "a = 0;"
                                    nodeReplacementMap.Add(node.Parent, syntaxFacts.GetRightHandSideOfAssignment(node.Parent));
                                }
                            }
                        }
                    }
                    else
                    {
                        // Value initialization/assignment where the right hand side may have side effects,
                        // and hence needs to be preserved in fixed code.
                        // For example, "x = MethodCall();" is replaced with "_ = MethodCall();" or "var unused = MethodCall();"

                        // Replace the flagged variable's indentifier token with new named, based on user's preference.
                        var newNameToken = preference == UnusedValuePreference.DiscardVariable
                            ? editor.Generator.Identifier(AbstractRemoveUnusedParametersAndValuesDiagnosticAnalyzer.DiscardVariableName)
                            : nameGenerator.GenerateUniqueNameAtSpanStart(node);
                        newLocalNameOpt = newNameToken.ValueText;
                        var newNameNode = TryUpdateNameForFlaggedNode(node, newNameToken);
                        if (newNameNode == null)
                        {
                            continue;
                        }

                        // Is this is compound assignment?
                        if (syntaxFacts.IsLeftSideOfAnyAssignment(node) && !syntaxFacts.IsLeftSideOfAssignment(node))
                        {
                            // Compound assignment is changed to simple assignment.
                            // For example, "x += MethodCall();", where assignment to 'x' is redundant
                            // is replaced with "_ = MethodCall();" or "var unused = MethodCall();"
                            nodeReplacementMap.Add(node.Parent, GetReplacementNodeForCompoundAssignment(node.Parent, newNameNode, editor, syntaxFacts));
                        }
                        else
                        {
                            nodeReplacementMap.Add(node, newNameNode);
                        }
                    }

                    if (declaredLocal != null)
                    {
                        // We have a dead initialization for a local declaration.
                        // Introduce a new local declaration statement without an initializer for this local.
                        var declarationStatement = CreateLocalDeclarationStatement(declaredLocal.Type, declaredLocal.Name);
                        if (isUnusedLocalAssignment)
                        {
                            declarationStatement = declarationStatement.WithAdditionalAnnotations(s_unusedLocalDeclarationAnnotation);
                        }

                        nodesToAdd.Add((declarationStatement, node));
                    }
                    else
                    {
                        // We have a dead assignment to a local/parameter, which is not at the declaration site.
                        // Create a new local declaration for the unused local if both following conditions are met:
                        //  1. User prefers unused local variables for unused value assignment AND
                        //  2. Assignment value has side effects and hence cannot be removed.
                        if (preference == UnusedValuePreference.UnusedLocalVariable && !removeAssignments)
                        {
                            var type = semanticModel.GetTypeInfo(node, cancellationToken).Type;
                            Debug.Assert(type != null);
                            Debug.Assert(newLocalNameOpt != null);
                            var declarationStatement = CreateLocalDeclarationStatement(type, newLocalNameOpt);
                            nodesToAdd.Add((declarationStatement, node));
                        }
                    }
                }

                // Process candidate declaration statements for removal.
                foreach (var localDeclarationStatement in candidateDeclarationStatementsForRemoval)
                {
                    // If all the variable declarators for the local declaration statement are being removed,
                    // we can remove the entire local declaration statement.
                    if (ShouldRemoveStatement(localDeclarationStatement, out var variables))
                    {
                        nodesToRemove.Add(localDeclarationStatement);
                        nodesToRemove.RemoveRange(variables);
                    }
                }

                foreach (var nodeToAdd in nodesToAdd)
                {
                    InsertLocalDeclarationStatement(nodeToAdd.declarationStatement, nodeToAdd.node);
                }

                if (hasAnyUnusedLocalAssignment)
                {
                    // Local declaration statements with no initializer, but non-zero references are candidates for removal
                    // if the code fix removes all these references.
                    // We annotate such declaration statements with no initializer abd non-zero references here
                    // and remove them in post process document pass later, if the code fix did remove all these references.
                    foreach (var localDeclarationStatement in containingMemberDeclaration.DescendantNodes().OfType <TLocalDeclarationStatementSyntax>())
                    {
                        var variables = syntaxFacts.GetVariablesOfLocalDeclarationStatement(localDeclarationStatement);
                        if (variables.Count == 1 &&
                            syntaxFacts.GetInitializerOfVariableDeclarator(variables[0]) == null &&
                            !(await IsLocalDeclarationWithNoReferencesAsync(localDeclarationStatement, document, cancellationToken).ConfigureAwait(false)))
                        {
                            nodeReplacementMap.Add(localDeclarationStatement, localDeclarationStatement.WithAdditionalAnnotations(s_existingLocalDeclarationWithoutInitializerAnnotation));
                        }
                    }
                }

                foreach (var node in nodesToRemove)
                {
                    var removeOptions = SyntaxGenerator.DefaultRemoveOptions;
                    // If the leading trivia was not added to a new node, process it now.
                    if (!processedNodes.Contains(node))
                    {
                        // Don't keep trivia if the node is part of a multiple declaration statement.
                        // e.g. int x = 0, y = 0, z = 0; any white space left behind can cause problems if the declaration gets split apart.
                        var containingDeclaration = node.GetAncestor <TLocalDeclarationStatementSyntax>();
                        if (containingDeclaration != null && candidateDeclarationStatementsForRemoval.Contains(containingDeclaration))
                        {
                            removeOptions = SyntaxRemoveOptions.KeepNoTrivia;
                        }
                        else
                        {
                            removeOptions |= SyntaxRemoveOptions.KeepLeadingTrivia;
                        }
                    }
                    editor.RemoveNode(node, removeOptions);
                }

                foreach (var kvp in nodeReplacementMap)
                {
                    editor.ReplaceNode(kvp.Key, kvp.Value.WithAdditionalAnnotations(Formatter.Annotation));
                }
            }
            finally
            {
                nodeReplacementMap.Free();
                nodesToRemove.Free();
                nodesToAdd.Free();
                processedNodes.Free();
            }

            return;

            // Local functions.
            IEnumerable <(SyntaxNode node, bool isUnusedLocalAssignment)> GetNodesToFix()
            {
                foreach (var diagnostic in diagnostics)
                {
                    var node = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);
                    var isUnusedLocalAssignment = AbstractRemoveUnusedParametersAndValuesDiagnosticAnalyzer.GetIsUnusedLocalDiagnostic(diagnostic);
                    yield return(node, isUnusedLocalAssignment);
                }
            }

            // Mark generated local declaration statement with:
            //  1. "s_newLocalDeclarationAnnotation" for post processing in "MoveNewLocalDeclarationsNearReference" below.
            //  2. Simplifier annotation so that 'var'/explicit type is correctly added based on user options.
            TLocalDeclarationStatementSyntax CreateLocalDeclarationStatement(ITypeSymbol type, string name)
            => (TLocalDeclarationStatementSyntax)editor.Generator.LocalDeclarationStatement(type, name)
            .WithLeadingTrivia(editor.Generator.ElasticCarriageReturnLineFeed)
            .WithAdditionalAnnotations(s_newLocalDeclarationStatementAnnotation, Simplifier.Annotation);

            void InsertLocalDeclarationStatement(TLocalDeclarationStatementSyntax declarationStatement, SyntaxNode node)
            {
                // Find the correct place to insert the given declaration statement based on the node's ancestors.
                var insertionNode = node.FirstAncestorOrSelf <SyntaxNode>(n => n.Parent is TSwitchCaseBlockSyntax ||
                                                                          syntaxFacts.IsExecutableBlock(n.Parent) &&
                                                                          !(n is TCatchStatementSyntax) &&
                                                                          !(n is TCatchBlockSyntax));

                if (insertionNode is TSwitchCaseLabelOrClauseSyntax)
                {
                    InsertAtStartOfSwitchCaseBlockForDeclarationInCaseLabelOrClause(insertionNode.GetAncestor <TSwitchCaseBlockSyntax>(), editor, declarationStatement);
                }
                else if (insertionNode is TStatementSyntax)
                {
                    // If the insertion node is being removed, keep the leading trivia with the new declaration.
                    if (nodesToRemove.Contains(insertionNode) && !processedNodes.Contains(insertionNode))
                    {
                        declarationStatement = declarationStatement.WithLeadingTrivia(insertionNode.GetLeadingTrivia());
                        // Mark the node as processed so that the trivia only gets added once.
                        processedNodes.Add(insertionNode);
                    }
                    editor.InsertBefore(insertionNode, declarationStatement);
                }
            }

            bool ShouldRemoveStatement(TLocalDeclarationStatementSyntax localDeclarationStatement, out SeparatedSyntaxList <SyntaxNode> variables)
            {
                Debug.Assert(removeAssignments);

                // We should remove the entire local declaration statement if all its variables are marked for removal.
                variables = syntaxFacts.GetVariablesOfLocalDeclarationStatement(localDeclarationStatement);
                foreach (var variable in variables)
                {
                    if (!nodesToRemove.Contains(variable))
                    {
                        return(false);
                    }
                }

                return(true);
            }
        }
        /// <exception cref="InvalidOperationException">Bad data.</exception>
        private static void GetCSharpDynamicLocalInfo(
            byte[] customDebugInfo,
            IEnumerable <ISymUnmanagedScope> scopes,
            out ImmutableDictionary <int, ImmutableArray <bool> > dynamicLocalMap,
            out ImmutableDictionary <string, ImmutableArray <bool> > dynamicLocalConstantMap)
        {
            dynamicLocalMap         = null;
            dynamicLocalConstantMap = null;

            var record = CustomDebugInfoReader.TryGetCustomDebugInfoRecord(customDebugInfo, CustomDebugInfoKind.DynamicLocals);

            if (record.IsDefault)
            {
                return;
            }

            var localKindsByName = PooledDictionary <string, LocalKind> .GetInstance();

            GetLocalKindByName(localKindsByName, scopes);

            ImmutableDictionary <int, ImmutableArray <bool> > .Builder    localBuilder    = null;
            ImmutableDictionary <string, ImmutableArray <bool> > .Builder constantBuilder = null;

            var dynamicLocals = CustomDebugInfoReader.DecodeDynamicLocalsRecord(record);

            foreach (var dynamicLocal in dynamicLocals)
            {
                int slot  = dynamicLocal.SlotId;
                var flags = dynamicLocal.Flags;
                if (slot == 0)
                {
                    LocalKind kind;
                    var       name = dynamicLocal.LocalName;
                    localKindsByName.TryGetValue(name, out kind);
                    switch (kind)
                    {
                    case LocalKind.DuplicateName:
                        // Drop locals with ambiguous names.
                        continue;

                    case LocalKind.ConstantName:
                        constantBuilder       = constantBuilder ?? ImmutableDictionary.CreateBuilder <string, ImmutableArray <bool> >();
                        constantBuilder[name] = CreateBoolArray(flags);
                        continue;
                    }
                }
                localBuilder       = localBuilder ?? ImmutableDictionary.CreateBuilder <int, ImmutableArray <bool> >();
                localBuilder[slot] = CreateBoolArray(flags);
            }


            if (localBuilder != null)
            {
                dynamicLocalMap = localBuilder.ToImmutable();
            }

            if (constantBuilder != null)
            {
                dynamicLocalConstantMap = constantBuilder.ToImmutable();
            }

            localKindsByName.Free();
        }
#pragma warning disable CA1000 // Do not declare static members on generic types
        public static SymbolNamesWithValueOption <TValue> Create(ImmutableArray <string> symbolNames, Compilation compilation, string?optionalPrefix,
#pragma warning restore CA1000 // Do not declare static members on generic types
                                                                 Func <string, NameParts>?getSymbolNamePartsFunc = null)
        {
            if (symbolNames.IsEmpty)
            {
                return(Empty);
            }

            var namesBuilder = PooledDictionary <string, TValue> .GetInstance();

            var symbolsBuilder = PooledDictionary <ISymbol, TValue> .GetInstance();

            foreach (var symbolName in symbolNames)
            {
                var parts = getSymbolNamePartsFunc != null
                    ? getSymbolNamePartsFunc(symbolName)
                    : new NameParts(symbolName);

                if (parts.SymbolName.Equals(".ctor", StringComparison.Ordinal) ||
                    parts.SymbolName.Equals(".cctor", StringComparison.Ordinal) ||
                    !parts.SymbolName.Contains(".") && !parts.SymbolName.Contains(":"))
                {
                    if (!namesBuilder.ContainsKey(parts.SymbolName))
                    {
                        namesBuilder.Add(parts.SymbolName, parts.AssociatedValue);
                    }
                }
                else
                {
                    var nameWithPrefix = (string.IsNullOrEmpty(optionalPrefix) || parts.SymbolName.StartsWith(optionalPrefix, StringComparison.Ordinal))
                        ? parts.SymbolName
                        : optionalPrefix + parts.SymbolName;

#pragma warning disable CA1307 // Specify StringComparison - https://github.com/dotnet/roslyn-analyzers/issues/1552
                    // Documentation comment ID for constructors uses '#ctor', but '#' is a comment start token for editorconfig.
                    // We instead search for a '..ctor' in editorconfig and replace it with a '.#ctor' here.
                    // Similarly, handle static constructors ".cctor"
                    nameWithPrefix = nameWithPrefix.Replace("..ctor", ".#ctor");
                    nameWithPrefix = nameWithPrefix.Replace("..cctor", ".#cctor");
#pragma warning restore

                    foreach (var symbol in DocumentationCommentId.GetSymbolsForDeclarationId(nameWithPrefix, compilation))
                    {
                        if (symbol == null)
                        {
                            continue;
                        }

                        if (symbol is INamespaceSymbol namespaceSymbol &&
                            namespaceSymbol.ConstituentNamespaces.Length > 1)
                        {
                            foreach (var constituentNamespace in namespaceSymbol.ConstituentNamespaces)
                            {
                                if (!symbolsBuilder.ContainsKey(constituentNamespace))
                                {
                                    symbolsBuilder.Add(constituentNamespace, parts.AssociatedValue);
                                }
                            }
                        }

                        if (!symbolsBuilder.ContainsKey(symbol))
                        {
                            symbolsBuilder.Add(symbol, parts.AssociatedValue);
                        }
                    }
                }
            }

            if (namesBuilder.Count == 0 && symbolsBuilder.Count == 0)
            {
                return(Empty);
            }

            return(new SymbolNamesWithValueOption <TValue>(namesBuilder.ToImmutableDictionaryAndFree(), symbolsBuilder.ToImmutableDictionaryAndFree()));
        }
Example #23
0
        private static void GetFlowGraph(System.Text.StringBuilder stringBuilder, Compilation compilation, ControlFlowGraph graph,
                                         ControlFlowRegion enclosing, string idSuffix, int indent)
        {
            ImmutableArray <BasicBlock> blocks = graph.Blocks;

            var visitor = TestOperationVisitor.Singleton;
            ControlFlowRegion currentRegion        = graph.Root;
            bool lastPrintedBlockIsInCurrentRegion = true;
            PooledDictionary <ControlFlowRegion, int> regionMap = buildRegionMap();
            var localFunctionsMap = PooledDictionary <IMethodSymbol, ControlFlowGraph> .GetInstance();

            var anonymousFunctionsMap = PooledDictionary <IFlowAnonymousFunctionOperation, ControlFlowGraph> .GetInstance();

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

                Assert.Equal(i, block.Ordinal);

                switch (block.Kind)
                {
                case BasicBlockKind.Block:
                    Assert.NotEqual(0, i);
                    Assert.NotEqual(blocks.Length - 1, i);
                    break;

                case BasicBlockKind.Entry:
                    Assert.Equal(0, i);
                    Assert.Empty(block.Operations);
                    Assert.Empty(block.Predecessors);
                    Assert.Null(block.BranchValue);
                    Assert.NotNull(block.FallThroughSuccessor);
                    Assert.NotNull(block.FallThroughSuccessor.Destination);
                    Assert.Null(block.ConditionalSuccessor);
                    Assert.Same(graph.Root, currentRegion);
                    Assert.Same(currentRegion, block.EnclosingRegion);
                    Assert.Equal(0, currentRegion.FirstBlockOrdinal);
                    Assert.Same(enclosing, currentRegion.EnclosingRegion);
                    Assert.Null(currentRegion.ExceptionType);
                    Assert.Empty(currentRegion.Locals);
                    Assert.Empty(currentRegion.LocalFunctions);
                    Assert.Equal(ControlFlowRegionKind.Root, currentRegion.Kind);
                    Assert.True(block.IsReachable);
                    break;

                case BasicBlockKind.Exit:
                    Assert.Equal(blocks.Length - 1, i);
                    Assert.Empty(block.Operations);
                    Assert.Null(block.FallThroughSuccessor);
                    Assert.Null(block.ConditionalSuccessor);
                    Assert.Null(block.BranchValue);
                    Assert.Same(graph.Root, currentRegion);
                    Assert.Same(currentRegion, block.EnclosingRegion);
                    Assert.Equal(i, currentRegion.LastBlockOrdinal);
                    break;

                default:
                    Assert.False(true, $"Unexpected block kind {block.Kind}");
                    break;
                }

                if (block.EnclosingRegion != currentRegion)
                {
                    enterRegions(block.EnclosingRegion, block.Ordinal);
                }

                if (!lastPrintedBlockIsInCurrentRegion)
                {
                    stringBuilder.AppendLine();
                }

                appendLine($"Block[{getBlockId(block)}] - {block.Kind}{(block.IsReachable ? "" : " [UnReachable]")}");

                var predecessors = block.Predecessors;

                if (!predecessors.IsEmpty)
                {
                    appendIndent();
                    stringBuilder.Append("    Predecessors:");
                    int previousPredecessorOrdinal = -1;
                    for (var predecessorIndex = 0; predecessorIndex < predecessors.Length; predecessorIndex++)
                    {
                        var predecessorBranch = predecessors[predecessorIndex];
                        Assert.Same(block, predecessorBranch.Destination);
                        var predecessor = predecessorBranch.Source;
                        Assert.True(previousPredecessorOrdinal < predecessor.Ordinal);
                        previousPredecessorOrdinal = predecessor.Ordinal;
                        Assert.Same(blocks[predecessor.Ordinal], predecessor);

                        if (predecessorBranch.IsConditionalSuccessor)
                        {
                            Assert.Same(predecessor.ConditionalSuccessor, predecessorBranch);
                            Assert.NotEqual(ControlFlowConditionKind.None, predecessor.ConditionKind);
                        }
                        else
                        {
                            Assert.Same(predecessor.FallThroughSuccessor, predecessorBranch);
                        }

                        stringBuilder.Append($" [{getBlockId(predecessor)}");

                        if (predecessorIndex < predecessors.Length - 1 && predecessors[predecessorIndex + 1].Source == predecessor)
                        {
                            // Multiple branches from same predecessor - one must be conditional and other fall through.
                            Assert.True(predecessorBranch.IsConditionalSuccessor);
                            predecessorIndex++;
                            predecessorBranch = predecessors[predecessorIndex];
                            Assert.Same(predecessor.FallThroughSuccessor, predecessorBranch);
                            Assert.False(predecessorBranch.IsConditionalSuccessor);

                            stringBuilder.Append("*2");
                        }

                        stringBuilder.Append("]");
                    }

                    stringBuilder.AppendLine();
                }
                else if (block.Kind != BasicBlockKind.Entry)
                {
                    appendLine("    Predecessors (0)");
                }

                var statements = block.Operations;
                appendLine($"    Statements ({statements.Length})");
                foreach (var statement in statements)
                {
                    validateRoot(statement);
                    stringBuilder.AppendLine(getOperationTree(statement));
                }

                ControlFlowBranch conditionalBranch = block.ConditionalSuccessor;

                if (block.ConditionKind != ControlFlowConditionKind.None)
                {
                    Assert.NotNull(conditionalBranch);
                    Assert.True(conditionalBranch.IsConditionalSuccessor);

                    Assert.Same(block, conditionalBranch.Source);
                    if (conditionalBranch.Destination != null)
                    {
                        Assert.Same(blocks[conditionalBranch.Destination.Ordinal], conditionalBranch.Destination);
                    }

                    Assert.NotEqual(ControlFlowBranchSemantics.Return, conditionalBranch.Semantics);
                    Assert.NotEqual(ControlFlowBranchSemantics.Throw, conditionalBranch.Semantics);
                    Assert.NotEqual(ControlFlowBranchSemantics.StructuredExceptionHandling, conditionalBranch.Semantics);

                    Assert.True(block.ConditionKind == ControlFlowConditionKind.WhenTrue || block.ConditionKind == ControlFlowConditionKind.WhenFalse);
                    string jumpIfTrue = block.ConditionKind == ControlFlowConditionKind.WhenTrue ? "True" : "False";
                    appendLine($"    Jump if {jumpIfTrue} ({conditionalBranch.Semantics}) to Block[{getDestinationString(ref conditionalBranch)}]");

                    IOperation value = block.BranchValue;
                    Assert.NotNull(value);
                    validateRoot(value);
                    stringBuilder.Append(getOperationTree(value));
                    validateBranch(block, conditionalBranch);
                    stringBuilder.AppendLine();
                }
                else
                {
                    Assert.Null(conditionalBranch);
                    Assert.Equal(ControlFlowConditionKind.None, block.ConditionKind);
                }

                ControlFlowBranch nextBranch = block.FallThroughSuccessor;

                if (block.Kind == BasicBlockKind.Exit)
                {
                    Assert.Null(nextBranch);
                    Assert.Null(block.BranchValue);
                }
                else
                {
                    Assert.NotNull(nextBranch);
                    Assert.False(nextBranch.IsConditionalSuccessor);

                    Assert.Same(block, nextBranch.Source);
                    if (nextBranch.Destination != null)
                    {
                        Assert.Same(blocks[nextBranch.Destination.Ordinal], nextBranch.Destination);
                    }

                    if (nextBranch.Semantics == ControlFlowBranchSemantics.StructuredExceptionHandling)
                    {
                        Assert.Null(nextBranch.Destination);
                        Assert.Equal(block.EnclosingRegion.LastBlockOrdinal, block.Ordinal);
                        Assert.True(block.EnclosingRegion.Kind == ControlFlowRegionKind.Filter || block.EnclosingRegion.Kind == ControlFlowRegionKind.Finally);
                    }

                    appendLine($"    Next ({nextBranch.Semantics}) Block[{getDestinationString(ref nextBranch)}]");
                    IOperation value = block.ConditionKind == ControlFlowConditionKind.None ? block.BranchValue : null;

                    if (value != null)
                    {
                        Assert.True(ControlFlowBranchSemantics.Return == nextBranch.Semantics || ControlFlowBranchSemantics.Throw == nextBranch.Semantics);
                        validateRoot(value);
                        stringBuilder.Append(getOperationTree(value));
                    }
                    else
                    {
                        Assert.NotEqual(ControlFlowBranchSemantics.Return, nextBranch.Semantics);
                        Assert.NotEqual(ControlFlowBranchSemantics.Throw, nextBranch.Semantics);
                    }

                    validateBranch(block, nextBranch);
                }

                validateLocalsAndMethodsLifetime(block);

                if (currentRegion.LastBlockOrdinal == block.Ordinal && i != blocks.Length - 1)
                {
                    leaveRegions(block.EnclosingRegion, block.Ordinal);
                }
                else
                {
                    lastPrintedBlockIsInCurrentRegion = true;
                }
            }

            foreach (IMethodSymbol m in graph.LocalFunctions)
            {
                ControlFlowGraph g = localFunctionsMap[m];
                Assert.Same(g, graph.GetLocalFunctionControlFlowGraph(m));
            }

            Assert.Equal(graph.LocalFunctions.Length, localFunctionsMap.Count);

            foreach (KeyValuePair <IFlowAnonymousFunctionOperation, ControlFlowGraph> pair in anonymousFunctionsMap)
            {
                Assert.Same(pair.Value, graph.GetAnonymousFunctionControlFlowGraph(pair.Key));
            }

            regionMap.Free();
            localFunctionsMap.Free();
            anonymousFunctionsMap.Free();
            return;

            string getDestinationString(ref ControlFlowBranch branch)
            {
                return(branch.Destination != null?getBlockId(branch.Destination) : "null");
            }

            PooledObjects.PooledDictionary <ControlFlowRegion, int> buildRegionMap()
            {
                var result = PooledObjects.PooledDictionary <ControlFlowRegion, int> .GetInstance();

                int ordinal = 0;

                visit(graph.Root);

                void visit(ControlFlowRegion region)
                {
                    result.Add(region, ordinal++);

                    foreach (ControlFlowRegion r in region.NestedRegions)
                    {
                        visit(r);
                    }
                }

                return(result);
            }

            void appendLine(string line)
            {
                appendIndent();
                stringBuilder.AppendLine(line);
            }

            void appendIndent()
            {
                stringBuilder.Append(' ', indent);
            }

            void printLocals(ControlFlowRegion region)
            {
                if (!region.Locals.IsEmpty)
                {
                    appendIndent();
                    stringBuilder.Append("Locals:");
                    foreach (ILocalSymbol local in region.Locals)
                    {
                        stringBuilder.Append($" [{local.ToTestDisplayString()}]");
                    }
                    stringBuilder.AppendLine();
                }

                if (!region.LocalFunctions.IsEmpty)
                {
                    appendIndent();
                    stringBuilder.Append("Methods:");
                    foreach (IMethodSymbol method in region.LocalFunctions)
                    {
                        stringBuilder.Append($" [{method.ToTestDisplayString()}]");
                    }
                    stringBuilder.AppendLine();
                }
            }

            void enterRegions(ControlFlowRegion region, int firstBlockOrdinal)
            {
                if (region.FirstBlockOrdinal != firstBlockOrdinal)
                {
                    Assert.Same(currentRegion, region);

                    if (lastPrintedBlockIsInCurrentRegion)
                    {
                        stringBuilder.AppendLine();
                    }

                    return;
                }

                enterRegions(region.EnclosingRegion, firstBlockOrdinal);
                currentRegion = region;
                lastPrintedBlockIsInCurrentRegion = true;

                switch (region.Kind)
                {
                case ControlFlowRegionKind.Filter:
                    Assert.Empty(region.Locals);
                    Assert.Empty(region.LocalFunctions);
                    Assert.Equal(firstBlockOrdinal, region.EnclosingRegion.FirstBlockOrdinal);
                    Assert.Same(region.ExceptionType, region.EnclosingRegion.ExceptionType);
                    enterRegion($".filter {{{getRegionId(region)}}}");
                    break;

                case ControlFlowRegionKind.Try:
                    Assert.Null(region.ExceptionType);
                    Assert.Equal(firstBlockOrdinal, region.EnclosingRegion.FirstBlockOrdinal);
                    enterRegion($".try {{{getRegionId(region.EnclosingRegion)}, {getRegionId(region)}}}");
                    break;

                case ControlFlowRegionKind.FilterAndHandler:
                    enterRegion($".catch {{{getRegionId(region)}}} ({region.ExceptionType?.ToTestDisplayString() ?? "null"})");
                    break;

                case ControlFlowRegionKind.Finally:
                    Assert.Null(region.ExceptionType);
                    enterRegion($".finally {{{getRegionId(region)}}}");
                    break;

                case ControlFlowRegionKind.Catch:
                    switch (region.EnclosingRegion.Kind)
                    {
                    case ControlFlowRegionKind.FilterAndHandler:
                        Assert.Same(region.ExceptionType, region.EnclosingRegion.ExceptionType);
                        enterRegion($".handler {{{getRegionId(region)}}}");
                        break;

                    case ControlFlowRegionKind.TryAndCatch:
                        enterRegion($".catch {{{getRegionId(region)}}} ({region.ExceptionType?.ToTestDisplayString() ?? "null"})");
                        break;

                    default:
                        Assert.False(true, $"Unexpected region kind {region.EnclosingRegion.Kind}");
                        break;
                    }
                    break;

                case ControlFlowRegionKind.LocalLifetime:
                    Assert.Null(region.ExceptionType);
                    Assert.False(region.Locals.IsEmpty && region.LocalFunctions.IsEmpty);
                    enterRegion($".locals {{{getRegionId(region)}}}");
                    break;

                case ControlFlowRegionKind.TryAndCatch:
                case ControlFlowRegionKind.TryAndFinally:
                    Assert.Empty(region.Locals);
                    Assert.Empty(region.LocalFunctions);
                    Assert.Null(region.ExceptionType);
                    break;

                case ControlFlowRegionKind.StaticLocalInitializer:
                    Assert.Null(region.ExceptionType);
                    Assert.Empty(region.Locals);
                    enterRegion($".static initializer {{{getRegionId(region)}}}");
                    break;

                case ControlFlowRegionKind.ErroneousBody:
                    Assert.Null(region.ExceptionType);
                    enterRegion($".erroneous body {{{getRegionId(region)}}}");
                    break;

                default:
                    Assert.False(true, $"Unexpected region kind {region.Kind}");
                    break;
                }

                void enterRegion(string header)
                {
                    appendLine(header);
                    appendLine("{");
                    indent += 4;
                    printLocals(region);
                }
            }

            void leaveRegions(ControlFlowRegion region, int lastBlockOrdinal)
            {
                if (region.LastBlockOrdinal != lastBlockOrdinal)
                {
                    currentRegion = region;
                    lastPrintedBlockIsInCurrentRegion = false;
                    return;
                }

                string regionId = getRegionId(region);

                for (var i = 0; i < region.LocalFunctions.Length; i++)
                {
                    var method = region.LocalFunctions[i];
                    appendLine("");
                    appendLine("{   " + method.ToTestDisplayString());
                    appendLine("");
                    var g = graph.GetLocalFunctionControlFlowGraph(method);
                    localFunctionsMap.Add(method, g);
                    Assert.Equal(OperationKind.LocalFunction, g.OriginalOperation.Kind);
                    GetFlowGraph(stringBuilder, compilation, g, region, $"#{i}{regionId}", indent + 4);
                    appendLine("}");
                }

                switch (region.Kind)
                {
                case ControlFlowRegionKind.LocalLifetime:
                case ControlFlowRegionKind.Filter:
                case ControlFlowRegionKind.Try:
                case ControlFlowRegionKind.Finally:
                case ControlFlowRegionKind.FilterAndHandler:
                case ControlFlowRegionKind.StaticLocalInitializer:
                case ControlFlowRegionKind.ErroneousBody:
                    indent -= 4;
                    appendLine("}");
                    break;

                case ControlFlowRegionKind.Catch:
                    switch (region.EnclosingRegion.Kind)
                    {
                    case ControlFlowRegionKind.FilterAndHandler:
                    case ControlFlowRegionKind.TryAndCatch:
                        goto endRegion;

                    default:
                        Assert.False(true, $"Unexpected region kind {region.EnclosingRegion.Kind}");
                        break;
                    }

                    break;

endRegion:
                    goto case ControlFlowRegionKind.Filter;

                case ControlFlowRegionKind.TryAndCatch:
                case ControlFlowRegionKind.TryAndFinally:
                    break;

                default:
                    Assert.False(true, $"Unexpected region kind {region.Kind}");
                    break;
                }

                leaveRegions(region.EnclosingRegion, lastBlockOrdinal);
            }

            void validateBranch(BasicBlock fromBlock, ControlFlowBranch branch)
            {
                if (branch.Destination == null)
                {
                    Assert.Empty(branch.FinallyRegions);
                    Assert.Empty(branch.LeavingRegions);
                    Assert.Empty(branch.EnteringRegions);
                    Assert.True(ControlFlowBranchSemantics.None == branch.Semantics || ControlFlowBranchSemantics.Throw == branch.Semantics ||
                                ControlFlowBranchSemantics.Rethrow == branch.Semantics || ControlFlowBranchSemantics.StructuredExceptionHandling == branch.Semantics ||
                                ControlFlowBranchSemantics.ProgramTermination == branch.Semantics || ControlFlowBranchSemantics.Error == branch.Semantics);
                    return;
                }

                Assert.True(ControlFlowBranchSemantics.Regular == branch.Semantics || ControlFlowBranchSemantics.Return == branch.Semantics);
                Assert.True(branch.Destination.Predecessors.Contains(p => p.Source == fromBlock));

                if (!branch.FinallyRegions.IsEmpty)
                {
                    appendLine($"        Finalizing:" + buildList(branch.FinallyRegions));
                }

                ControlFlowRegion remainedIn1 = fromBlock.EnclosingRegion;

                if (!branch.LeavingRegions.IsEmpty)
                {
                    appendLine($"        Leaving:" + buildList(branch.LeavingRegions));
                    foreach (ControlFlowRegion r in branch.LeavingRegions)
                    {
                        Assert.Same(remainedIn1, r);
                        remainedIn1 = r.EnclosingRegion;
                    }
                }

                ControlFlowRegion remainedIn2 = branch.Destination.EnclosingRegion;

                if (!branch.EnteringRegions.IsEmpty)
                {
                    appendLine($"        Entering:" + buildList(branch.EnteringRegions));
                    for (int j = branch.EnteringRegions.Length - 1; j >= 0; j--)
                    {
                        ControlFlowRegion r = branch.EnteringRegions[j];
                        Assert.Same(remainedIn2, r);
                        remainedIn2 = r.EnclosingRegion;
                    }
                }

                Assert.Same(remainedIn1.EnclosingRegion, remainedIn2.EnclosingRegion);

                string buildList(ImmutableArray <ControlFlowRegion> list)
                {
                    var builder = PooledObjects.PooledStringBuilder.GetInstance();

                    foreach (ControlFlowRegion r in list)
                    {
                        builder.Builder.Append($" {{{getRegionId(r)}}}");
                    }

                    return(builder.ToStringAndFree());
                }
            }

            void validateRoot(IOperation root)
            {
                visitor.Visit(root);
                Assert.Null(root.Parent);
                Assert.Null(((Operation)root).SemanticModel);
                Assert.True(CanBeInControlFlowGraph(root), $"Unexpected node kind OperationKind.{root.Kind}");

                foreach (var operation in root.Descendants())
                {
                    visitor.Visit(operation);
                    Assert.NotNull(operation.Parent);
                    Assert.Null(((Operation)operation).SemanticModel);
                    Assert.True(CanBeInControlFlowGraph(operation), $"Unexpected node kind OperationKind.{operation.Kind}");
                }
            }

            void validateLocalsAndMethodsLifetime(BasicBlock block)
            {
                ISymbol[] localsOrMethodsInBlock = Enumerable.Concat(block.Operations, new[] { block.BranchValue }).
                                                   Where(o => o != null).
                                                   SelectMany(o => o.DescendantsAndSelf().
                                                              Select(node =>
                {
                    IMethodSymbol method;

                    switch (node.Kind)
                    {
                    case OperationKind.LocalReference:
                        return(((ILocalReferenceOperation)node).Local);

                    case OperationKind.MethodReference:
                        method = ((IMethodReferenceOperation)node).Method;
                        return(method.MethodKind == MethodKind.LocalFunction ? method.OriginalDefinition : null);

                    case OperationKind.Invocation:
                        method = ((IInvocationOperation)node).TargetMethod;
                        return(method.MethodKind == MethodKind.LocalFunction ? method.OriginalDefinition : null);

                    default:
                        return((ISymbol)null);
                    }
                }).
                                                              Where(s => s != null)).
                                                   Distinct().ToArray();

                if (localsOrMethodsInBlock.Length == 0)
                {
                    return;
                }

                var localsAndMethodsInRegions = PooledHashSet <ISymbol> .GetInstance();

                ControlFlowRegion region = block.EnclosingRegion;

                do
                {
                    foreach (ILocalSymbol l in region.Locals)
                    {
                        Assert.True(localsAndMethodsInRegions.Add(l));
                    }

                    foreach (IMethodSymbol m in region.LocalFunctions)
                    {
                        Assert.True(localsAndMethodsInRegions.Add(m));
                    }

                    region = region.EnclosingRegion;
                }while (region != null);

                foreach (ISymbol l in localsOrMethodsInBlock)
                {
                    Assert.False(localsAndMethodsInRegions.Add(l), $"Local/method without owning region {l.ToTestDisplayString()} in [{getBlockId(block)}]");
                }

                localsAndMethodsInRegions.Free();
            }

            string getBlockId(BasicBlock block)
            {
                return($"B{block.Ordinal}{idSuffix}");
            }

            string getRegionId(ControlFlowRegion region)
            {
                return($"R{regionMap[region]}{idSuffix}");
            }

            string getOperationTree(IOperation operation)
            {
                var walker = new OperationTreeSerializer(graph, currentRegion, idSuffix, anonymousFunctionsMap, compilation, operation, initialIndent: 8 + indent);

                walker.Visit(operation);
                return(walker.Builder.ToString());
            }
        }
Example #24
0
        private ActiveStatementsMap CreateActiveStatementsMap(Solution solution, ImmutableArray <ActiveStatementDebugInfo> debugInfos)
        {
            var byDocument = PooledDictionary <DocumentId, ArrayBuilder <ActiveStatement> > .GetInstance();

            var byInstruction = PooledDictionary <ActiveInstructionId, ActiveStatement> .GetInstance();

            bool SupportsEditAndContinue(DocumentId documentId)
            => solution.GetProject(documentId.ProjectId).LanguageServices.GetService <IEditAndContinueAnalyzer>() != null;

            foreach (var debugInfo in debugInfos)
            {
                var documentName = debugInfo.DocumentNameOpt;
                if (documentName == null)
                {
                    // Ignore active statements that do not have a source location.
                    continue;
                }

                var documentIds     = solution.GetDocumentIdsWithFilePath(documentName);
                var firstDocumentId = documentIds.FirstOrDefault(SupportsEditAndContinue);
                if (firstDocumentId == null)
                {
                    // Ignore active statements that don't belong to the solution or language that supports EnC service.
                    continue;
                }

                if (!byDocument.TryGetValue(firstDocumentId, out var primaryDocumentActiveStatements))
                {
                    byDocument.Add(firstDocumentId, primaryDocumentActiveStatements = ArrayBuilder <ActiveStatement> .GetInstance());
                }

                var activeStatement = new ActiveStatement(
                    ordinal: byInstruction.Count,
                    primaryDocumentOrdinal: primaryDocumentActiveStatements.Count,
                    documentIds: documentIds,
                    flags: debugInfo.Flags,
                    span: GetUpToDateSpan(debugInfo),
                    instructionId: debugInfo.InstructionId,
                    threadIds: debugInfo.ThreadIds);

                primaryDocumentActiveStatements.Add(activeStatement);

                // TODO: associate only those documents that are from a project with the right module id
                // https://github.com/dotnet/roslyn/issues/24320
                for (var i = 1; i < documentIds.Length; i++)
                {
                    var documentId = documentIds[i];
                    if (!SupportsEditAndContinue(documentId))
                    {
                        continue;
                    }

                    if (!byDocument.TryGetValue(documentId, out var linkedDocumentActiveStatements))
                    {
                        byDocument.Add(documentId, linkedDocumentActiveStatements = ArrayBuilder <ActiveStatement> .GetInstance());
                    }

                    linkedDocumentActiveStatements.Add(activeStatement);
                }

                try
                {
                    byInstruction.Add(debugInfo.InstructionId, activeStatement);
                }
                catch (ArgumentException)
                {
                    throw new InvalidOperationException($"Multiple active statements with the same instruction id returned by " +
                                                        $"{_activeStatementProvider.GetType()}.{nameof(IActiveStatementProvider.GetActiveStatementsAsync)}");
                }
            }

            return(new ActiveStatementsMap(byDocument.ToDictionaryAndFree(), byInstruction.ToDictionaryAndFree()));
        }
Example #25
0
        private static void ProcessBody <TSyntaxKind>(
            SemanticModel semanticModel,
            ImmutableArray <IDiagnosticAnalyzer> analyzers,
            ICodeBlockStartedAnalyzer[] bodyAnalyzers,
            ISymbol symbol,
            SyntaxNode syntax,
            CancellationToken cancellationToken,
            Action <Diagnostic> addDiagnostic,
            AnalyzerOptions analyzerOptions,
            bool continueOnError,
            Func <SyntaxNode, TSyntaxKind> getKind)
        {
            var endedAnalyzers = ArrayBuilder <ICodeBlockEndedAnalyzer> .GetInstance();

            PooledDictionary <TSyntaxKind, ArrayBuilder <ISyntaxNodeAnalyzer <TSyntaxKind> > > nodeAnalyzersByKind = null;

            foreach (var a in bodyAnalyzers)
            {
                // Catch Exception from a.OnCodeBlockStarted
                ExecuteAndCatchIfThrows(a, addDiagnostic, continueOnError, cancellationToken, () =>
                {
                    var analyzer = a.OnCodeBlockStarted(syntax, symbol, semanticModel, addDiagnostic, analyzerOptions, cancellationToken);
                    if (analyzer != null && analyzer != a)
                    {
                        endedAnalyzers.Add(analyzer);
                    }
                });
            }

            foreach (var nodeAnalyzer in endedAnalyzers.Concat(analyzers).OfType <ISyntaxNodeAnalyzer <TSyntaxKind> >())
            {
                // Catch Exception from  nodeAnalyzer.SyntaxKindsOfInterest
                try
                {
                    foreach (var kind in nodeAnalyzer.SyntaxKindsOfInterest)
                    {
                        if (nodeAnalyzersByKind == null)
                        {
                            nodeAnalyzersByKind = PooledDictionary <TSyntaxKind, ArrayBuilder <ISyntaxNodeAnalyzer <TSyntaxKind> > > .GetInstance();
                        }
                        ArrayBuilder <ISyntaxNodeAnalyzer <TSyntaxKind> > analyzersForKind;
                        if (!nodeAnalyzersByKind.TryGetValue(kind, out analyzersForKind))
                        {
                            nodeAnalyzersByKind.Add(kind, analyzersForKind = ArrayBuilder <ISyntaxNodeAnalyzer <TSyntaxKind> > .GetInstance());
                        }
                        analyzersForKind.Add(nodeAnalyzer);
                    }
                }
                catch (Exception e)
                {
                    // Create a info diagnostic saying that the analyzer failed
                    addDiagnostic(GetAnalyzerDiagnostic(nodeAnalyzer, e));
                }
            }

            if (nodeAnalyzersByKind != null)
            {
                foreach (var child in syntax.DescendantNodesAndSelf())
                {
                    ArrayBuilder <ISyntaxNodeAnalyzer <TSyntaxKind> > analyzersForKind;
                    if (nodeAnalyzersByKind.TryGetValue(getKind(child), out analyzersForKind))
                    {
                        foreach (var analyzer in analyzersForKind)
                        {
                            // Catch Exception from analyzer.AnalyzeNode
                            ExecuteAndCatchIfThrows(analyzer, addDiagnostic, continueOnError, cancellationToken, () => { analyzer.AnalyzeNode(child, semanticModel, addDiagnostic, analyzerOptions, cancellationToken); });
                        }
                    }
                }

                foreach (var b in nodeAnalyzersByKind.Values)
                {
                    b.Free();
                }
                nodeAnalyzersByKind.Free();
            }

            foreach (var a in endedAnalyzers.Concat(analyzers.OfType <ICodeBlockEndedAnalyzer>()))
            {
                // Catch Exception from a.OnCodeBlockEnded
                ExecuteAndCatchIfThrows(a, addDiagnostic, continueOnError, cancellationToken, () => { a.OnCodeBlockEnded(syntax, symbol, semanticModel, addDiagnostic, analyzerOptions, cancellationToken); });
            }

            endedAnalyzers.Free();
        }
Example #26
0
        /// <summary>
        /// Starting with `this` state, produce a human-readable description of the state tables.
        /// This is very useful for debugging and optimizing the dag state construction.
        /// </summary>
        internal new string Dump()
        {
            var allStates          = this.TopologicallySortedNodes;
            var stateIdentifierMap = PooledDictionary <BoundDecisionDagNode, int> .GetInstance();

            for (int i = 0; i < allStates.Length; i++)
            {
                stateIdentifierMap.Add(allStates[i], i);
            }

            int nextTempNumber    = 0;
            var tempIdentifierMap = PooledDictionary <BoundDagEvaluation, int> .GetInstance();

            int tempIdentifier(BoundDagEvaluation e)
            {
                return((e == null) ? 0 : tempIdentifierMap.TryGetValue(e, out int value) ? value : tempIdentifierMap[e] = ++nextTempNumber);
            }

            string tempName(BoundDagTemp t)
            {
                return($"t{tempIdentifier(t.Source)}{(t.Index != 0 ? $".{t.Index.ToString()}" : "")}");
            }

            var resultBuilder = PooledStringBuilder.GetInstance();
            var result        = resultBuilder.Builder;

            foreach (var state in allStates)
            {
                result.AppendLine($"State " + stateIdentifierMap[state]);
                switch (state)
                {
                case BoundTestDecisionDagNode node:
                    result.AppendLine($"  Test: {dumpDagTest(node.Test)}");
                    if (node.WhenTrue != null)
                    {
                        result.AppendLine($"  WhenTrue: {stateIdentifierMap[node.WhenTrue]}");
                    }

                    if (node.WhenFalse != null)
                    {
                        result.AppendLine($"  WhenFalse: {stateIdentifierMap[node.WhenFalse]}");
                    }
                    break;

                case BoundEvaluationDecisionDagNode node:
                    result.AppendLine($"  Test: {dumpDagTest(node.Evaluation)}");
                    if (node.Next != null)
                    {
                        result.AppendLine($"  Next: {stateIdentifierMap[node.Next]}");
                    }
                    break;

                case BoundWhenDecisionDagNode node:
                    result.AppendLine($"  WhenClause: " + node.WhenExpression?.Syntax);
                    if (node.WhenTrue != null)
                    {
                        result.AppendLine($"  WhenTrue: {stateIdentifierMap[node.WhenTrue]}");
                    }

                    if (node.WhenFalse != null)
                    {
                        result.AppendLine($"  WhenFalse: {stateIdentifierMap[node.WhenFalse]}");
                    }
                    break;

                case BoundLeafDecisionDagNode node:
                    result.AppendLine($"  Case: " + node.Syntax);
                    break;

                default:
                    throw ExceptionUtilities.UnexpectedValue(state);
                }
            }

            stateIdentifierMap.Free();
            tempIdentifierMap.Free();
            return(resultBuilder.ToStringAndFree());

            string dumpDagTest(BoundDagTest d)
            {
                switch (d)
                {
                case BoundDagTypeEvaluation a:
                    return($"t{tempIdentifier(a)}={a.Kind}(t{tempIdentifier(a)} as {a.Type})");

                case BoundDagEvaluation e:
                    return($"t{tempIdentifier(e)}={e.Kind}(t{tempIdentifier(e)})");

                case BoundDagTypeTest b:
                    return($"?{d.Kind}({tempName(d.Input)} is {b.Type})");

                case BoundDagValueTest v:
                    return($"?{d.Kind}({tempName(d.Input)} == {v.Value})");

                case BoundDagRelationalTest r:
                    var operatorName = r.Relation.Operator() switch
                    {
                        BinaryOperatorKind.LessThan => "<",
                        BinaryOperatorKind.LessThanOrEqual => "<=",
                        BinaryOperatorKind.GreaterThan => ">",
                        BinaryOperatorKind.GreaterThanOrEqual => ">=",
                        _ => "??"
                    };
                    return($"?{d.Kind}({tempName(d.Input)} {operatorName} {r.Value})");

                default:
                    return($"?{d.Kind}({tempName(d.Input)})");
                }
            }
        }
        public SyntaxAndDeclarationManager RemoveSyntaxTrees(HashSet <SyntaxTree> trees)
        {
            var state = _lazyState;
            var newExternalSyntaxTrees = this.ExternalSyntaxTrees.RemoveAll(t => trees.Contains(t));

            if (state == null)
            {
                return(this.WithExternalSyntaxTrees(newExternalSyntaxTrees));
            }

            var syntaxTrees         = state.SyntaxTrees;
            var loadDirectiveMap    = state.LoadDirectiveMap;
            var loadedSyntaxTreeMap = state.LoadedSyntaxTreeMap;
            var removeSet           = PooledHashSet <SyntaxTree> .GetInstance();

            foreach (var tree in trees)
            {
                int unused1;
                ImmutableArray <DeclarationLoadDirective> unused2;
                GetRemoveSet(
                    tree,
                    includeLoadedTrees: true,
                    syntaxTrees: syntaxTrees,
                    syntaxTreeOrdinalMap: state.OrdinalMap,
                    loadDirectiveMap: loadDirectiveMap,
                    loadedSyntaxTreeMap: loadedSyntaxTreeMap,
                    removeSet: removeSet,
                    totalReferencedTreeCount: out unused1,
                    oldLoadDirectives: out unused2);
            }

            var treesBuilder = ArrayBuilder <SyntaxTree> .GetInstance();

            var ordinalMapBuilder = PooledDictionary <SyntaxTree, int> .GetInstance();

            var declMapBuilder = state.RootNamespaces.ToBuilder();
            var declTable      = state.DeclarationTable;

            foreach (var tree in syntaxTrees)
            {
                if (removeSet.Contains(tree))
                {
                    loadDirectiveMap    = loadDirectiveMap.Remove(tree);
                    loadedSyntaxTreeMap = loadedSyntaxTreeMap.Remove(tree.FilePath);
                    RemoveSyntaxTreeFromDeclarationMapAndTable(tree, declMapBuilder, ref declTable);
                }
                else if (!IsLoadedSyntaxTree(tree, loadedSyntaxTreeMap))
                {
                    UpdateSyntaxTreesAndOrdinalMapOnly(
                        treesBuilder,
                        tree,
                        ordinalMapBuilder,
                        loadDirectiveMap,
                        loadedSyntaxTreeMap);
                }
            }
            removeSet.Free();

            state = new State(
                treesBuilder.ToImmutableAndFree(),
                ordinalMapBuilder.ToImmutableDictionaryAndFree(),
                loadDirectiveMap,
                loadedSyntaxTreeMap,
                declMapBuilder.ToImmutableDictionary(),
                declTable);

            return(new SyntaxAndDeclarationManager(
                       newExternalSyntaxTrees,
                       this.ScriptClassName,
                       this.Resolver,
                       this.Language,
                       this.IsSubmission,
                       state));
        }
Example #28
0
        internal EEMethodSymbol(
            EENamedTypeSymbol container,
            string name,
            Location location,
            MethodSymbol sourceMethod,
            ImmutableArray <LocalSymbol> sourceLocals,
            ImmutableArray <LocalSymbol> sourceLocalsForBinding,
            ImmutableDictionary <string, DisplayClassVariable> sourceDisplayClassVariables,
            GenerateMethodBody generateMethodBody)
        {
            Debug.Assert(sourceMethod.IsDefinition);
            Debug.Assert(sourceMethod.ContainingSymbol == container.SubstitutedSourceType.OriginalDefinition);
            Debug.Assert(sourceLocals.All(l => l.ContainingSymbol == sourceMethod));

            _container = container;
            _name      = name;
            _locations = ImmutableArray.Create(location);

            // What we want is to map all original type parameters to the corresponding new type parameters
            // (since the old ones have the wrong owners).  Unfortunately, we have a circular dependency:
            //   1) Each new type parameter requires the entire map in order to be able to construct its constraint list.
            //   2) The map cannot be constructed until all new type parameters exist.
            // Our solution is to pass each new type parameter a lazy reference to the type map.  We then
            // initialize the map as soon as the new type parameters are available - and before they are
            // handed out - so that there is never a period where they can require the type map and find
            // it uninitialized.

            var sourceMethodTypeParameters = sourceMethod.TypeParameters;
            var allSourceTypeParameters    = container.SourceTypeParameters.Concat(sourceMethodTypeParameters);

            var getTypeMap = new Func <TypeMap>(() => this.TypeMap);

            _typeParameters = sourceMethodTypeParameters.SelectAsArray(
                (tp, i, arg) => (TypeParameterSymbol) new EETypeParameterSymbol(this, tp, i, getTypeMap),
                (object)null);
            _allTypeParameters = container.TypeParameters.Concat(_typeParameters);
            this.TypeMap       = new TypeMap(allSourceTypeParameters, _allTypeParameters);

            EENamedTypeSymbol.VerifyTypeParameters(this, _typeParameters);

            var substitutedSourceType = container.SubstitutedSourceType;

            this.SubstitutedSourceMethod = sourceMethod.AsMember(substitutedSourceType);
            if (sourceMethod.Arity > 0)
            {
                this.SubstitutedSourceMethod = this.SubstitutedSourceMethod.Construct(_typeParameters.As <TypeSymbol>());
            }
            TypeParameterChecker.Check(this.SubstitutedSourceMethod, _allTypeParameters);

            // Create a map from original parameter to target parameter.
            var parameterBuilder = ArrayBuilder <ParameterSymbol> .GetInstance();

            var substitutedSourceThisParameter    = this.SubstitutedSourceMethod.ThisParameter;
            var substitutedSourceHasThisParameter = (object)substitutedSourceThisParameter != null;

            if (substitutedSourceHasThisParameter)
            {
                _thisParameter = MakeParameterSymbol(0, GeneratedNames.ThisProxyFieldName(), substitutedSourceThisParameter);
                Debug.Assert(_thisParameter.Type == this.SubstitutedSourceMethod.ContainingType);
                parameterBuilder.Add(_thisParameter);
            }

            var ordinalOffset = (substitutedSourceHasThisParameter ? 1 : 0);

            foreach (var substitutedSourceParameter in this.SubstitutedSourceMethod.Parameters)
            {
                var ordinal = substitutedSourceParameter.Ordinal + ordinalOffset;
                Debug.Assert(ordinal == parameterBuilder.Count);
                var parameter = MakeParameterSymbol(ordinal, substitutedSourceParameter.Name, substitutedSourceParameter);
                parameterBuilder.Add(parameter);
            }

            _parameters = parameterBuilder.ToImmutableAndFree();

            var localsBuilder = ArrayBuilder <LocalSymbol> .GetInstance();

            var localsMap = PooledDictionary <LocalSymbol, LocalSymbol> .GetInstance();

            foreach (var sourceLocal in sourceLocals)
            {
                var local = sourceLocal.ToOtherMethod(this, this.TypeMap);
                localsMap.Add(sourceLocal, local);
                localsBuilder.Add(local);
            }
            this.Locals   = localsBuilder.ToImmutableAndFree();
            localsBuilder = ArrayBuilder <LocalSymbol> .GetInstance();

            foreach (var sourceLocal in sourceLocalsForBinding)
            {
                LocalSymbol local;
                if (!localsMap.TryGetValue(sourceLocal, out local))
                {
                    local = sourceLocal.ToOtherMethod(this, this.TypeMap);
                    localsMap.Add(sourceLocal, local);
                }
                localsBuilder.Add(local);
            }
            this.LocalsForBinding = localsBuilder.ToImmutableAndFree();

            // Create a map from variable name to display class field.
            var displayClassVariables = PooledDictionary <string, DisplayClassVariable> .GetInstance();

            foreach (var pair in sourceDisplayClassVariables)
            {
                var variable = pair.Value;
                var oldDisplayClassInstance = variable.DisplayClassInstance;

                // Note: we don't call ToOtherMethod in the local case because doing so would produce
                // a new LocalSymbol that would not be ReferenceEquals to the one in this.LocalsForBinding.
                var oldDisplayClassInstanceFromLocal = oldDisplayClassInstance as DisplayClassInstanceFromLocal;
                var newDisplayClassInstance          = (oldDisplayClassInstanceFromLocal == null) ?
                                                       oldDisplayClassInstance.ToOtherMethod(this, this.TypeMap) :
                                                       new DisplayClassInstanceFromLocal((EELocalSymbol)localsMap[oldDisplayClassInstanceFromLocal.Local]);

                variable = variable.SubstituteFields(newDisplayClassInstance, this.TypeMap);
                displayClassVariables.Add(pair.Key, variable);
            }

            _displayClassVariables = displayClassVariables.ToImmutableDictionary();
            displayClassVariables.Free();
            localsMap.Free();

            _generateMethodBody = generateMethodBody;
        }
Example #29
0
        private Tuple <NamedTypeSymbol, ImmutableArray <NamedTypeSymbol> > MakeDeclaredBases(ConsList <TypeSymbol> basesBeingResolved, DiagnosticBag diagnostics)
        {
            if (this.TypeKind == TypeKind.Enum)
            {
                // Handled by GetEnumUnderlyingType().
                return(new Tuple <NamedTypeSymbol, ImmutableArray <NamedTypeSymbol> >(null, ImmutableArray <NamedTypeSymbol> .Empty));
            }

            var reportedPartialConflict = false;

            Debug.Assert(basesBeingResolved == null || !basesBeingResolved.ContainsReference(this.OriginalDefinition));
            var newBasesBeingResolved = basesBeingResolved.Prepend(this.OriginalDefinition);
            var baseInterfaces        = ArrayBuilder <NamedTypeSymbol> .GetInstance();

            NamedTypeSymbol baseType           = null;
            SourceLocation  baseTypeLocation   = null;
            var             interfaceLocations = PooledDictionary <NamedTypeSymbol, SourceLocation> .GetInstance();

            foreach (var decl in this.declaration.Declarations)
            {
                Tuple <NamedTypeSymbol, ImmutableArray <NamedTypeSymbol> > one = MakeOneDeclaredBases(newBasesBeingResolved, decl, diagnostics);
                if ((object)one == null)
                {
                    continue;
                }

                var partBase       = one.Item1;
                var partInterfaces = one.Item2;
                if (!reportedPartialConflict)
                {
                    if ((object)baseType == null)
                    {
                        baseType         = partBase;
                        baseTypeLocation = decl.NameLocation;
                    }
                    else if (baseType.TypeKind == TypeKind.Error && (object)partBase != null)
                    {
                        // if the old base was an error symbol, copy it to the interfaces list so it doesn't get lost
                        partInterfaces   = partInterfaces.Add(baseType);
                        baseType         = partBase;
                        baseTypeLocation = decl.NameLocation;
                    }
                    else if ((object)partBase != null && !TypeSymbol.Equals(partBase, baseType, TypeCompareKind.ConsiderEverything2) && partBase.TypeKind != TypeKind.Error)
                    {
                        // the parts do not agree
                        var info = diagnostics.Add(ErrorCode.ERR_PartialMultipleBases, Locations[0], this);
                        baseType                = new ExtendedErrorTypeSymbol(baseType, LookupResultKind.Ambiguous, info);
                        baseTypeLocation        = decl.NameLocation;
                        reportedPartialConflict = true;
                    }
                }

                foreach (var t in partInterfaces)
                {
                    if (!interfaceLocations.ContainsKey(t))
                    {
                        baseInterfaces.Add(t);
                        interfaceLocations.Add(t, decl.NameLocation);
                    }
                }
            }

            HashSet <DiagnosticInfo> useSiteDiagnostics = null;

            if ((object)baseType != null)
            {
                Debug.Assert(baseTypeLocation != null);
                if (baseType.IsStatic)
                {
                    // '{1}': cannot derive from static class '{0}'
                    diagnostics.Add(ErrorCode.ERR_StaticBaseClass, baseTypeLocation, baseType, this);
                }

                if (!this.IsNoMoreVisibleThan(baseType, ref useSiteDiagnostics))
                {
                    // Inconsistent accessibility: base class '{1}' is less accessible than class '{0}'
                    diagnostics.Add(ErrorCode.ERR_BadVisBaseClass, baseTypeLocation, this, baseType);
                }
            }

            var baseInterfacesRO = baseInterfaces.ToImmutableAndFree();

            if (DeclaredAccessibility != Accessibility.Private && IsInterface)
            {
                foreach (var i in baseInterfacesRO)
                {
                    if (!i.IsAtLeastAsVisibleAs(this, ref useSiteDiagnostics))
                    {
                        // Inconsistent accessibility: base interface '{1}' is less accessible than interface '{0}'
                        diagnostics.Add(ErrorCode.ERR_BadVisBaseInterface, interfaceLocations[i], this, i);
                    }
                }
            }

            interfaceLocations.Free();

            diagnostics.Add(Locations[0], useSiteDiagnostics);

            return(new Tuple <NamedTypeSymbol, ImmutableArray <NamedTypeSymbol> >(baseType, baseInterfacesRO));
        }
        public SyntaxAndDeclarationManager ReplaceSyntaxTree(SyntaxTree oldTree, SyntaxTree newTree)
        {
            var state = _lazyState;
            var newExternalSyntaxTrees = this.ExternalSyntaxTrees.Replace(oldTree, newTree);

            if (state == null)
            {
                return(this.WithExternalSyntaxTrees(newExternalSyntaxTrees));
            }

            var newLoadDirectivesSyntax   = newTree.GetCompilationUnitRoot().GetLoadDirectives();
            var loadDirectivesHaveChanged = !oldTree.GetCompilationUnitRoot().GetLoadDirectives().SequenceEqual(newLoadDirectivesSyntax);
            var syntaxTrees         = state.SyntaxTrees;
            var ordinalMap          = state.OrdinalMap;
            var loadDirectiveMap    = state.LoadDirectiveMap;
            var loadedSyntaxTreeMap = state.LoadedSyntaxTreeMap;
            var removeSet           = PooledHashSet <SyntaxTree> .GetInstance();

            int totalReferencedTreeCount;
            ImmutableArray <DeclarationLoadDirective> oldLoadDirectives;

            GetRemoveSet(
                oldTree,
                loadDirectivesHaveChanged,
                syntaxTrees,
                ordinalMap,
                loadDirectiveMap,
                loadedSyntaxTreeMap,
                removeSet,
                out totalReferencedTreeCount,
                out oldLoadDirectives);

            var loadDirectiveMapBuilder    = loadDirectiveMap.ToBuilder();
            var loadedSyntaxTreeMapBuilder = loadedSyntaxTreeMap.ToBuilder();
            var declMapBuilder             = state.RootNamespaces.ToBuilder();
            var declTable = state.DeclarationTable;

            foreach (var tree in removeSet)
            {
                loadDirectiveMapBuilder.Remove(tree);
                loadedSyntaxTreeMapBuilder.Remove(tree.FilePath);
                RemoveSyntaxTreeFromDeclarationMapAndTable(tree, declMapBuilder, ref declTable);
            }
            removeSet.Free();

            var oldOrdinal = ordinalMap[oldTree];
            ImmutableArray <SyntaxTree> newTrees;

            if (loadDirectivesHaveChanged)
            {
                // Should have been removed above...
                Debug.Assert(!loadDirectiveMapBuilder.ContainsKey(oldTree));
                Debug.Assert(!loadDirectiveMapBuilder.ContainsKey(newTree));

                // If we're inserting new #load'ed trees, we'll rebuild
                // the whole syntaxTree array and the ordinalMap.
                var treesBuilder = ArrayBuilder <SyntaxTree> .GetInstance();

                var ordinalMapBuilder = PooledDictionary <SyntaxTree, int> .GetInstance();

                for (var i = 0; i <= (oldOrdinal - totalReferencedTreeCount); i++)
                {
                    var tree = syntaxTrees[i];
                    treesBuilder.Add(tree);
                    ordinalMapBuilder.Add(tree, i);
                }

                AppendAllSyntaxTrees(
                    treesBuilder,
                    newTree,
                    this.ScriptClassName,
                    this.Resolver,
                    this.Language,
                    this.IsSubmission,
                    ordinalMapBuilder,
                    loadDirectiveMapBuilder,
                    loadedSyntaxTreeMapBuilder,
                    declMapBuilder,
                    ref declTable);

                for (var i = oldOrdinal + 1; i < syntaxTrees.Length; i++)
                {
                    var tree = syntaxTrees[i];
                    if (!IsLoadedSyntaxTree(tree, loadedSyntaxTreeMap))
                    {
                        UpdateSyntaxTreesAndOrdinalMapOnly(
                            treesBuilder,
                            tree,
                            ordinalMapBuilder,
                            loadDirectiveMap,
                            loadedSyntaxTreeMap);
                    }
                }

                newTrees   = treesBuilder.ToImmutableAndFree();
                ordinalMap = ordinalMapBuilder.ToImmutableDictionaryAndFree();
                Debug.Assert(newTrees.Length == ordinalMap.Count);
            }
            else
            {
                AddSyntaxTreeToDeclarationMapAndTable(newTree, this.ScriptClassName, this.IsSubmission, declMapBuilder, ref declTable);

                if (newLoadDirectivesSyntax.Any())
                {
                    // If load directives have not changed and there are new directives,
                    // then there should have been (matching) old directives as well.
                    Debug.Assert(!oldLoadDirectives.IsDefault);
                    Debug.Assert(!oldLoadDirectives.IsEmpty);
                    Debug.Assert(oldLoadDirectives.Length == newLoadDirectivesSyntax.Count);
                    loadDirectiveMapBuilder[newTree] = oldLoadDirectives;
                }

                Debug.Assert(ordinalMap.ContainsKey(oldTree)); // Checked by RemoveSyntaxTreeFromDeclarationMapAndTable

                newTrees = syntaxTrees.SetItem(oldOrdinal, newTree);

                ordinalMap = ordinalMap.Remove(oldTree);
                ordinalMap = ordinalMap.SetItem(newTree, oldOrdinal);
            }

            state = new State(
                newTrees,
                ordinalMap,
                loadDirectiveMapBuilder.ToImmutable(),
                loadedSyntaxTreeMapBuilder.ToImmutable(),
                declMapBuilder.ToImmutable(),
                declTable);

            return(new SyntaxAndDeclarationManager(
                       newExternalSyntaxTrees,
                       this.ScriptClassName,
                       this.Resolver,
                       this.Language,
                       this.IsSubmission,
                       state));
        }