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

CSharpAddImportCodeFixProvider.cs « AddImport « MonoDevelop.CSharp.CodeFixes « CSharpBinding « addins « src « main - github.com/mono/monodevelop.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 994bd39571a731ccfb665ed9ead936f2823e7f1a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
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;
		}
	}
}