public async Task <List <CompilationErrorDetails> > GetCompilationErrorDetails(Document document, SyntaxNode bodyOpt, CancellationToken cancellationToken)
        {
            try
            {
                var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);

                var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);

                ImmutableArray <Diagnostic> diagnostics;

                // If we have the SyntaxNode bodyOpt,
                // then we can use its fullSpan property to process a subset of the document.
                if (bodyOpt == null)
                {
                    diagnostics = semanticModel.GetDiagnostics(cancellationToken: cancellationToken);
                }
                else
                {
                    diagnostics = semanticModel.GetDiagnostics(bodyOpt.FullSpan, cancellationToken: cancellationToken);
                }

                if (diagnostics.Length == 0)
                {
                    return(null);
                }

                var syntaxFacts = document.Project.LanguageServices.GetService <ISyntaxFactsService>();

                List <CompilationErrorDetails> ret = new List <CompilationErrorDetails>();

                foreach (var diagnostic in diagnostics)
                {
                    if (diagnostic.Severity != DiagnosticSeverity.Error || diagnostic.IsWarningAsError)
                    {
                        continue;
                    }

                    if (!IsTrackedCompilationError(diagnostic))
                    {
                        continue;
                    }

                    string   errorDetailsFilename                     = diagnostic.Location.GetLineSpan().Path;
                    string   errorDetailsUnresolvedMemberName         = null;
                    string   errorDetailsMethodName                   = null;
                    string   errorDetailsLeftExpressionDocId          = null;
                    string[] errorDetailsGenericArguments             = null;
                    string[] errorDetailsArgumentTypes                = null;
                    string[] errorDetailsLeftExpressionBaseTypeDocIds = null;

                    TextSpan span = diagnostic.Location.SourceSpan;

                    var node = root.FindNode(span);
                    if (node == null)
                    {
                        continue;
                    }

                    // If the expression binds, this could just be an extension method so we will continue and not
                    // log, as it's not actually an error.
                    if (ExpressionBinds(node, semanticModel, cancellationToken, checkForExtensionMethods: true))
                    {
                        continue;
                    }
                    // This is used to get unresolved member names.  It will not alway find a member name, but has been tested to do so
                    // in the cases where the error code needs it.
                    syntaxFacts.GetNameAndArityOfSimpleName(node, out var name, out var arity);
                    errorDetailsUnresolvedMemberName = name;

                    // Here we reuse the unresolved member name field for attribute classes that can't be resolved as
                    // actually being attributes.  Could factor this into a separate field for readability later.
                    AttributeSyntax attributeSyntax = node as AttributeSyntax;
                    if (attributeSyntax != null)
                    {
                        errorDetailsUnresolvedMemberName = semanticModel.GetTypeInfo(attributeSyntax, cancellationToken).Type.GetDocumentationCommentId();
                    }

                    GenericNameSyntax genericNameSyntax = node as GenericNameSyntax;
                    if (genericNameSyntax != null)
                    {
                        List <string> genericArgumentDocIds = new List <string>();
                        foreach (var genericArgument in genericNameSyntax.TypeArgumentList.Arguments)
                        {
                            var semanticInfo = semanticModel.GetTypeInfo(genericArgument, cancellationToken);
                            if (semanticInfo.Type != null)
                            {
                                genericArgumentDocIds.Add(GetDocId(semanticInfo.Type));
                            }
                            else
                            {
                                genericArgumentDocIds.Add(UnknownGenericArgumentTypeName);
                            }
                        }

                        errorDetailsGenericArguments = genericArgumentDocIds.ToArray();
                    }

                    ArgumentSyntax argumentSyntax = node as ArgumentSyntax;
                    if (argumentSyntax != null)
                    {
                        var argumentListSyntax   = argumentSyntax.GetAncestor <BaseArgumentListSyntax>().Arguments;
                        var invocationExpression = argumentSyntax.GetAncestor <InvocationExpressionSyntax>();

                        errorDetailsArgumentTypes = (from argument in argumentListSyntax
                                                     select GetDocId(semanticModel.GetTypeInfo(argument.Expression, cancellationToken).Type)).ToArray();

                        if (invocationExpression != null)
                        {
                            var memberAccessExpression = invocationExpression.Expression as ExpressionSyntax;
                            var symbolInfo             = semanticModel.GetSymbolInfo(memberAccessExpression, cancellationToken);

                            if (symbolInfo.CandidateSymbols.Length > 0)
                            {
                                // In this case, there is argument mismatch of some sort.
                                // Here we are getting the method name of any candidate symbols, then
                                // getting the docid of the type where the argument mismatch happened and storing this in LeftExpressionDocId.

                                errorDetailsMethodName          = symbolInfo.CandidateSymbols.First().Name;
                                errorDetailsLeftExpressionDocId = GetDocId(symbolInfo.CandidateSymbols.First().ContainingType);
                            }
                        }
                    }

                    if (syntaxFacts.IsSimpleMemberAccessExpression(node.Parent))
                    {
                        var expression = node.Parent;

                        var leftExpression = syntaxFacts.GetExpressionOfMemberAccessExpression(expression);
                        if (leftExpression != null)
                        {
                            var semanticInfo = semanticModel.GetTypeInfo(leftExpression, cancellationToken);

                            var leftExpressionType = semanticInfo.Type;
                            if (leftExpressionType != null)
                            {
                                errorDetailsLeftExpressionDocId = GetDocId(leftExpressionType);

                                IEnumerable <string> baseTypeDocids = leftExpressionType.GetBaseTypes().Select(t => GetDocId(t));
                                errorDetailsLeftExpressionBaseTypeDocIds = baseTypeDocids.ToArray();
                            }
                        }
                    }

                    ret.Add(new CompilationErrorDetails(diagnostic.Id, errorDetailsFilename, errorDetailsMethodName, errorDetailsUnresolvedMemberName,
                                                        errorDetailsLeftExpressionDocId, errorDetailsLeftExpressionBaseTypeDocIds, errorDetailsGenericArguments, errorDetailsArgumentTypes));
                }

                return(ret);
            }
            catch (Exception e)
            {
                List <CompilationErrorDetails> ret = new List <CompilationErrorDetails>();
                ret.Add(new CompilationErrorDetails("EXCEPTION", e.Message, e.StackTrace, null, null, null, null, null));
                return(ret);
            }
        }