Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/AddImport')
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/AddImport/AbstractAddImportCodeFixProvider.cs523
-rw-r--r--main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/AddImport/CSharpAddImportCodeFixProvider.cs642
2 files changed, 1165 insertions, 0 deletions
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/AddImport/AbstractAddImportCodeFixProvider.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/AddImport/AbstractAddImportCodeFixProvider.cs
new file mode 100644
index 0000000000..ca6de1af18
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/AddImport/AbstractAddImportCodeFixProvider.cs
@@ -0,0 +1,523 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.FindSymbols;
+using Microsoft.CodeAnalysis.Internal.Log;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Roslyn.Utilities;
+using ICSharpCode.NRefactory6.CSharp;
+using ICSharpCode.NRefactory6.CSharp.Refactoring;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeFixes;
+
+namespace MonoDevelop.CSharp.CodeFixes
+{
+ internal abstract partial class AbstractAddImportCodeFixProvider : CodeFixProvider
+ {
+ protected abstract bool IgnoreCase { get; }
+
+ protected abstract bool CanAddImport(SyntaxNode node, CancellationToken cancellationToken);
+ protected abstract bool CanAddImportForMethod(Diagnostic diagnostic, ref SyntaxNode node);
+ protected abstract bool CanAddImportForNamespace(Diagnostic diagnostic, ref SyntaxNode node);
+ protected abstract bool CanAddImportForQuery(Diagnostic diagnostic, ref SyntaxNode node);
+ protected abstract bool CanAddImportForType(Diagnostic diagnostic, ref SyntaxNode node);
+
+ protected abstract ISet<INamespaceSymbol> GetNamespacesInScope(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken);
+ protected abstract ITypeSymbol GetQueryClauseInfo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken);
+ protected abstract string GetDescription(INamespaceOrTypeSymbol symbol, SemanticModel semanticModel, SyntaxNode root);
+ protected abstract Task<Document> AddImportAsync(SyntaxNode contextNode, INamespaceOrTypeSymbol symbol, Document documemt, bool specialCaseSystem, CancellationToken cancellationToken);
+ protected abstract bool IsViableExtensionMethod(IMethodSymbol method, SyntaxNode expression, SemanticModel semanticModel, CancellationToken cancellationToken);
+ protected abstract IEnumerable<ITypeSymbol> GetProposedTypes(string name, List<ITypeSymbol> accessibleTypeSymbols, SemanticModel semanticModel, ISet<INamespaceSymbol> namespacesInScope);
+ internal abstract bool IsViableField(IFieldSymbol field, SyntaxNode expression, SemanticModel semanticModel, CancellationToken cancellationToken);
+ internal abstract bool IsViableProperty(IPropertySymbol property, SyntaxNode expression, SemanticModel semanticModel, CancellationToken cancellationToken);
+ internal abstract bool IsAddMethodContext(SyntaxNode node, SemanticModel semanticModel);
+
+ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
+ {
+ var document = context.Document;
+ var span = context.Span;
+ var diagnostics = context.Diagnostics;
+ var cancellationToken = context.CancellationToken;
+
+ var project = document.Project;
+ var diagnostic = diagnostics.First();
+ var model = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait (false);
+ if (model.IsFromGeneratedCode (context.CancellationToken))
+ return;
+ var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
+ var ancestors = root.FindToken(span.Start, findInsideTrivia: true).GetAncestors<SyntaxNode>();
+ if (!ancestors.Any())
+ {
+ return;
+ }
+
+ var node = ancestors.FirstOrDefault(n => n.Span.Contains(span) && n != root);
+ if (node == null)
+ {
+ return;
+ }
+
+ var placeSystemNamespaceFirst = true; //document.Project.Solution.Workspace.Options.GetOption(Microsoft.CodeAnalysis.Shared.Options.OrganizerOptions.PlaceSystemNamespaceFirst, document.Project.Language);
+
+ if (!cancellationToken.IsCancellationRequested)
+ {
+ if (this.CanAddImport(node, cancellationToken))
+ {
+ var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
+ var containingType = semanticModel.GetEnclosingNamedType(node.SpanStart, cancellationToken);
+ var containingTypeOrAssembly = containingType ?? (ISymbol)semanticModel.Compilation.Assembly;
+ var namespacesInScope = this.GetNamespacesInScope(semanticModel, node, cancellationToken);
+
+ var matchingTypesNamespaces = await this.GetNamespacesForMatchingTypesAsync(project, diagnostic, node, semanticModel, namespacesInScope, cancellationToken).ConfigureAwait(false);
+ var matchingTypes = await this.GetMatchingTypesAsync(project, diagnostic, node, semanticModel, namespacesInScope, cancellationToken).ConfigureAwait(false);
+ var matchingNamespaces = await this.GetNamespacesForMatchingNamespacesAsync(project, diagnostic, node, semanticModel, namespacesInScope, cancellationToken).ConfigureAwait(false);
+ var matchingExtensionMethodsNamespaces = await this.GetNamespacesForMatchingExtensionMethodsAsync(project, diagnostic, node, semanticModel, namespacesInScope, cancellationToken).ConfigureAwait(false);
+ var matchingFieldsAndPropertiesAsync = await this.GetNamespacesForMatchingFieldsAndPropertiesAsync(project, diagnostic, node, semanticModel, namespacesInScope, cancellationToken).ConfigureAwait(false);
+ var queryPatternsNamespaces = await this.GetNamespacesForQueryPatternsAsync(project, diagnostic, node, semanticModel, namespacesInScope, cancellationToken).ConfigureAwait(false);
+
+ if (matchingTypesNamespaces != null || matchingNamespaces != null || matchingExtensionMethodsNamespaces != null || matchingFieldsAndPropertiesAsync != null || queryPatternsNamespaces != null || matchingTypes != null)
+ {
+ matchingTypesNamespaces = matchingTypesNamespaces ?? SpecializedCollections.EmptyList<INamespaceSymbol>();
+ matchingNamespaces = matchingNamespaces ?? SpecializedCollections.EmptyList<INamespaceSymbol>();
+ matchingExtensionMethodsNamespaces = matchingExtensionMethodsNamespaces ?? SpecializedCollections.EmptyList<INamespaceSymbol>();
+ matchingFieldsAndPropertiesAsync = matchingFieldsAndPropertiesAsync ?? SpecializedCollections.EmptyList<INamespaceSymbol>();
+ queryPatternsNamespaces = queryPatternsNamespaces ?? SpecializedCollections.EmptyList<INamespaceSymbol>();
+ matchingTypes = matchingTypes ?? SpecializedCollections.EmptyList<ITypeSymbol>();
+
+ var proposedImports =
+ matchingTypesNamespaces.Cast<INamespaceOrTypeSymbol> ()
+ .Concat (matchingNamespaces.Cast<INamespaceOrTypeSymbol> ())
+ .Concat (matchingExtensionMethodsNamespaces.Cast<INamespaceOrTypeSymbol> ())
+ .Concat (matchingFieldsAndPropertiesAsync.Cast<INamespaceOrTypeSymbol> ())
+ .Concat (queryPatternsNamespaces.Cast<INamespaceOrTypeSymbol> ())
+ .Concat (matchingTypes.Cast<INamespaceOrTypeSymbol> ())
+ .Distinct ()
+ .Where (NotNull)
+ .Where (NotGlobalNamespace)
+ .ToList ();
+ proposedImports.Sort (INamespaceOrTypeSymbolExtensions.CompareNamespaceOrTypeSymbols);
+ proposedImports = proposedImports.Take (8).ToList ();
+
+ if (proposedImports.Count > 0)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ foreach (var import in proposedImports)
+ {
+ var action = new DocumentChangeAction(
+ node.Span,
+ DiagnosticSeverity.Error,
+ this.GetDescription(import, semanticModel, node),
+ (c) => this.AddImportAsync(node, import, document, placeSystemNamespaceFirst, cancellationToken)
+ );
+
+ context.RegisterCodeFix(action, diagnostic);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private async Task<IEnumerable<INamespaceSymbol>> GetNamespacesForMatchingTypesAsync(
+ Microsoft.CodeAnalysis.Project project,
+ Diagnostic diagnostic,
+ SyntaxNode node,
+ SemanticModel semanticModel,
+ ISet<INamespaceSymbol> namespacesInScope,
+ CancellationToken cancellationToken)
+ {
+ if (!this.CanAddImportForType(diagnostic, ref node))
+ {
+ return null;
+ }
+
+ string name;
+ int arity;
+ bool inAttributeContext, hasIncompleteParentMember;
+ CalculateContext(node, out name, out arity, out inAttributeContext, out hasIncompleteParentMember);
+
+ var symbols = await GetTypeSymbols(project, node, semanticModel, name, inAttributeContext, cancellationToken).ConfigureAwait(false);
+ if (symbols == null)
+ {
+ return null;
+ }
+
+ return GetNamespacesForMatchingTypesAsync(semanticModel, namespacesInScope, arity, inAttributeContext, hasIncompleteParentMember, symbols);
+ }
+
+ private IEnumerable<INamespaceSymbol> GetNamespacesForMatchingTypesAsync(SemanticModel semanticModel, ISet<INamespaceSymbol> namespacesInScope, int arity, bool inAttributeContext, bool hasIncompleteParentMember, IEnumerable<ITypeSymbol> symbols)
+ {
+ var accessibleTypeSymbols = symbols
+ .Where(s => s.ContainingSymbol is INamespaceSymbol
+ && ArityAccessibilityAndAttributeContextAreCorrect(
+ semanticModel, s, arity,
+ inAttributeContext, hasIncompleteParentMember))
+ .ToList();
+
+ return GetProposedNamespaces(
+ accessibleTypeSymbols.Select(s => s.ContainingNamespace),
+ semanticModel,
+ namespacesInScope);
+ }
+
+ private async Task<IEnumerable<ITypeSymbol>> GetMatchingTypesAsync(
+ Microsoft.CodeAnalysis.Project project,
+ Diagnostic diagnostic,
+ SyntaxNode node,
+ SemanticModel semanticModel,
+ ISet<INamespaceSymbol> namespacesInScope,
+ CancellationToken cancellationToken)
+ {
+ if (!this.CanAddImportForType(diagnostic, ref node))
+ {
+ return null;
+ }
+
+ string name;
+ int arity;
+ bool inAttributeContext, hasIncompleteParentMember;
+ CalculateContext(node, out name, out arity, out inAttributeContext, out hasIncompleteParentMember);
+
+ var symbols = await GetTypeSymbols(project, node, semanticModel, name, inAttributeContext, cancellationToken).ConfigureAwait(false);
+ if (symbols == null)
+ {
+ return null;
+ }
+
+ return GetMatchingTypes(semanticModel, namespacesInScope, name, arity, inAttributeContext, symbols, hasIncompleteParentMember);
+ }
+
+ private async Task<IEnumerable<INamespaceSymbol>> GetNamespacesForMatchingNamespacesAsync(
+ Microsoft.CodeAnalysis.Project project,
+ Diagnostic diagnostic,
+ SyntaxNode node,
+ SemanticModel semanticModel,
+ ISet<INamespaceSymbol> namespacesInScope,
+ CancellationToken cancellationToken)
+ {
+ if (!this.CanAddImportForNamespace(diagnostic, ref node))
+ {
+ return null;
+ }
+
+ string name;
+ int arity;
+ node.GetNameAndArityOfSimpleName(out name, out arity);
+
+ if (ExpressionBinds(node, semanticModel, cancellationToken))
+ {
+ return null;
+ }
+
+ var symbols = await SymbolFinder.FindDeclarationsAsync(
+ project, name, this.IgnoreCase, SymbolFilter.Namespace, cancellationToken).ConfigureAwait(false);
+
+ return GetProposedNamespaces(
+ symbols.OfType<INamespaceSymbol>().Select(n => n.ContainingNamespace),
+ semanticModel,
+ namespacesInScope);
+ }
+
+ private async Task<IEnumerable<INamespaceSymbol>> GetNamespacesForMatchingExtensionMethodsAsync(
+ Microsoft.CodeAnalysis.Project project,
+ Diagnostic diagnostic,
+ SyntaxNode node,
+ SemanticModel semanticModel,
+ ISet<INamespaceSymbol> namespacesInScope,
+ CancellationToken cancellationToken)
+ {
+ if (!this.CanAddImportForMethod(diagnostic, ref node))
+ {
+ return null;
+ }
+
+ var expression = node.Parent;
+
+ var extensionMethods = SpecializedCollections.EmptyEnumerable<INamespaceSymbol>();
+ var symbols = await GetSymbolsAsync(project, node, semanticModel, cancellationToken).ConfigureAwait(false);
+ if (symbols != null)
+ {
+ extensionMethods = FilterForExtensionMethods(semanticModel, namespacesInScope, expression, symbols, cancellationToken);
+ }
+
+ var addMethods = SpecializedCollections.EmptyEnumerable<INamespaceSymbol>();
+ var methodSymbols = await GetAddMethodsAsync(project, diagnostic, node, semanticModel, namespacesInScope, expression, cancellationToken).ConfigureAwait(false);
+ if (methodSymbols != null)
+ {
+ addMethods = GetProposedNamespaces(
+ methodSymbols.Select(s => s.ContainingNamespace),
+ semanticModel,
+ namespacesInScope);
+ }
+
+ return extensionMethods.Concat(addMethods);
+ }
+
+ private async Task<IEnumerable<INamespaceSymbol>> GetNamespacesForMatchingFieldsAndPropertiesAsync(
+ Microsoft.CodeAnalysis.Project project,
+ Diagnostic diagnostic,
+ SyntaxNode node,
+ SemanticModel semanticModel,
+ ISet<INamespaceSymbol> namespacesInScope,
+ CancellationToken cancellationToken)
+ {
+ if (!this.CanAddImportForMethod(diagnostic, ref node))
+ {
+ return null;
+ }
+
+ var expression = node.Parent;
+
+ var symbols = await GetSymbolsAsync(project, node, semanticModel, cancellationToken).ConfigureAwait(false);
+
+ if (symbols != null)
+ {
+ return FilterForFieldsAndProperties(semanticModel, namespacesInScope, expression, symbols, cancellationToken);
+ }
+
+ return null;
+ }
+
+ private IEnumerable<INamespaceSymbol> FilterForFieldsAndProperties(SemanticModel semanticModel, ISet<INamespaceSymbol> namespacesInScope, SyntaxNode expression, IEnumerable<ISymbol> symbols, CancellationToken cancellationToken)
+ {
+ var propertySymbols = symbols
+ .OfType<IPropertySymbol>()
+ .Where(property => property.ContainingType?.IsAccessibleWithin(semanticModel.Compilation.Assembly) == true &&
+ IsViableProperty(property, expression, semanticModel, cancellationToken))
+ .ToList();
+
+ var fieldSymbols = symbols
+ .OfType<IFieldSymbol>()
+ .Where(field => field.ContainingType?.IsAccessibleWithin(semanticModel.Compilation.Assembly) == true &&
+ IsViableField(field, expression, semanticModel, cancellationToken))
+ .ToList();
+
+ return GetProposedNamespaces(
+ propertySymbols.Select(s => s.ContainingNamespace).Concat(fieldSymbols.Select(s => s.ContainingNamespace)),
+ semanticModel,
+ namespacesInScope);
+ }
+
+ private Task<IEnumerable<ISymbol>> GetSymbolsAsync(
+ Microsoft.CodeAnalysis.Project project,
+ SyntaxNode node,
+ SemanticModel semanticModel,
+ CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // See if the name binds. If it does, there's nothing further we need to do.
+ if (ExpressionBinds(node, semanticModel, cancellationToken, checkForExtensionMethods: true))
+ {
+ return Task.FromResult (Enumerable.Empty<ISymbol>());
+ }
+
+ string name;
+ int arity;
+ node.GetNameAndArityOfSimpleName(out name, out arity);
+ if (name == null)
+ {
+ return Task.FromResult (Enumerable.Empty<ISymbol>());
+ }
+
+ return SymbolFinder.FindDeclarationsAsync(project, name, this.IgnoreCase, SymbolFilter.Member, cancellationToken);
+ }
+
+ private async Task<IEnumerable<IMethodSymbol>> GetAddMethodsAsync(
+ Microsoft.CodeAnalysis.Project project,
+ Diagnostic diagnostic,
+ SyntaxNode node,
+ SemanticModel semanticModel,
+ ISet<INamespaceSymbol> namespacesInScope,
+ SyntaxNode expression,
+ CancellationToken cancellationToken)
+ {
+ string name;
+ int arity;
+ node.GetNameAndArityOfSimpleName(out name, out arity);
+ if (name != null)
+ {
+ return SpecializedCollections.EmptyEnumerable<IMethodSymbol>();
+ }
+
+ if (IsAddMethodContext(node, semanticModel))
+ {
+ var symbols = await SymbolFinder.FindDeclarationsAsync(project, "Add", this.IgnoreCase, SymbolFilter.Member, cancellationToken).ConfigureAwait(false);
+ return symbols
+ .OfType<IMethodSymbol>()
+ .Where(method => method.IsExtensionMethod &&
+ method.ContainingType?.IsAccessibleWithin(semanticModel.Compilation.Assembly) == true &&
+ IsViableExtensionMethod(method, expression, semanticModel, cancellationToken));
+ }
+
+ return SpecializedCollections.EmptyEnumerable<IMethodSymbol>();
+ }
+
+ private IEnumerable<INamespaceSymbol> FilterForExtensionMethods(SemanticModel semanticModel, ISet<INamespaceSymbol> namespacesInScope, SyntaxNode expression, IEnumerable<ISymbol> symbols, CancellationToken cancellationToken)
+ {
+ var extensionMethodSymbols = symbols
+ .OfType<IMethodSymbol>()
+ .Where(method => method.IsExtensionMethod &&
+ method.ContainingType?.IsAccessibleWithin(semanticModel.Compilation.Assembly) == true &&
+ IsViableExtensionMethod(method, expression, semanticModel, cancellationToken))
+ .ToList();
+
+ return GetProposedNamespaces(
+ extensionMethodSymbols.Select(s => s.ContainingNamespace),
+ semanticModel,
+ namespacesInScope);
+ }
+
+ private async Task<IEnumerable<INamespaceSymbol>> GetNamespacesForQueryPatternsAsync(
+ Microsoft.CodeAnalysis.Project project,
+ Diagnostic diagnostic,
+ SyntaxNode node,
+ SemanticModel semanticModel,
+ ISet<INamespaceSymbol> namespacesInScope,
+ CancellationToken cancellationToken)
+ {
+ if (!this.CanAddImportForQuery(diagnostic, ref node))
+ {
+ return null;
+ }
+
+ ITypeSymbol type = this.GetQueryClauseInfo(semanticModel, node, cancellationToken);
+ if (type == null)
+ {
+ return null;
+ }
+
+ // find extension methods named "Select"
+ var symbols = await SymbolFinder.FindDeclarationsAsync(project, "Select", this.IgnoreCase, SymbolFilter.Member, cancellationToken).ConfigureAwait(false);
+
+ var extensionMethodSymbols = symbols
+ .OfType<IMethodSymbol>()
+ .Where(s => s.IsExtensionMethod && IsViableExtensionMethod(type, s))
+ .ToList();
+
+ return GetProposedNamespaces(
+ extensionMethodSymbols.Select(s => s.ContainingNamespace),
+ semanticModel,
+ namespacesInScope);
+ }
+
+ private bool IsViableExtensionMethod(
+ ITypeSymbol typeSymbol,
+ IMethodSymbol method)
+ {
+ return typeSymbol != null && method.ReduceExtensionMethod(typeSymbol) != null;
+ }
+
+ private static bool ArityAccessibilityAndAttributeContextAreCorrect(
+ SemanticModel semanticModel,
+ ITypeSymbol symbol,
+ int arity,
+ bool inAttributeContext,
+ bool hasIncompleteParentMember)
+ {
+ return (arity == 0 || symbol.GetArity() == arity || hasIncompleteParentMember)
+ && symbol.IsAccessibleWithin(semanticModel.Compilation.Assembly)
+ && (!inAttributeContext || symbol.IsAttribute());
+ }
+
+ private async Task<IEnumerable<ITypeSymbol>> GetTypeSymbols(
+ Microsoft.CodeAnalysis.Project project,
+ SyntaxNode node,
+ SemanticModel semanticModel,
+ string name,
+ bool inAttributeContext,
+ CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return null;
+ }
+
+ if (ExpressionBinds(node, semanticModel, cancellationToken))
+ {
+ return null;
+ }
+
+ var symbols = await SymbolFinder.FindDeclarationsAsync(project, name, this.IgnoreCase, SymbolFilter.Type, cancellationToken).ConfigureAwait(false);
+
+ // also lookup type symbols with the "Attribute" suffix.
+ if (inAttributeContext)
+ {
+ symbols = symbols.Concat(
+ await SymbolFinder.FindDeclarationsAsync(project, name + "Attribute", this.IgnoreCase, SymbolFilter.Type, cancellationToken).ConfigureAwait(false));
+ }
+
+ return symbols.OfType<ITypeSymbol>();
+ }
+
+ private IEnumerable<ITypeSymbol> GetMatchingTypes(SemanticModel semanticModel, ISet<INamespaceSymbol> namespacesInScope, string name, int arity, bool inAttributeContext, IEnumerable<ITypeSymbol> symbols, bool hasIncompleteParentMember)
+ {
+ var accessibleTypeSymbols = symbols
+ .Where(s => ArityAccessibilityAndAttributeContextAreCorrect(
+ semanticModel, s, arity,
+ inAttributeContext, hasIncompleteParentMember))
+ .ToList();
+
+ return GetProposedTypes(
+ name,
+ accessibleTypeSymbols,
+ semanticModel,
+ namespacesInScope);
+ }
+
+ private static void CalculateContext(SyntaxNode node, out string name, out int arity, out bool inAttributeContext, out bool hasIncompleteParentMember)
+ {
+ // Has to be a simple identifier or generic name.
+ node.GetNameAndArityOfSimpleName(out name, out arity);
+
+ inAttributeContext = node.IsAttributeName();
+ hasIncompleteParentMember = node.HasIncompleteParentMember();
+ }
+
+ protected bool ExpressionBinds(SyntaxNode expression, SemanticModel semanticModel, CancellationToken cancellationToken, bool checkForExtensionMethods = false)
+ {
+ // See if the name binds to something other then the error type. If it does, there's nothing further we need to do.
+ // For extension methods, however, we will continue to search if there exists any better matched method.
+ cancellationToken.ThrowIfCancellationRequested();
+ var symbolInfo = semanticModel.GetSymbolInfo(expression, cancellationToken);
+ if (symbolInfo.CandidateReason == CandidateReason.OverloadResolutionFailure && !checkForExtensionMethods)
+ {
+ return true;
+ }
+
+ return symbolInfo.Symbol != null;
+ }
+
+ protected IEnumerable<INamespaceSymbol> GetProposedNamespaces(
+ IEnumerable<INamespaceSymbol> namespaces,
+ SemanticModel semanticModel,
+ ISet<INamespaceSymbol> namespacesInScope)
+ {
+ // We only want to offer to add a using if we don't already have one.
+ return
+ namespaces.Where(n => !n.IsGlobalNamespace)
+ .Select(n => semanticModel.Compilation.GetCompilationNamespace(n) ?? n)
+ .Where(n => n != null && !namespacesInScope.Contains(n));
+ }
+
+ private static bool NotGlobalNamespace(INamespaceOrTypeSymbol symbol)
+ {
+ return symbol.IsNamespace ? !((INamespaceSymbol)symbol).IsGlobalNamespace : true;
+ }
+
+ private static bool NotNull(INamespaceOrTypeSymbol symbol)
+ {
+ return symbol != null;
+ }
+
+
+ }
+}
diff --git a/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/AddImport/CSharpAddImportCodeFixProvider.cs b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/AddImport/CSharpAddImportCodeFixProvider.cs
new file mode 100644
index 0000000000..994bd39571
--- /dev/null
+++ b/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/AddImport/CSharpAddImportCodeFixProvider.cs
@@ -0,0 +1,642 @@
+// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Composition;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Extensions;
+using Microsoft.CodeAnalysis.CSharp.Symbols;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.CSharp.Utilities;
+using Microsoft.CodeAnalysis.FindSymbols;
+using Microsoft.CodeAnalysis.Formatting;
+using Microsoft.CodeAnalysis.LanguageServices;
+using Microsoft.CodeAnalysis.Shared.Extensions;
+using Microsoft.CodeAnalysis.Simplification;
+using Roslyn.Utilities;
+using MonoDevelop.CSharp.CodeFixes;
+using ICSharpCode.NRefactory6.CSharp;
+
+namespace MonoDevelop.CSharp.CodeFixes
+{
+ [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AddUsingOrImport), Shared]
+ class CSharpAddImportCodeFixProvider : AbstractAddImportCodeFixProvider
+ {
+ /// <summary>
+ /// name does not exist in context
+ /// </summary>
+ private const string CS0103 = "CS0103";
+
+ /// <summary>
+ /// type or namespace could not be found
+ /// </summary>
+ private const string CS0246 = "CS0246";
+
+ /// <summary>
+ /// wrong number of type args
+ /// </summary>
+ private const string CS0305 = "CS0305";
+
+ /// <summary>
+ /// type does not contain a definition of method or extension method
+ /// </summary>
+ private const string CS1061 = "CS1061";
+
+ /// <summary>
+ /// cannot find implementation of query pattern
+ /// </summary>
+ private const string CS1935 = "CS1935";
+
+ /// <summary>
+ /// The non-generic type 'A' cannot be used with type arguments
+ /// </summary>
+ private const string CS0308 = "CS0308";
+
+ /// <summary>
+ /// 'A' is inaccessible due to its protection level
+ /// </summary>
+ private const string CS0122 = "CS0122";
+
+ /// <summary>
+ /// The using alias 'A' cannot be used with type arguments
+ /// </summary>
+ private const string CS0307 = "CS0307";
+
+ /// <summary>
+ /// 'A' is not an attribute class
+ /// </summary>
+ private const string CS0616 = "CS0616";
+
+ /// <summary>
+ /// ; expected.
+ /// </summary>
+ private const string CS1002 = "CS1002";
+
+ /// <summary>
+ /// Syntax error, 'A' expected
+ /// </summary>
+ private const string CS1003 = "CS1003";
+
+ /// <summary>
+ /// cannot convert from 'int' to 'string'
+ /// </summary>
+ private const string CS1503 = "CS1503";
+
+ /// <summary>
+ /// XML comment on 'construct' has syntactically incorrect cref attribute 'name'
+ /// </summary>
+ private const string CS1574 = "CS1574";
+
+ /// <summary>
+ /// Invalid type for parameter 'parameter number' in XML comment cref attribute
+ /// </summary>
+ private const string CS1580 = "CS1580";
+
+ /// <summary>
+ /// Invalid return type in XML comment cref attribute
+ /// </summary>
+ private const string CS1581 = "CS1581";
+
+ /// <summary>
+ /// XML comment has syntactically incorrect cref attribute
+ /// </summary>
+ private const string CS1584 = "CS1584";
+
+ public override ImmutableArray<string> FixableDiagnosticIds
+ {
+ get
+ {
+ return ImmutableArray.Create(
+ CS0103,
+ CS0246,
+ CS0305,
+ CS1061,
+ CS1935,
+ CS0308,
+ CS0122,
+ CS0307,
+ CS0616,
+ CS1002,
+ CS1003,
+ CS1503,
+ CS1574,
+ CS1580,
+ CS1581,
+ CS1584);
+ }
+ }
+
+ protected override bool IgnoreCase
+ {
+ get { return false; }
+ }
+
+ protected override bool CanAddImport(SyntaxNode node, CancellationToken cancellationToken)
+ {
+ if (cancellationToken.IsCancellationRequested)
+ {
+ return false;
+ }
+
+ return node.CanAddUsingDirectives(cancellationToken);
+ }
+
+ protected override bool CanAddImportForMethod(Diagnostic diagnostic, ref SyntaxNode node)
+ {
+ switch (diagnostic.Id)
+ {
+ case CS1061:
+ if (node.IsKind(SyntaxKind.ConditionalAccessExpression))
+ {
+ node = (node as ConditionalAccessExpressionSyntax).WhenNotNull;
+ }
+ else if (node.IsKind(SyntaxKind.MemberBindingExpression))
+ {
+ node = (node as MemberBindingExpressionSyntax).Name;
+ }
+ else if (node.Parent.IsKind(SyntaxKind.CollectionInitializerExpression))
+ {
+ return true;
+ }
+
+ break;
+ case CS0122:
+ break;
+
+ case CS1503:
+ //// look up its corresponding method name
+ var parent = node.GetAncestor<InvocationExpressionSyntax>();
+ if (parent == null)
+ {
+ return false;
+ }
+
+ var method = parent.Expression as MemberAccessExpressionSyntax;
+ if (method != null)
+ {
+ node = method.Name;
+ }
+
+ break;
+
+ default:
+ return false;
+ }
+
+ var simpleName = node as SimpleNameSyntax;
+ if (!simpleName.IsParentKind(SyntaxKind.SimpleMemberAccessExpression) &&
+ !simpleName.IsParentKind(SyntaxKind.MemberBindingExpression))
+ {
+ return false;
+ }
+
+ var memberAccess = simpleName.Parent as MemberAccessExpressionSyntax;
+ var memberBinding = simpleName.Parent as MemberBindingExpressionSyntax;
+ if (memberAccess.IsParentKind(SyntaxKind.SimpleMemberAccessExpression) ||
+ memberAccess.IsParentKind(SyntaxKind.ElementAccessExpression) ||
+ memberBinding.IsParentKind(SyntaxKind.SimpleMemberAccessExpression) ||
+ memberBinding.IsParentKind(SyntaxKind.ElementAccessExpression))
+ {
+ return false;
+ }
+
+ if (!node.IsMemberAccessExpressionName())
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ protected override bool CanAddImportForNamespace(Diagnostic diagnostic, ref SyntaxNode node)
+ {
+ return false;
+ }
+
+ protected override bool CanAddImportForQuery(Diagnostic diagnostic, ref SyntaxNode node)
+ {
+ if (diagnostic.Id != CS1935)
+ {
+ return false;
+ }
+
+ return node.AncestorsAndSelf().Any(n => n is QueryExpressionSyntax && !(n.Parent is QueryContinuationSyntax));
+ }
+
+ protected override bool CanAddImportForType(Diagnostic diagnostic, ref SyntaxNode node)
+ {
+ switch (diagnostic.Id)
+ {
+ case CS0103:
+ case CS0246:
+ case CS0305:
+ case CS0308:
+ case CS0122:
+ case CS0307:
+ case CS0616:
+ case CS1003:
+ case CS1580:
+ case CS1581:
+ break;
+
+ case CS1002:
+ //// only lookup errors inside ParenthesizedLambdaExpression e.g., () => { ... }
+ if (node.Ancestors().OfType<ParenthesizedLambdaExpressionSyntax>().Any())
+ {
+ if (node is SimpleNameSyntax)
+ {
+ break;
+ }
+ else if (node is BlockSyntax || node is MemberAccessExpressionSyntax || node is BinaryExpressionSyntax)
+ {
+ var last = node.DescendantNodes().OfType<SimpleNameSyntax>().LastOrDefault();
+ if (!TryFindStandaloneType(ref node))
+ {
+ node = node.DescendantNodes().OfType<SimpleNameSyntax>().FirstOrDefault();
+ }
+ else
+ {
+ node = last;
+ }
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ break;
+
+ case CS1574:
+ case CS1584:
+ var cref = node as QualifiedCrefSyntax;
+ if (cref != null)
+ {
+ node = cref.Container;
+ }
+
+ break;
+
+ default:
+ return false;
+ }
+
+ return TryFindStandaloneType(ref node);
+ }
+
+ private static bool TryFindStandaloneType(ref SyntaxNode node)
+ {
+ var qn = node as QualifiedNameSyntax;
+ if (qn != null)
+ {
+ node = GetLeftMostSimpleName(qn);
+ }
+
+ var simpleName = node as SimpleNameSyntax;
+ return simpleName.LooksLikeStandaloneTypeName();
+ }
+
+ private static SimpleNameSyntax GetLeftMostSimpleName(QualifiedNameSyntax qn)
+ {
+ while (qn != null)
+ {
+ var left = qn.Left;
+ var simpleName = left as SimpleNameSyntax;
+ if (simpleName != null)
+ {
+ return simpleName;
+ }
+
+ qn = left as QualifiedNameSyntax;
+ }
+
+ return null;
+ }
+
+ protected override ISet<INamespaceSymbol> GetNamespacesInScope(
+ SemanticModel semanticModel,
+ SyntaxNode node,
+ CancellationToken cancellationToken)
+ {
+ return semanticModel.GetUsingNamespacesInScope(node);
+ }
+
+ protected override ITypeSymbol GetQueryClauseInfo(
+ SemanticModel semanticModel,
+ SyntaxNode node,
+ CancellationToken cancellationToken)
+ {
+ var query = node.AncestorsAndSelf().OfType<QueryExpressionSyntax>().First();
+
+ if (InfoBoundSuccessfully(semanticModel.GetQueryClauseInfo(query.FromClause, cancellationToken)))
+ {
+ return null;
+ }
+
+ foreach (var clause in query.Body.Clauses)
+ {
+ if (InfoBoundSuccessfully(semanticModel.GetQueryClauseInfo(clause, cancellationToken)))
+ {
+ return null;
+ }
+ }
+
+ if (InfoBoundSuccessfully(semanticModel.GetSymbolInfo(query.Body.SelectOrGroup, cancellationToken)))
+ {
+ return null;
+ }
+
+ var fromClause = query.FromClause;
+ return semanticModel.GetTypeInfo(fromClause.Expression, cancellationToken).Type;
+ }
+
+ private bool InfoBoundSuccessfully(SymbolInfo symbolInfo)
+ {
+ return InfoBoundSuccessfully(symbolInfo.Symbol);
+ }
+
+ private bool InfoBoundSuccessfully(QueryClauseInfo semanticInfo)
+ {
+ return InfoBoundSuccessfully(semanticInfo.OperationInfo);
+ }
+
+ private static bool InfoBoundSuccessfully(ISymbol operation)
+ {
+ operation = operation.GetOriginalUnreducedDefinition();
+ return operation != null;
+ }
+
+ protected override string GetDescription(INamespaceOrTypeSymbol namespaceSymbol, SemanticModel semanticModel, SyntaxNode contextNode)
+ {
+ var root = GetCompilationUnitSyntaxNode(contextNode);
+
+ // No localization necessary
+ string externAliasString;
+ if (TryGetExternAliasString(namespaceSymbol, semanticModel, root, out externAliasString))
+ {
+ return string.Format ("extern alias {0};", externAliasString);
+ }
+
+ string namespaceString;
+ if (TryGetNamespaceString(namespaceSymbol, root, false, null, out namespaceString))
+ {
+ return string.Format ("using {0};", namespaceString);
+ }
+
+ // If we get here then neither a namespace or a an extern alias can be added.
+ // There is no valid string to show to the user and there is
+ // likely a bug in that we should know about.
+ throw new InvalidOperationException ();
+ }
+
+ protected override async Task<Document> AddImportAsync(
+ SyntaxNode contextNode,
+ INamespaceOrTypeSymbol namespaceSymbol,
+ Document document,
+ bool placeSystemNamespaceFirst,
+ CancellationToken cancellationToken)
+ {
+ var root = GetCompilationUnitSyntaxNode(contextNode, cancellationToken);
+ var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
+ var simpleUsingDirective = GetUsingDirective(root, namespaceSymbol, semanticModel, fullyQualify: false);
+ var externAliasUsingDirective = GetExternAliasUsingDirective(root, namespaceSymbol, semanticModel);
+ if (externAliasUsingDirective != null)
+ {
+ root = root.AddExterns(
+ externAliasUsingDirective
+ .WithAdditionalAnnotations(Formatter.Annotation));
+ }
+
+ if (simpleUsingDirective != null)
+ {
+ // Because of the way usings can be nested inside of namespace declarations,
+ // we need to check if the usings must be fully qualified so as not to be
+ // ambiguous with the containing namespace.
+ if (UsingsAreContainedInNamespace(contextNode))
+ {
+ // When we add usings we try and place them, as best we can, where the user
+ // wants them according to their settings. This means we can't just add the fully-
+ // qualified usings and expect the simplifier to take care of it, the usings have to be
+ // simplified before we attempt to add them to the document.
+ // You might be tempted to think that we could call
+ // AddUsings -> Simplifier -> SortUsings
+ // But this will clobber the users using settings without asking. Instead we create a new
+ // Document and check if our using can be simplified. Worst case we need to back out the
+ // fully qualified change and reapply with the simple name.
+ var fullyQualifiedUsingDirective = GetUsingDirective(root, namespaceSymbol, semanticModel, fullyQualify: true);
+ SyntaxNode newRoot = root.AddUsingDirective(
+ fullyQualifiedUsingDirective, contextNode, placeSystemNamespaceFirst,
+ Formatter.Annotation);
+ var newDocument = document.WithSyntaxRoot(newRoot);
+ var newSemanticModel = await newDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
+ newRoot = await newDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
+ var newUsing = newRoot
+ .DescendantNodes().OfType<UsingDirectiveSyntax>().Where(uds => uds.IsEquivalentTo(fullyQualifiedUsingDirective, topLevel: true)).Single();
+ var speculationAnalyzer = new SpeculationAnalyzer(newUsing.Name, simpleUsingDirective.Name, newSemanticModel, cancellationToken);
+ if (speculationAnalyzer.ReplacementChangesSemantics())
+ {
+ // Not fully qualifying the using causes to refer to a different namespace so we need to keep it as is.
+ return newDocument;
+ }
+ else
+ {
+ // It does not matter if it is fully qualified or simple so lets return the simple name.
+ return document.WithSyntaxRoot(root.AddUsingDirective(
+ simpleUsingDirective, contextNode, placeSystemNamespaceFirst,
+ Formatter.Annotation));
+ }
+ }
+ else
+ {
+ // simple form
+ return document.WithSyntaxRoot(root.AddUsingDirective(
+ simpleUsingDirective, contextNode, placeSystemNamespaceFirst,
+ Formatter.Annotation));
+ }
+ }
+
+ return document.WithSyntaxRoot(root);
+ }
+
+ private static ExternAliasDirectiveSyntax GetExternAliasUsingDirective(CompilationUnitSyntax root, INamespaceOrTypeSymbol namespaceSymbol, SemanticModel semanticModel)
+ {
+ string externAliasString;
+ if (TryGetExternAliasString(namespaceSymbol, semanticModel, root, out externAliasString))
+ {
+ return SyntaxFactory.ExternAliasDirective(SyntaxFactory.Identifier(externAliasString));
+ }
+
+ return null;
+ }
+
+ private UsingDirectiveSyntax GetUsingDirective(CompilationUnitSyntax root, INamespaceOrTypeSymbol namespaceSymbol, SemanticModel semanticModel, bool fullyQualify)
+ {
+ string namespaceString;
+ string externAliasString;
+ TryGetExternAliasString(namespaceSymbol, semanticModel, root, out externAliasString);
+ if (externAliasString != null)
+ {
+ if (TryGetNamespaceString(namespaceSymbol, root, false, externAliasString, out namespaceString))
+ {
+ return SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(namespaceString));
+ }
+
+ return null;
+ }
+
+ if (TryGetNamespaceString(namespaceSymbol, root, fullyQualify, null, out namespaceString))
+ {
+ return SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(namespaceString));
+ }
+
+ return null;
+ }
+
+ private bool UsingsAreContainedInNamespace(SyntaxNode contextNode)
+ {
+ return contextNode.GetAncestor<NamespaceDeclarationSyntax>()?.DescendantNodes().OfType<UsingDirectiveSyntax>().FirstOrDefault() != null;
+ }
+
+ private static bool TryGetExternAliasString(INamespaceOrTypeSymbol namespaceSymbol, SemanticModel semanticModel, CompilationUnitSyntax root, out string externAliasString)
+ {
+ externAliasString = null;
+ var metadataReference = semanticModel.Compilation.GetMetadataReference(namespaceSymbol.ContainingAssembly);
+ if (metadataReference == null)
+ {
+ return false;
+ }
+
+ var properties = metadataReference.Properties;
+ var aliases = properties.Aliases;
+ if (aliases.IsDefaultOrEmpty)
+ {
+ return false;
+ }
+
+ aliases = properties.Aliases.Where(a => a != MetadataReferenceProperties.GlobalAlias).ToImmutableArray();
+ if (!aliases.Any())
+ {
+ return false;
+ }
+
+ externAliasString = aliases.First();
+ return ShouldAddExternAlias(aliases, root);
+ }
+
+ private static bool TryGetNamespaceString(INamespaceOrTypeSymbol namespaceSymbol, CompilationUnitSyntax root, bool fullyQualify, string alias, out string namespaceString)
+ {
+ namespaceString = fullyQualify
+ ? namespaceSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
+ : namespaceSymbol.ToDisplayString();
+
+ if (alias != null)
+ {
+ namespaceString = alias + "::" + namespaceString;
+ }
+
+ return ShouldAddUsing(namespaceString, root);
+ }
+
+ private static bool ShouldAddExternAlias(ImmutableArray<string> aliases, CompilationUnitSyntax root)
+ {
+ var identifiers = root.DescendantNodes().OfType<ExternAliasDirectiveSyntax>().Select(e => e.Identifier.ToString());
+ var externAliases = aliases.Where(a => identifiers.Contains(a));
+ return !externAliases.Any();
+ }
+
+ private static bool ShouldAddUsing(string usingDirective, CompilationUnitSyntax root)
+ {
+ return !root.Usings.Select(u => u.Name.ToString()).Contains(usingDirective);
+ }
+
+ private static CompilationUnitSyntax GetCompilationUnitSyntaxNode(SyntaxNode contextNode, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ return (CompilationUnitSyntax)contextNode.SyntaxTree.GetRoot(cancellationToken);
+ }
+
+ protected override bool IsViableExtensionMethod(IMethodSymbol method, SyntaxNode expression, SemanticModel semanticModel, CancellationToken cancellationToken)
+ {
+ var leftExpression = expression.GetExpressionOfMemberAccessExpression() ?? expression.GetExpressionOfConditionalMemberAccessExpression();
+ if (leftExpression == null)
+ {
+ if (expression.IsKind(SyntaxKind.CollectionInitializerExpression))
+ {
+ leftExpression = expression.GetAncestor<ObjectCreationExpressionSyntax>();
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ var semanticInfo = semanticModel.GetTypeInfo(leftExpression, cancellationToken);
+ var leftExpressionType = semanticInfo.Type;
+
+ return leftExpressionType != null && method.ReduceExtensionMethod(leftExpressionType) != null;
+ }
+
+ protected override IEnumerable<ITypeSymbol> GetProposedTypes(string name, List<ITypeSymbol> accessibleTypeSymbols, SemanticModel semanticModel, ISet<INamespaceSymbol> namespacesInScope)
+ {
+ if (accessibleTypeSymbols == null)
+ {
+ yield break;
+ }
+
+ foreach (var typeSymbol in accessibleTypeSymbols)
+ {
+ if ((typeSymbol != null) && (typeSymbol.ContainingType != null) && typeSymbol.ContainingType.IsStatic)
+ {
+ yield return typeSymbol.ContainingType;
+ }
+ }
+ }
+
+ internal override bool IsViableField(IFieldSymbol field, SyntaxNode expression, SemanticModel semanticModel, CancellationToken cancellationToken)
+ {
+ return IsViablePropertyOrField(field, expression, semanticModel, cancellationToken);
+ }
+
+ internal override bool IsViableProperty(IPropertySymbol property, SyntaxNode expression, SemanticModel semanticModel, CancellationToken cancellationToken)
+ {
+ return IsViablePropertyOrField(property, expression, semanticModel, cancellationToken);
+ }
+
+ private bool IsViablePropertyOrField(ISymbol propertyOrField, SyntaxNode expression, SemanticModel semanticModel, CancellationToken cancellationToken)
+ {
+ if (!propertyOrField.IsStatic)
+ {
+ return false;
+ }
+
+ var leftName = (expression as MemberAccessExpressionSyntax)?.Expression as SimpleNameSyntax;
+ if (leftName == null)
+ {
+ return false;
+ }
+
+ return string.Compare(propertyOrField.ContainingType.Name, leftName.Identifier.Text, this.IgnoreCase) == 0;
+ }
+
+ internal override bool IsAddMethodContext(SyntaxNode node, SemanticModel semanticModel)
+ {
+ if (node.Parent.IsKind(SyntaxKind.CollectionInitializerExpression))
+ {
+ var objectCreationExpressionSyntax = node.GetAncestor<ObjectCreationExpressionSyntax>();
+ if (objectCreationExpressionSyntax == null)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+ }
+}