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); } }