/main/src/addins/CSharpBinding/MonoDevelop.CSharp.CodeFixes/AddImport/CSharpAddImportCodeFixProvider.cs

http://github.com/mono/monodevelop · C# · 642 lines · 480 code · 92 blank · 70 comment · 78 complexity · 634f0e6f1705c201ab0f677442142807 MD5 · raw file

  1. // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Collections.Immutable;
  5. using System.Composition;
  6. using System.Linq;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. using Microsoft.CodeAnalysis;
  10. using Microsoft.CodeAnalysis.CodeFixes;
  11. using Microsoft.CodeAnalysis.CSharp;
  12. using Microsoft.CodeAnalysis.CSharp.Extensions;
  13. using Microsoft.CodeAnalysis.CSharp.Symbols;
  14. using Microsoft.CodeAnalysis.CSharp.Syntax;
  15. using Microsoft.CodeAnalysis.CSharp.Utilities;
  16. using Microsoft.CodeAnalysis.FindSymbols;
  17. using Microsoft.CodeAnalysis.Formatting;
  18. using Microsoft.CodeAnalysis.LanguageServices;
  19. using Microsoft.CodeAnalysis.Shared.Extensions;
  20. using Microsoft.CodeAnalysis.Simplification;
  21. using Roslyn.Utilities;
  22. using MonoDevelop.CSharp.CodeFixes;
  23. using ICSharpCode.NRefactory6.CSharp;
  24. namespace MonoDevelop.CSharp.CodeFixes
  25. {
  26. [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AddUsingOrImport), Shared]
  27. class CSharpAddImportCodeFixProvider : AbstractAddImportCodeFixProvider
  28. {
  29. /// <summary>
  30. /// name does not exist in context
  31. /// </summary>
  32. private const string CS0103 = "CS0103";
  33. /// <summary>
  34. /// type or namespace could not be found
  35. /// </summary>
  36. private const string CS0246 = "CS0246";
  37. /// <summary>
  38. /// wrong number of type args
  39. /// </summary>
  40. private const string CS0305 = "CS0305";
  41. /// <summary>
  42. /// type does not contain a definition of method or extension method
  43. /// </summary>
  44. private const string CS1061 = "CS1061";
  45. /// <summary>
  46. /// cannot find implementation of query pattern
  47. /// </summary>
  48. private const string CS1935 = "CS1935";
  49. /// <summary>
  50. /// The non-generic type 'A' cannot be used with type arguments
  51. /// </summary>
  52. private const string CS0308 = "CS0308";
  53. /// <summary>
  54. /// 'A' is inaccessible due to its protection level
  55. /// </summary>
  56. private const string CS0122 = "CS0122";
  57. /// <summary>
  58. /// The using alias 'A' cannot be used with type arguments
  59. /// </summary>
  60. private const string CS0307 = "CS0307";
  61. /// <summary>
  62. /// 'A' is not an attribute class
  63. /// </summary>
  64. private const string CS0616 = "CS0616";
  65. /// <summary>
  66. /// ; expected.
  67. /// </summary>
  68. private const string CS1002 = "CS1002";
  69. /// <summary>
  70. /// Syntax error, 'A' expected
  71. /// </summary>
  72. private const string CS1003 = "CS1003";
  73. /// <summary>
  74. /// cannot convert from 'int' to 'string'
  75. /// </summary>
  76. private const string CS1503 = "CS1503";
  77. /// <summary>
  78. /// XML comment on 'construct' has syntactically incorrect cref attribute 'name'
  79. /// </summary>
  80. private const string CS1574 = "CS1574";
  81. /// <summary>
  82. /// Invalid type for parameter 'parameter number' in XML comment cref attribute
  83. /// </summary>
  84. private const string CS1580 = "CS1580";
  85. /// <summary>
  86. /// Invalid return type in XML comment cref attribute
  87. /// </summary>
  88. private const string CS1581 = "CS1581";
  89. /// <summary>
  90. /// XML comment has syntactically incorrect cref attribute
  91. /// </summary>
  92. private const string CS1584 = "CS1584";
  93. public override ImmutableArray<string> FixableDiagnosticIds
  94. {
  95. get
  96. {
  97. return ImmutableArray.Create(
  98. CS0103,
  99. CS0246,
  100. CS0305,
  101. CS1061,
  102. CS1935,
  103. CS0308,
  104. CS0122,
  105. CS0307,
  106. CS0616,
  107. CS1002,
  108. CS1003,
  109. CS1503,
  110. CS1574,
  111. CS1580,
  112. CS1581,
  113. CS1584);
  114. }
  115. }
  116. protected override bool IgnoreCase
  117. {
  118. get { return false; }
  119. }
  120. protected override bool CanAddImport(SyntaxNode node, CancellationToken cancellationToken)
  121. {
  122. if (cancellationToken.IsCancellationRequested)
  123. {
  124. return false;
  125. }
  126. return node.CanAddUsingDirectives(cancellationToken);
  127. }
  128. protected override bool CanAddImportForMethod(Diagnostic diagnostic, ref SyntaxNode node)
  129. {
  130. switch (diagnostic.Id)
  131. {
  132. case CS1061:
  133. if (node.IsKind(SyntaxKind.ConditionalAccessExpression))
  134. {
  135. node = (node as ConditionalAccessExpressionSyntax).WhenNotNull;
  136. }
  137. else if (node.IsKind(SyntaxKind.MemberBindingExpression))
  138. {
  139. node = (node as MemberBindingExpressionSyntax).Name;
  140. }
  141. else if (node.Parent.IsKind(SyntaxKind.CollectionInitializerExpression))
  142. {
  143. return true;
  144. }
  145. break;
  146. case CS0122:
  147. break;
  148. case CS1503:
  149. //// look up its corresponding method name
  150. var parent = node.GetAncestor<InvocationExpressionSyntax>();
  151. if (parent == null)
  152. {
  153. return false;
  154. }
  155. var method = parent.Expression as MemberAccessExpressionSyntax;
  156. if (method != null)
  157. {
  158. node = method.Name;
  159. }
  160. break;
  161. default:
  162. return false;
  163. }
  164. var simpleName = node as SimpleNameSyntax;
  165. if (!simpleName.IsParentKind(SyntaxKind.SimpleMemberAccessExpression) &&
  166. !simpleName.IsParentKind(SyntaxKind.MemberBindingExpression))
  167. {
  168. return false;
  169. }
  170. var memberAccess = simpleName.Parent as MemberAccessExpressionSyntax;
  171. var memberBinding = simpleName.Parent as MemberBindingExpressionSyntax;
  172. if (memberAccess.IsParentKind(SyntaxKind.SimpleMemberAccessExpression) ||
  173. memberAccess.IsParentKind(SyntaxKind.ElementAccessExpression) ||
  174. memberBinding.IsParentKind(SyntaxKind.SimpleMemberAccessExpression) ||
  175. memberBinding.IsParentKind(SyntaxKind.ElementAccessExpression))
  176. {
  177. return false;
  178. }
  179. if (!node.IsMemberAccessExpressionName())
  180. {
  181. return false;
  182. }
  183. return true;
  184. }
  185. protected override bool CanAddImportForNamespace(Diagnostic diagnostic, ref SyntaxNode node)
  186. {
  187. return false;
  188. }
  189. protected override bool CanAddImportForQuery(Diagnostic diagnostic, ref SyntaxNode node)
  190. {
  191. if (diagnostic.Id != CS1935)
  192. {
  193. return false;
  194. }
  195. return node.AncestorsAndSelf().Any(n => n is QueryExpressionSyntax && !(n.Parent is QueryContinuationSyntax));
  196. }
  197. protected override bool CanAddImportForType(Diagnostic diagnostic, ref SyntaxNode node)
  198. {
  199. switch (diagnostic.Id)
  200. {
  201. case CS0103:
  202. case CS0246:
  203. case CS0305:
  204. case CS0308:
  205. case CS0122:
  206. case CS0307:
  207. case CS0616:
  208. case CS1003:
  209. case CS1580:
  210. case CS1581:
  211. break;
  212. case CS1002:
  213. //// only lookup errors inside ParenthesizedLambdaExpression e.g., () => { ... }
  214. if (node.Ancestors().OfType<ParenthesizedLambdaExpressionSyntax>().Any())
  215. {
  216. if (node is SimpleNameSyntax)
  217. {
  218. break;
  219. }
  220. else if (node is BlockSyntax || node is MemberAccessExpressionSyntax || node is BinaryExpressionSyntax)
  221. {
  222. var last = node.DescendantNodes().OfType<SimpleNameSyntax>().LastOrDefault();
  223. if (!TryFindStandaloneType(ref node))
  224. {
  225. node = node.DescendantNodes().OfType<SimpleNameSyntax>().FirstOrDefault();
  226. }
  227. else
  228. {
  229. node = last;
  230. }
  231. }
  232. }
  233. else
  234. {
  235. return false;
  236. }
  237. break;
  238. case CS1574:
  239. case CS1584:
  240. var cref = node as QualifiedCrefSyntax;
  241. if (cref != null)
  242. {
  243. node = cref.Container;
  244. }
  245. break;
  246. default:
  247. return false;
  248. }
  249. return TryFindStandaloneType(ref node);
  250. }
  251. private static bool TryFindStandaloneType(ref SyntaxNode node)
  252. {
  253. var qn = node as QualifiedNameSyntax;
  254. if (qn != null)
  255. {
  256. node = GetLeftMostSimpleName(qn);
  257. }
  258. var simpleName = node as SimpleNameSyntax;
  259. return simpleName.LooksLikeStandaloneTypeName();
  260. }
  261. private static SimpleNameSyntax GetLeftMostSimpleName(QualifiedNameSyntax qn)
  262. {
  263. while (qn != null)
  264. {
  265. var left = qn.Left;
  266. var simpleName = left as SimpleNameSyntax;
  267. if (simpleName != null)
  268. {
  269. return simpleName;
  270. }
  271. qn = left as QualifiedNameSyntax;
  272. }
  273. return null;
  274. }
  275. protected override ISet<INamespaceSymbol> GetNamespacesInScope(
  276. SemanticModel semanticModel,
  277. SyntaxNode node,
  278. CancellationToken cancellationToken)
  279. {
  280. return semanticModel.GetUsingNamespacesInScope(node);
  281. }
  282. protected override ITypeSymbol GetQueryClauseInfo(
  283. SemanticModel semanticModel,
  284. SyntaxNode node,
  285. CancellationToken cancellationToken)
  286. {
  287. var query = node.AncestorsAndSelf().OfType<QueryExpressionSyntax>().First();
  288. if (InfoBoundSuccessfully(semanticModel.GetQueryClauseInfo(query.FromClause, cancellationToken)))
  289. {
  290. return null;
  291. }
  292. foreach (var clause in query.Body.Clauses)
  293. {
  294. if (InfoBoundSuccessfully(semanticModel.GetQueryClauseInfo(clause, cancellationToken)))
  295. {
  296. return null;
  297. }
  298. }
  299. if (InfoBoundSuccessfully(semanticModel.GetSymbolInfo(query.Body.SelectOrGroup, cancellationToken)))
  300. {
  301. return null;
  302. }
  303. var fromClause = query.FromClause;
  304. return semanticModel.GetTypeInfo(fromClause.Expression, cancellationToken).Type;
  305. }
  306. private bool InfoBoundSuccessfully(SymbolInfo symbolInfo)
  307. {
  308. return InfoBoundSuccessfully(symbolInfo.Symbol);
  309. }
  310. private bool InfoBoundSuccessfully(QueryClauseInfo semanticInfo)
  311. {
  312. return InfoBoundSuccessfully(semanticInfo.OperationInfo);
  313. }
  314. private static bool InfoBoundSuccessfully(ISymbol operation)
  315. {
  316. operation = operation.GetOriginalUnreducedDefinition();
  317. return operation != null;
  318. }
  319. protected override string GetDescription(INamespaceOrTypeSymbol namespaceSymbol, SemanticModel semanticModel, SyntaxNode contextNode)
  320. {
  321. var root = GetCompilationUnitSyntaxNode(contextNode);
  322. // No localization necessary
  323. string externAliasString;
  324. if (TryGetExternAliasString(namespaceSymbol, semanticModel, root, out externAliasString))
  325. {
  326. return string.Format ("extern alias {0};", externAliasString);
  327. }
  328. string namespaceString;
  329. if (TryGetNamespaceString(namespaceSymbol, root, false, null, out namespaceString))
  330. {
  331. return string.Format ("using {0};", namespaceString);
  332. }
  333. // If we get here then neither a namespace or a an extern alias can be added.
  334. // There is no valid string to show to the user and there is
  335. // likely a bug in that we should know about.
  336. throw new InvalidOperationException ();
  337. }
  338. protected override async Task<Document> AddImportAsync(
  339. SyntaxNode contextNode,
  340. INamespaceOrTypeSymbol namespaceSymbol,
  341. Document document,
  342. bool placeSystemNamespaceFirst,
  343. CancellationToken cancellationToken)
  344. {
  345. var root = GetCompilationUnitSyntaxNode(contextNode, cancellationToken);
  346. var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
  347. var simpleUsingDirective = GetUsingDirective(root, namespaceSymbol, semanticModel, fullyQualify: false);
  348. var externAliasUsingDirective = GetExternAliasUsingDirective(root, namespaceSymbol, semanticModel);
  349. if (externAliasUsingDirective != null)
  350. {
  351. root = root.AddExterns(
  352. externAliasUsingDirective
  353. .WithAdditionalAnnotations(Formatter.Annotation));
  354. }
  355. if (simpleUsingDirective != null)
  356. {
  357. // Because of the way usings can be nested inside of namespace declarations,
  358. // we need to check if the usings must be fully qualified so as not to be
  359. // ambiguous with the containing namespace.
  360. if (UsingsAreContainedInNamespace(contextNode))
  361. {
  362. // When we add usings we try and place them, as best we can, where the user
  363. // wants them according to their settings. This means we can't just add the fully-
  364. // qualified usings and expect the simplifier to take care of it, the usings have to be
  365. // simplified before we attempt to add them to the document.
  366. // You might be tempted to think that we could call
  367. // AddUsings -> Simplifier -> SortUsings
  368. // But this will clobber the users using settings without asking. Instead we create a new
  369. // Document and check if our using can be simplified. Worst case we need to back out the
  370. // fully qualified change and reapply with the simple name.
  371. var fullyQualifiedUsingDirective = GetUsingDirective(root, namespaceSymbol, semanticModel, fullyQualify: true);
  372. SyntaxNode newRoot = root.AddUsingDirective(
  373. fullyQualifiedUsingDirective, contextNode, placeSystemNamespaceFirst,
  374. Formatter.Annotation);
  375. var newDocument = document.WithSyntaxRoot(newRoot);
  376. var newSemanticModel = await newDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
  377. newRoot = await newDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
  378. var newUsing = newRoot
  379. .DescendantNodes().OfType<UsingDirectiveSyntax>().Where(uds => uds.IsEquivalentTo(fullyQualifiedUsingDirective, topLevel: true)).Single();
  380. var speculationAnalyzer = new SpeculationAnalyzer(newUsing.Name, simpleUsingDirective.Name, newSemanticModel, cancellationToken);
  381. if (speculationAnalyzer.ReplacementChangesSemantics())
  382. {
  383. // Not fully qualifying the using causes to refer to a different namespace so we need to keep it as is.
  384. return newDocument;
  385. }
  386. else
  387. {
  388. // It does not matter if it is fully qualified or simple so lets return the simple name.
  389. return document.WithSyntaxRoot(root.AddUsingDirective(
  390. simpleUsingDirective, contextNode, placeSystemNamespaceFirst,
  391. Formatter.Annotation));
  392. }
  393. }
  394. else
  395. {
  396. // simple form
  397. return document.WithSyntaxRoot(root.AddUsingDirective(
  398. simpleUsingDirective, contextNode, placeSystemNamespaceFirst,
  399. Formatter.Annotation));
  400. }
  401. }
  402. return document.WithSyntaxRoot(root);
  403. }
  404. private static ExternAliasDirectiveSyntax GetExternAliasUsingDirective(CompilationUnitSyntax root, INamespaceOrTypeSymbol namespaceSymbol, SemanticModel semanticModel)
  405. {
  406. string externAliasString;
  407. if (TryGetExternAliasString(namespaceSymbol, semanticModel, root, out externAliasString))
  408. {
  409. return SyntaxFactory.ExternAliasDirective(SyntaxFactory.Identifier(externAliasString));
  410. }
  411. return null;
  412. }
  413. private UsingDirectiveSyntax GetUsingDirective(CompilationUnitSyntax root, INamespaceOrTypeSymbol namespaceSymbol, SemanticModel semanticModel, bool fullyQualify)
  414. {
  415. string namespaceString;
  416. string externAliasString;
  417. TryGetExternAliasString(namespaceSymbol, semanticModel, root, out externAliasString);
  418. if (externAliasString != null)
  419. {
  420. if (TryGetNamespaceString(namespaceSymbol, root, false, externAliasString, out namespaceString))
  421. {
  422. return SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(namespaceString));
  423. }
  424. return null;
  425. }
  426. if (TryGetNamespaceString(namespaceSymbol, root, fullyQualify, null, out namespaceString))
  427. {
  428. return SyntaxFactory.UsingDirective(SyntaxFactory.ParseName(namespaceString));
  429. }
  430. return null;
  431. }
  432. private bool UsingsAreContainedInNamespace(SyntaxNode contextNode)
  433. {
  434. return contextNode.GetAncestor<NamespaceDeclarationSyntax>()?.DescendantNodes().OfType<UsingDirectiveSyntax>().FirstOrDefault() != null;
  435. }
  436. private static bool TryGetExternAliasString(INamespaceOrTypeSymbol namespaceSymbol, SemanticModel semanticModel, CompilationUnitSyntax root, out string externAliasString)
  437. {
  438. externAliasString = null;
  439. var metadataReference = semanticModel.Compilation.GetMetadataReference(namespaceSymbol.ContainingAssembly);
  440. if (metadataReference == null)
  441. {
  442. return false;
  443. }
  444. var properties = metadataReference.Properties;
  445. var aliases = properties.Aliases;
  446. if (aliases.IsDefaultOrEmpty)
  447. {
  448. return false;
  449. }
  450. aliases = properties.Aliases.Where(a => a != MetadataReferenceProperties.GlobalAlias).ToImmutableArray();
  451. if (!aliases.Any())
  452. {
  453. return false;
  454. }
  455. externAliasString = aliases.First();
  456. return ShouldAddExternAlias(aliases, root);
  457. }
  458. private static bool TryGetNamespaceString(INamespaceOrTypeSymbol namespaceSymbol, CompilationUnitSyntax root, bool fullyQualify, string alias, out string namespaceString)
  459. {
  460. namespaceString = fullyQualify
  461. ? namespaceSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
  462. : namespaceSymbol.ToDisplayString();
  463. if (alias != null)
  464. {
  465. namespaceString = alias + "::" + namespaceString;
  466. }
  467. return ShouldAddUsing(namespaceString, root);
  468. }
  469. private static bool ShouldAddExternAlias(ImmutableArray<string> aliases, CompilationUnitSyntax root)
  470. {
  471. var identifiers = root.DescendantNodes().OfType<ExternAliasDirectiveSyntax>().Select(e => e.Identifier.ToString());
  472. var externAliases = aliases.Where(a => identifiers.Contains(a));
  473. return !externAliases.Any();
  474. }
  475. private static bool ShouldAddUsing(string usingDirective, CompilationUnitSyntax root)
  476. {
  477. return !root.Usings.Select(u => u.Name.ToString()).Contains(usingDirective);
  478. }
  479. private static CompilationUnitSyntax GetCompilationUnitSyntaxNode(SyntaxNode contextNode, CancellationToken cancellationToken = default(CancellationToken))
  480. {
  481. return (CompilationUnitSyntax)contextNode.SyntaxTree.GetRoot(cancellationToken);
  482. }
  483. protected override bool IsViableExtensionMethod(IMethodSymbol method, SyntaxNode expression, SemanticModel semanticModel, CancellationToken cancellationToken)
  484. {
  485. var leftExpression = expression.GetExpressionOfMemberAccessExpression() ?? expression.GetExpressionOfConditionalMemberAccessExpression();
  486. if (leftExpression == null)
  487. {
  488. if (expression.IsKind(SyntaxKind.CollectionInitializerExpression))
  489. {
  490. leftExpression = expression.GetAncestor<ObjectCreationExpressionSyntax>();
  491. }
  492. else
  493. {
  494. return false;
  495. }
  496. }
  497. var semanticInfo = semanticModel.GetTypeInfo(leftExpression, cancellationToken);
  498. var leftExpressionType = semanticInfo.Type;
  499. return leftExpressionType != null && method.ReduceExtensionMethod(leftExpressionType) != null;
  500. }
  501. protected override IEnumerable<ITypeSymbol> GetProposedTypes(string name, List<ITypeSymbol> accessibleTypeSymbols, SemanticModel semanticModel, ISet<INamespaceSymbol> namespacesInScope)
  502. {
  503. if (accessibleTypeSymbols == null)
  504. {
  505. yield break;
  506. }
  507. foreach (var typeSymbol in accessibleTypeSymbols)
  508. {
  509. if ((typeSymbol != null) && (typeSymbol.ContainingType != null) && typeSymbol.ContainingType.IsStatic)
  510. {
  511. yield return typeSymbol.ContainingType;
  512. }
  513. }
  514. }
  515. internal override bool IsViableField(IFieldSymbol field, SyntaxNode expression, SemanticModel semanticModel, CancellationToken cancellationToken)
  516. {
  517. return IsViablePropertyOrField(field, expression, semanticModel, cancellationToken);
  518. }
  519. internal override bool IsViableProperty(IPropertySymbol property, SyntaxNode expression, SemanticModel semanticModel, CancellationToken cancellationToken)
  520. {
  521. return IsViablePropertyOrField(property, expression, semanticModel, cancellationToken);
  522. }
  523. private bool IsViablePropertyOrField(ISymbol propertyOrField, SyntaxNode expression, SemanticModel semanticModel, CancellationToken cancellationToken)
  524. {
  525. if (!propertyOrField.IsStatic)
  526. {
  527. return false;
  528. }
  529. var leftName = (expression as MemberAccessExpressionSyntax)?.Expression as SimpleNameSyntax;
  530. if (leftName == null)
  531. {
  532. return false;
  533. }
  534. return string.Compare(propertyOrField.ContainingType.Name, leftName.Identifier.Text, this.IgnoreCase) == 0;
  535. }
  536. internal override bool IsAddMethodContext(SyntaxNode node, SemanticModel semanticModel)
  537. {
  538. if (node.Parent.IsKind(SyntaxKind.CollectionInitializerExpression))
  539. {
  540. var objectCreationExpressionSyntax = node.GetAncestor<ObjectCreationExpressionSyntax>();
  541. if (objectCreationExpressionSyntax == null)
  542. {
  543. return false;
  544. }
  545. return true;
  546. }
  547. return false;
  548. }
  549. }
  550. }