From f829d5833c1244c90dcbbbe6d2bfb45a4847af54 Mon Sep 17 00:00:00 2001 From: Simon Lindgren Date: Sat, 8 Sep 2012 23:37:19 +0200 Subject: [PATCH 01/37] [CodeIssues] Don't mark calls to IFormattable.ToString() as redundant. --- .../CodeIssues/RedundantToStringIssue.cs | 3 ++- .../CSharp/CodeIssues/RedundantToStringTests.cs | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/RedundantToStringIssue.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/RedundantToStringIssue.cs index 10e6f6d143..8d7df7b841 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/RedundantToStringIssue.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/RedundantToStringIssue.cs @@ -79,7 +79,8 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring if (resolveResult == null) { return; } - if (resolveResult.Member.Name != "ToString") { + var member = resolveResult.Member; + if (member.Name != "ToString" || member.Parameters.Count != 0) { return; } AddRedundantToStringIssue(memberExpression, invocationExpression); diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/RedundantToStringTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/RedundantToStringTests.cs index 1bd2dbc6e0..7925e405c9 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/RedundantToStringTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/RedundantToStringTests.cs @@ -76,6 +76,23 @@ class Foo Assert.AreEqual (0, issues.Count); } + [Test] + public void IgnoresCallsToIFormattableToString () + { + var input = @" +class Foo +{ + void Bar (System.DateTime dt) + { + string s = dt.ToString("""", CultureInfo.InvariantCulture) + string.Empty; + } +}"; + + TestRefactoringContext context; + var issues = GetIssues (new RedundantToStringIssue (), input, out context); + Assert.AreEqual (0, issues.Count); + } + [Test] public void StringTarget () { From 3b5e5731eae3b04de80c4f1714106ccacdfecc06 Mon Sep 17 00:00:00 2001 From: Simon Lindgren Date: Sun, 9 Sep 2012 01:17:47 +0200 Subject: [PATCH 02/37] [CodeIssues] Don't warn for unused parameters on overrides and interface implementations. --- .../ParameterNotUsedIssue.cs | 20 +++++++++++++ .../CodeIssues/ParameterNotUsedIssueTests.cs | 30 +++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableNotUsedIssues/ParameterNotUsedIssue.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableNotUsedIssues/ParameterNotUsedIssue.cs index 1a07c73eac..d8b24b8b56 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableNotUsedIssues/ParameterNotUsedIssue.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableNotUsedIssues/ParameterNotUsedIssue.cs @@ -25,6 +25,8 @@ // THE SOFTWARE. using ICSharpCode.NRefactory.Semantics; +using System.Linq; +using ICSharpCode.NRefactory.TypeSystem; namespace ICSharpCode.NRefactory.CSharp.Refactoring { @@ -51,6 +53,24 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring this.unit = unit; } + public override void VisitMethodDeclaration(MethodDeclaration methodDeclaration) + { + // Only some methods are candidates for the warning + + if (methodDeclaration.Body.IsNull) + return; + var methodResolveResult = ctx.Resolve(methodDeclaration) as MemberResolveResult; + if (methodResolveResult == null) + return; + var member = methodResolveResult.Member; + if (member.IsOverride) + return; + if (member.ImplementedInterfaceMembers.Any ()) + return; + + base.VisitMethodDeclaration(methodDeclaration); + } + public override void VisitParameterDeclaration (ParameterDeclaration parameterDeclaration) { base.VisitParameterDeclaration (parameterDeclaration); diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ParameterNotUsedIssueTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ParameterNotUsedIssueTests.cs index 81da14b795..e939c3cfa0 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ParameterNotUsedIssueTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ParameterNotUsedIssueTests.cs @@ -43,6 +43,36 @@ class TestClass { }"; Test (input, 1); } + + [Test] + public void TestInterfaceImplementation () + { + var input = @" +interface ITestClass { + void TestMethod (int i); +} +class TestClass : ITestClass { + public void TestMethod (int i) + { + } +}"; + Test (input, 0); + } + + [Test] + public void TestAbstractMethodImplementation () + { + var input = @" +abstract class TestBase { + public abstract void TestMethod (int i); +} +class TestClass : TestBase { + public override void TestMethod (int i) + { + } +}"; + Test (input, 0); + } [Test] public void TestUsedParameter () From 6682aa6f9253e350eb0d6f3e0ffb8c1462c6edf2 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 10 Sep 2012 13:39:34 +0200 Subject: [PATCH 03/37] NRefactory changes from SharpDevelop: add IDocument.FileName and IAssembly.FullAssemblyName, --- .../CSharpProjectContent.cs | 14 ++++++- .../TypeSystem/CSharpAssembly.cs | 4 ++ ICSharpCode.NRefactory/Editor/IDocument.cs | 11 ++++++ .../Editor/ReadOnlyDocument.cs | 17 ++++++-- .../Editor/StringBuilderDocument.cs | 12 +++++- .../TypeSystem/CecilLoader.cs | 6 +-- .../TypeSystem/IAssembly.cs | 12 +++++- .../TypeSystem/ISupportsInterning.cs | 2 +- .../DefaultUnresolvedAssembly.cs | 39 ++++++++++++++++++- ICSharpCode.NRefactory/Utils/KeyComparer.cs | 21 ++++++++++ .../Utils/MultiDictionary.cs | 20 ++++++++++ 11 files changed, 145 insertions(+), 13 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs b/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs index fccba4622f..5d2112875e 100644 --- a/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs +++ b/ICSharpCode.NRefactory.CSharp/CSharpProjectContent.cs @@ -31,6 +31,7 @@ namespace ICSharpCode.NRefactory.CSharp public class CSharpProjectContent : IProjectContent { string assemblyName; + string fullAssemblyName; string projectFileName; string location; Dictionary unresolvedFiles; @@ -48,6 +49,7 @@ namespace ICSharpCode.NRefactory.CSharp protected CSharpProjectContent(CSharpProjectContent pc) { this.assemblyName = pc.assemblyName; + this.fullAssemblyName = pc.fullAssemblyName; this.projectFileName = pc.projectFileName; this.location = pc.location; this.unresolvedFiles = new Dictionary(pc.unresolvedFiles, Platform.FileNameComparer); @@ -71,6 +73,10 @@ namespace ICSharpCode.NRefactory.CSharp get { return assemblyName; } } + public string FullAssemblyName { + get { return fullAssemblyName; } + } + public string Location { get { return location; } } @@ -128,10 +134,16 @@ namespace ICSharpCode.NRefactory.CSharp return new CSharpProjectContent(this); } + /// + /// Sets both the short and the full assembly names. + /// + /// New full assembly name. public IProjectContent SetAssemblyName(string newAssemblyName) { CSharpProjectContent pc = Clone(); - pc.assemblyName = newAssemblyName; + pc.fullAssemblyName = newAssemblyName; + int pos = newAssemblyName != null ? newAssemblyName.IndexOf(',') : -1; + pc.assemblyName = pos < 0 ? newAssemblyName : newAssemblyName.Substring(0, pos); return pc; } diff --git a/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpAssembly.cs b/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpAssembly.cs index ec9b9cfeb4..9b0f15a531 100644 --- a/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpAssembly.cs +++ b/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpAssembly.cs @@ -54,6 +54,10 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem get { return projectContent.AssemblyName; } } + public string FullAssemblyName { + get { return projectContent.FullAssemblyName; } + } + public IList AssemblyAttributes { get { return GetAttributes(ref assemblyAttributes, true); diff --git a/ICSharpCode.NRefactory/Editor/IDocument.cs b/ICSharpCode.NRefactory/Editor/IDocument.cs index f9a793b911..fdd28113db 100644 --- a/ICSharpCode.NRefactory/Editor/IDocument.cs +++ b/ICSharpCode.NRefactory/Editor/IDocument.cs @@ -190,5 +190,16 @@ namespace ICSharpCode.NRefactory.Editor /// /// ITextAnchor CreateAnchor(int offset); + + /// + /// Gets the name of the file the document is stored in. + /// Could also be a non-existent dummy file name or null if no name has been set. + /// + string FileName { get; } + + /// + /// Fired when the file name of the document changes. + /// + event EventHandler FileNameChanged; } } diff --git a/ICSharpCode.NRefactory/Editor/ReadOnlyDocument.cs b/ICSharpCode.NRefactory/Editor/ReadOnlyDocument.cs index 0cc4b68dc5..7e0e9ac188 100644 --- a/ICSharpCode.NRefactory/Editor/ReadOnlyDocument.cs +++ b/ICSharpCode.NRefactory/Editor/ReadOnlyDocument.cs @@ -28,6 +28,7 @@ namespace ICSharpCode.NRefactory.Editor public sealed class ReadOnlyDocument : IDocument { readonly ITextSource textSource; + readonly string fileName; int[] lines; static readonly char[] newline = { '\r', '\n' }; @@ -35,12 +36,13 @@ namespace ICSharpCode.NRefactory.Editor /// /// Creates a new ReadOnlyDocument from the given text source. /// - public ReadOnlyDocument(ITextSource textSource) + public ReadOnlyDocument(ITextSource textSource, string fileName = null) { if (textSource == null) throw new ArgumentNullException("textSource"); // ensure that underlying buffer is immutable this.textSource = textSource.CreateSnapshot(); + this.fileName = fileName; List lines = new List(); lines.Add(0); int offset = 0; @@ -58,8 +60,8 @@ namespace ICSharpCode.NRefactory.Editor /// /// Creates a new ReadOnlyDocument from the given string. /// - public ReadOnlyDocument(string text) - : this(new StringTextSource(text)) + public ReadOnlyDocument(string text, string fileName = null) + : this(new StringTextSource(text), fileName) { } @@ -423,5 +425,14 @@ namespace ICSharpCode.NRefactory.Editor { return null; } + + /// + /// Will never be raised on . + public event EventHandler FileNameChanged { add {} remove {} } + + /// + public string FileName { + get { return fileName; } + } } } diff --git a/ICSharpCode.NRefactory/Editor/StringBuilderDocument.cs b/ICSharpCode.NRefactory/Editor/StringBuilderDocument.cs index d310f34c4a..bcd14a939a 100644 --- a/ICSharpCode.NRefactory/Editor/StringBuilderDocument.cs +++ b/ICSharpCode.NRefactory/Editor/StringBuilderDocument.cs @@ -312,13 +312,13 @@ namespace ICSharpCode.NRefactory.Editor /// public string Text { - get { + get { if (cachedText == null) cachedText = b.ToString(); return cachedText; } set { - Replace(0, b.Length, value); + Replace(0, b.Length, value); } } @@ -481,5 +481,13 @@ namespace ICSharpCode.NRefactory.Editor { return null; } + + /// + public virtual event EventHandler FileNameChanged { add {} remove {} } + + /// + public virtual string FileName { + get { return string.Empty; } + } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs index 82ad487cea..f272fe4750 100644 --- a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs +++ b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs @@ -175,7 +175,7 @@ namespace ICSharpCode.NRefactory.TypeSystem assemblyAttributes = interningProvider.InternList(assemblyAttributes); moduleAttributes = interningProvider.InternList(moduleAttributes); - this.currentAssembly = new CecilUnresolvedAssembly(assemblyDefinition.Name.Name, this.DocumentationProvider); + this.currentAssembly = new CecilUnresolvedAssembly(assemblyDefinition.Name, this.DocumentationProvider); currentAssembly.Location = assemblyDefinition.MainModule.FullyQualifiedName; currentAssembly.AssemblyAttributes.AddRange(assemblyAttributes); currentAssembly.ModuleAttributes.AddRange(assemblyAttributes); @@ -267,8 +267,8 @@ namespace ICSharpCode.NRefactory.TypeSystem { readonly IDocumentationProvider documentationProvider; - public CecilUnresolvedAssembly(string assemblyName, IDocumentationProvider documentationProvider) - : base(assemblyName) + public CecilUnresolvedAssembly(AssemblyNameDefinition assemblyName, IDocumentationProvider documentationProvider) + : base(assemblyName.FullName) { Debug.Assert(assemblyName != null); this.documentationProvider = documentationProvider; diff --git a/ICSharpCode.NRefactory/TypeSystem/IAssembly.cs b/ICSharpCode.NRefactory/TypeSystem/IAssembly.cs index 1c98a0498e..c93484300a 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IAssembly.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IAssembly.cs @@ -30,7 +30,12 @@ namespace ICSharpCode.NRefactory.TypeSystem /// Gets the assembly name (short name). /// string AssemblyName { get; } - + + /// + /// Gets the full assembly name (including public key token etc.) + /// + string FullAssemblyName { get; } + /// /// Gets the path to the assembly location. /// For projects it is the same as the output path. @@ -81,6 +86,11 @@ namespace ICSharpCode.NRefactory.TypeSystem /// string AssemblyName { get; } + /// + /// Gets the full assembly name (including public key token etc.) + /// + string FullAssemblyName { get; } + /// /// Gets the list of all assembly attributes in the project. /// diff --git a/ICSharpCode.NRefactory/TypeSystem/ISupportsInterning.cs b/ICSharpCode.NRefactory/TypeSystem/ISupportsInterning.cs index 21c2e1b36c..369eac6487 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ISupportsInterning.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ISupportsInterning.cs @@ -22,7 +22,7 @@ namespace ICSharpCode.NRefactory.TypeSystem { /// /// Interface for TypeSystem objects that support interning. - /// See for more information. + /// See for more information. /// public interface ISupportsInterning { diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs index d74c1cae84..1111ef638f 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs @@ -36,6 +36,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation public class DefaultUnresolvedAssembly : AbstractFreezable, IUnresolvedAssembly { string assemblyName; + string fullAssemblyName; IList assemblyAttributes; IList moduleAttributes; Dictionary typeDefinitions = new Dictionary(FullNameAndTypeParameterCountComparer.Ordinal); @@ -51,15 +52,28 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } } + /// + /// Creates a new unresolved assembly. + /// + /// Full assembly name public DefaultUnresolvedAssembly(string assemblyName) { if (assemblyName == null) throw new ArgumentNullException("assemblyName"); - this.assemblyName = assemblyName; + this.fullAssemblyName = assemblyName; + int pos = assemblyName != null ? assemblyName.IndexOf(',') : -1; + this.assemblyName = pos < 0 ? assemblyName : assemblyName.Substring(0, pos); this.assemblyAttributes = new List(); this.moduleAttributes = new List(); } + /// + /// Gets/Sets the short assembly name. + /// + /// + /// This class handles the short and the full name independently; + /// if you change the short name, you should also change the full name. + /// public string AssemblyName { get { return assemblyName; } set { @@ -69,7 +83,24 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation assemblyName = value; } } - + + /// + /// Gets/Sets the full assembly name. + /// + /// + /// This class handles the short and the full name independently; + /// if you change the full name, you should also change the short name. + /// + public string FullAssemblyName { + get { return fullAssemblyName; } + set { + if (value == null) + throw new ArgumentNullException("value"); + FreezableHelper.ThrowIfFrozen(this); + fullAssemblyName = value; + } + } + string location; public string Location { get { @@ -287,6 +318,10 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation get { return unresolvedAssembly.AssemblyName; } } + public string FullAssemblyName { + get { return unresolvedAssembly.FullAssemblyName; } + } + public IList AssemblyAttributes { get; private set; } public IList ModuleAttributes { get; private set; } diff --git a/ICSharpCode.NRefactory/Utils/KeyComparer.cs b/ICSharpCode.NRefactory/Utils/KeyComparer.cs index 2bb657e469..27d2fa342f 100644 --- a/ICSharpCode.NRefactory/Utils/KeyComparer.cs +++ b/ICSharpCode.NRefactory/Utils/KeyComparer.cs @@ -27,6 +27,21 @@ namespace ICSharpCode.NRefactory.Utils { return new KeyComparer(keySelector, Comparer.Default, EqualityComparer.Default); } + + public static KeyComparer Create(Func keySelector, IComparer comparer, IEqualityComparer equalityComparer) + { + return new KeyComparer(keySelector, comparer, equalityComparer); + } + + public static IComparer Create(Func keySelector, IComparer comparer) + { + return new KeyComparer(keySelector, comparer, EqualityComparer.Default); + } + + public static IEqualityComparer Create(Func keySelector, IEqualityComparer equalityComparer) + { + return new KeyComparer(keySelector, Comparer.Default, equalityComparer); + } } public class KeyComparer : IComparer, IEqualityComparer @@ -37,6 +52,12 @@ namespace ICSharpCode.NRefactory.Utils public KeyComparer(Func keySelector, IComparer keyComparer, IEqualityComparer keyEqualityComparer) { + if (keySelector == null) + throw new ArgumentNullException("keySelector"); + if (keyComparer == null) + throw new ArgumentNullException("keyComparer"); + if (keyEqualityComparer == null) + throw new ArgumentNullException("keyEqualityComparer"); this.keySelector = keySelector; this.keyComparer = keyComparer; this.keyEqualityComparer = keyEqualityComparer; diff --git a/ICSharpCode.NRefactory/Utils/MultiDictionary.cs b/ICSharpCode.NRefactory/Utils/MultiDictionary.cs index b081ef06f3..96adc8eb16 100644 --- a/ICSharpCode.NRefactory/Utils/MultiDictionary.cs +++ b/ICSharpCode.NRefactory/Utils/MultiDictionary.cs @@ -62,6 +62,15 @@ namespace ICSharpCode.NRefactory.Utils return false; } + /// + /// Removes all entries with the specified key. + /// + /// Returns true if at least one entry was removed. + public bool RemoveAll(TKey key) + { + return dict.Remove(key); + } + public void Clear() { dict.Clear(); @@ -81,10 +90,21 @@ namespace ICSharpCode.NRefactory.Utils } } + /// + /// Returns the number of different keys. + /// public int Count { get { return dict.Count; } } + public ICollection Keys { + get { return dict.Keys; } + } + + public IEnumerable Values { + get { return dict.Values.SelectMany(list => list); } + } + IEnumerable ILookup.this[TKey key] { get { return this[key]; } } From 1098051271db40819cf39a48802f47b4bd70e420 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 10 Sep 2012 13:41:57 +0200 Subject: [PATCH 04/37] Fix icsharpcode/NRefactory#110: CecilLoader fails on System.Data v2.0.50727 --- ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs index f272fe4750..140a2f23f0 100644 --- a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs +++ b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs @@ -514,7 +514,7 @@ namespace ICSharpCode.NRefactory.TypeSystem MethodImplAttributes implAttributes = methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask; #region DllImportAttribute - if (methodDefinition.HasPInvokeInfo) { + if (methodDefinition.HasPInvokeInfo && methodDefinition.PInvokeInfo != null) { PInvokeInfo info = methodDefinition.PInvokeInfo; var dllImport = new DefaultUnresolvedAttribute(dllImportAttributeTypeRef, new[] { KnownTypeReference.String }); dllImport.PositionalArguments.Add(CreateSimpleConstantValue(KnownTypeReference.String, info.Module.Name)); From f7d8e871836293faafddf187fc79562779f83377 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 10 Sep 2012 14:50:26 +0200 Subject: [PATCH 05/37] Fix race condition caused by freezing cecil-loaded assemblies too late. --- ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs index 140a2f23f0..67099a30e7 100644 --- a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs +++ b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs @@ -227,6 +227,15 @@ namespace ICSharpCode.NRefactory.TypeSystem } AddToTypeSystemTranslationTable(this.currentAssembly, assemblyDefinition); + // Freezing the assembly here is important: + // otherwise it will be frozen when a compilation is first created + // from it. But freezing has the effect of changing some collection instances + // (to ReadOnlyCollection). This hidden mutation was causing a crash + // when the FastSerializer was saving the assembly at the same time as + // the first compilation was created from it. + // By freezing the assembly now, we ensure it is usable on multiple + // threads without issues. + currentAssembly.Freeze(); var result = this.currentAssembly; this.currentAssembly = null; From b0e1fac6b6a1618b9be19b3e241efabac050daa4 Mon Sep 17 00:00:00 2001 From: Simon Lindgren Date: Tue, 11 Sep 2012 01:02:23 +0200 Subject: [PATCH 06/37] [CodeIssues] Don't suggest moving declarations into invalid or weird places. --- .../VariableDeclaredInWideScopeIssue.cs | 36 +++++++++++++++---- .../VariableDeclaredInWideScopeTests.cs | 21 +++++++++++ 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableDeclaredInWideScopeIssue.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableDeclaredInWideScopeIssue.cs index 9746aaa371..83facbfdd9 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableDeclaredInWideScopeIssue.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/VariableDeclaredInWideScopeIssue.cs @@ -88,14 +88,15 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring var path = GetPath(rootNode, deepestCommonAncestor); // The node that will follow the moved declaration statement - AstNode anchorNode = GetInitialAnchorNode (rootNode, identifiers, path); + AstNode anchorNode = GetInitialAnchorNode(rootNode, identifiers, path); // Restrict path to only those where the initializer has not changed var pathToCheck = path.Skip(1).ToList(); var firstInitializerChangeNode = GetFirstInitializerChange(variableDeclarationStatement, pathToCheck, variableInitializer.Initializer); if (firstInitializerChangeNode != null) { - // The initializer and usage nodes may not be in the same path, so merge them - // instead of just making a path to firstInitializerChange + // The node changing the initializer expression may not be on the path + // to the actual usages of the variable, so we need to merge the paths + // so we get the part of the paths that are common between them var pathToChange = GetPath(rootNode, firstInitializerChangeNode); var deepestCommonIndex = GetLowestCommonAncestorIndex(path, pathToChange); anchorNode = pathToChange [deepestCommonIndex + 1]; @@ -109,9 +110,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring anchorNode = firstBlackListedNode; } - // Get the parent statement - while (anchorNode != null && !(anchorNode is Statement)) - anchorNode = anchorNode.Parent; + anchorNode = GetInsertionPoint(anchorNode); if (anchorNode != null && anchorNode != rootNode && anchorNode.Parent != rootNode) { AddIssue(variableDeclarationStatement, context.TranslateString("Variable could be moved to a nested scope"), @@ -119,6 +118,31 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring } } + static bool IsBannedInsertionPoint(AstNode anchorNode) + { + var parent = anchorNode.Parent; + + // Don't split 'else if ...' into else { if ... } + if (parent is IfElseStatement && anchorNode is IfElseStatement) + return true; + // Don't allow moving the declaration into the resource aquisition of a using statement + if (parent is UsingStatement) + return true; + return false; + } + + static AstNode GetInsertionPoint(AstNode node) + { + while (true) { + if (node == null) + break; + if (node is Statement && !IsBannedInsertionPoint(node)) + break; + node = node.Parent; + } + return node; + } + AstNode GetInitialAnchorNode (BlockStatement rootNode, List identifiers, IList path) { if (identifiers.Count > 1) { diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/VariableDeclaredInWideScopeTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/VariableDeclaredInWideScopeTests.cs index 397374bfb4..cfa1c737e2 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/VariableDeclaredInWideScopeTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/VariableDeclaredInWideScopeTests.cs @@ -379,6 +379,27 @@ class A } "); } + + [Test] + public void DoesNotExpandElseClause () + { + TestStatements(@" + string o = ""Hello World !""; + if (false) { + } else if (!o.Contains (""Foo"")) { + } +", 0); + } + + [Test] + public void DoesNotInsertBlockStatementInResourceAquisition () + { + TestStatements(@" + string o = ""Hello World !""; + using (var file = File.Open(o, FileMode.Open)) + return; +", 0); + } } } From 704b5304d8a2d0e3823db73cd283d217e55296d9 Mon Sep 17 00:00:00 2001 From: Simon Lindgren Date: Mon, 10 Sep 2012 23:44:50 +0200 Subject: [PATCH 07/37] [CodeIssues] Also warn for null or empty checks using 'str.Length == 0' --- .../CodeIssues/StringIsNullOrEmptyIssue.cs | 57 ++++++++++++---- .../StringIsNullOrEmptyInspectorTests.cs | 68 ++++++++++++++++++- 2 files changed, 109 insertions(+), 16 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/StringIsNullOrEmptyIssue.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/StringIsNullOrEmptyIssue.cs index 9d191f63ac..a3cc5237a2 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/StringIsNullOrEmptyIssue.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/StringIsNullOrEmptyIssue.cs @@ -41,47 +41,76 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring public class StringIsNullOrEmptyIssue : ICodeIssueProvider { static readonly Pattern pattern = new Choice { - // str == null || str == "" + // str == null || str == "" + // str == null || str.Length == 0 new BinaryOperatorExpression ( - PatternHelper.CommutativeOperator(new AnyNode ("str"), BinaryOperatorType.Equality, new NullReferenceExpression ()), + PatternHelper.CommutativeOperator (new AnyNode ("str"), BinaryOperatorType.Equality, new NullReferenceExpression ()), BinaryOperatorType.ConditionalOr, - PatternHelper.CommutativeOperator(new Backreference ("str"), BinaryOperatorType.Equality, new PrimitiveExpression ("")) + new Choice { + PatternHelper.CommutativeOperator (new Backreference ("str"), BinaryOperatorType.Equality, new PrimitiveExpression ("")), + PatternHelper.CommutativeOperator ( + new MemberReferenceExpression (new Backreference ("str"), "Length"), + BinaryOperatorType.Equality, + new PrimitiveExpression (0) + ) + } ), // str == "" || str == null + // str.Length == 0 || str == null new BinaryOperatorExpression ( - PatternHelper.CommutativeOperator(new AnyNode ("str"), BinaryOperatorType.Equality, new PrimitiveExpression ("")), + new Choice { + PatternHelper.CommutativeOperator (new AnyNode ("str"), BinaryOperatorType.Equality, new PrimitiveExpression ("")), + PatternHelper.CommutativeOperator ( + new MemberReferenceExpression (new AnyNode ("str"), "Length"), + BinaryOperatorType.Equality, + new PrimitiveExpression (0) + ) + }, BinaryOperatorType.ConditionalOr, PatternHelper.CommutativeOperator(new Backreference ("str"), BinaryOperatorType.Equality, new NullReferenceExpression ()) - ), + ) }; static readonly Pattern negPattern = new Choice { - // str != null && str != "" + // str != null && str != "" + // str != null && str.Length != 0 new BinaryOperatorExpression ( PatternHelper.CommutativeOperator(new AnyNode ("str"), BinaryOperatorType.InEquality, new NullReferenceExpression ()), BinaryOperatorType.ConditionalAnd, - PatternHelper.CommutativeOperator(new Backreference ("str"), BinaryOperatorType.InEquality, new PrimitiveExpression ("")) + new Choice { + PatternHelper.CommutativeOperator (new Backreference ("str"), BinaryOperatorType.InEquality, new PrimitiveExpression ("")), + PatternHelper.CommutativeOperator ( + new MemberReferenceExpression (new Backreference ("str"), "Length"), + BinaryOperatorType.InEquality, + new PrimitiveExpression (0) + ) + } ), // str != "" && str != null + // str.Length != 0 && str != null new BinaryOperatorExpression ( - PatternHelper.CommutativeOperator(new AnyNode ("str"), BinaryOperatorType.InEquality, new PrimitiveExpression ("")), + new Choice { + PatternHelper.CommutativeOperator (new AnyNode ("str"), BinaryOperatorType.InEquality, new PrimitiveExpression ("")), + PatternHelper.CommutativeOperator ( + new MemberReferenceExpression (new AnyNode ("str"), "Length"), + BinaryOperatorType.InEquality, + new PrimitiveExpression (0) + ) + }, BinaryOperatorType.ConditionalAnd, PatternHelper.CommutativeOperator(new Backreference ("str"), BinaryOperatorType.InEquality, new NullReferenceExpression ()) - ), + ) }; public IEnumerable GetIssues(BaseRefactoringContext context) { - return new GatherVisitor(context, this).GetIssues(); + return new GatherVisitor(context).GetIssues(); } class GatherVisitor : GatherVisitorBase { - readonly StringIsNullOrEmptyIssue inspector; - - public GatherVisitor (BaseRefactoringContext ctx, StringIsNullOrEmptyIssue inspector) : base (ctx) + public GatherVisitor (BaseRefactoringContext ctx) : base (ctx) { - this.inspector = inspector; } public override void VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression) diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/StringIsNullOrEmptyInspectorTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/StringIsNullOrEmptyInspectorTests.cs index 51d155ed32..d256ca5682 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/StringIsNullOrEmptyInspectorTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/StringIsNullOrEmptyInspectorTests.cs @@ -410,7 +410,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeIssues } }"); } - + [Test] public void TestInspectorCaseSN8 () { @@ -422,7 +422,39 @@ namespace ICSharpCode.NRefactory.CSharp.CodeIssues ; } }"; - + + TestRefactoringContext context; + var issues = GetIssues (new StringIsNullOrEmptyIssue (), input, out context); + Assert.AreEqual (1, issues.Count); + CheckFix (context, issues, @"class Foo +{ + void Bar (string str) + { + if (string.IsNullOrEmpty (str)) + ; + } +}"); + } + + [TestCase("str == null || str.Length == 0")] + [TestCase("str == null || 0 == str.Length")] + [TestCase("str.Length == 0 || str == null")] + [TestCase("0 == str.Length || str == null")] + [TestCase("null == str || str.Length == 0")] + [TestCase("null == str || 0 == str.Length")] + [TestCase("str.Length == 0 || null == str")] + [TestCase("0 == str.Length || null == str")] + public void TestInspectorCaseNL (string expression) + { + var input = @"class Foo +{ + void Bar (string str) + { + if (" + expression + @") + ; + } +}"; + TestRefactoringContext context; var issues = GetIssues (new StringIsNullOrEmptyIssue (), input, out context); Assert.AreEqual (1, issues.Count); @@ -433,6 +465,38 @@ namespace ICSharpCode.NRefactory.CSharp.CodeIssues if (string.IsNullOrEmpty (str)) ; } +}"); + } + + [TestCase("str != null && str.Length != 0")] + [TestCase("str != null && 0 != str.Length")] + [TestCase("str.Length != 0 && str != null")] + [TestCase("0 != str.Length && str != null")] + [TestCase("null != str && str.Length != 0")] + [TestCase("null != str && 0 != str.Length")] + [TestCase("str.Length != 0 && null != str")] + [TestCase("0 != str.Length && null != str")] + public void TestInspectorCaseLN (string expression) + { + var input = @"class Foo +{ + void Bar (string str) + { + if (" + expression + @") + ; + } +}"; + + TestRefactoringContext context; + var issues = GetIssues (new StringIsNullOrEmptyIssue (), input, out context); + Assert.AreEqual (1, issues.Count); + CheckFix (context, issues, @"class Foo +{ + void Bar (string str) + { + if (!string.IsNullOrEmpty (str)) + ; + } }"); } } From 3cf3032f50bb13c3784e54fd8dfedb6c939d2c27 Mon Sep 17 00:00:00 2001 From: Simon Lindgren Date: Tue, 11 Sep 2012 03:12:48 +0200 Subject: [PATCH 08/37] [CodeActions] Clean up StatementsToInitializerConverter and InitializerPath. Also, rename InitializerPath to AccessPath. --- .../ICSharpCode.NRefactory.CSharp.csproj | 2 +- .../{InitializerPath.cs => AccessPath.cs} | 85 ++++++--------- .../StatementsToInitializerConverter.cs | 100 +++++++++--------- 3 files changed, 83 insertions(+), 104 deletions(-) rename ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ConvertToInitializer/{InitializerPath.cs => AccessPath.cs} (65%) diff --git a/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj b/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj index 10a85c68b2..d1517435ec 100644 --- a/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj +++ b/ICSharpCode.NRefactory.CSharp/ICSharpCode.NRefactory.CSharp.csproj @@ -476,7 +476,6 @@ - @@ -503,6 +502,7 @@ + diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ConvertToInitializer/InitializerPath.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ConvertToInitializer/AccessPath.cs similarity index 65% rename from ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ConvertToInitializer/InitializerPath.cs rename to ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ConvertToInitializer/AccessPath.cs index 492214c500..6968c9675f 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ConvertToInitializer/InitializerPath.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ConvertToInitializer/AccessPath.cs @@ -32,25 +32,16 @@ using System.Linq; namespace ICSharpCode.NRefactory.CSharp.Refactoring { - class InitializerPath + class AccessPath { - InitializerPath(object anchor) + public AccessPath(IVariable target) { - this.anchor = anchor; + VariableRoot = target; MemberPath = new List(); } - public InitializerPath(IVariable target) : this((object)target) + public static AccessPath FromResolveResult(ResolveResult resolveResult) { - } - - public InitializerPath(IMember target) : this((object)target) - { - } - - public static InitializerPath FromResolveResult(ResolveResult resolveResult) - { - InitializerPath initializerPath = null; var memberPath = new List(); var currentResolveResult = resolveResult; do { @@ -59,42 +50,38 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring memberPath.Add(memberResolveResult.Member); currentResolveResult = memberResolveResult.TargetResult; } else if (currentResolveResult is LocalResolveResult) { + // This is the root variable var localResolveResult = (LocalResolveResult)currentResolveResult; memberPath.Reverse(); - initializerPath = new InitializerPath(localResolveResult.Variable) { + return new AccessPath(localResolveResult.Variable) { MemberPath = memberPath }; - break; } else if (currentResolveResult is ThisResolveResult) { break; } else { + // Unsupported path return null; } - } while (currentResolveResult != null); - if (initializerPath == null) { - // This path is rooted at a member - memberPath.Reverse(); - initializerPath = new InitializerPath(memberPath [0]) { - MemberPath = memberPath.Skip(1).ToList() - }; - } - return initializerPath; + memberPath.Reverse(); + return new AccessPath(null) { + MemberPath = memberPath + }; } - public InitializerPath GetParentPath() + public AccessPath GetParentPath() { if (MemberPath.Count < 1) throw new InvalidOperationException("Cannot get the parent path of a path that does not contain any members."); - return new InitializerPath(anchor) { + return new AccessPath(VariableRoot) { MemberPath = MemberPath.Take(MemberPath.Count - 1).ToList() }; } - public bool IsSubPath(InitializerPath other) + public bool IsSubPath(AccessPath other) { - if (!other.anchor.Equals(anchor)) + if (!object.Equals(other.VariableRoot, VariableRoot)) return false; if (MemberPath.Count <= other.MemberPath.Count) return false; @@ -105,22 +92,19 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring return true; } - object anchor; + public IVariable VariableRoot { get; set; } - public IVariable VariableRoot { - get { return anchor as IVariable; } - } - - public IMember MemberRoot { - get { return anchor as IMember; } + public int PartCount { + get { + return MemberPath.Count + (VariableRoot == null ? 0 : 1); + } } public string RootName { get { - if (anchor is IMember) - return (anchor as IMember).Name; - else - return (anchor as IVariable).Name; + if (VariableRoot != null) + return VariableRoot.Name; + return MemberPath.First().Name; } } @@ -128,12 +112,12 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring public override bool Equals(object obj) { - if (obj.GetType() != typeof(InitializerPath)) + if (obj.GetType() != typeof(AccessPath)) return false; - var other = (InitializerPath)obj; + var other = (AccessPath)obj; - if (!object.Equals(anchor, other.anchor)) + if (!object.Equals(VariableRoot, other.VariableRoot)) return false; if (MemberPath.Count != other.MemberPath.Count) @@ -148,29 +132,28 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring public override int GetHashCode() { - int hash = anchor.GetHashCode(); + int hash = VariableRoot != null ? VariableRoot.GetHashCode() : 37; foreach (var member in MemberPath) - hash ^= member.GetHashCode(); + hash ^= 31 * member.GetHashCode(); return hash; } - public static bool operator==(InitializerPath left, InitializerPath right) + public static bool operator==(AccessPath left, AccessPath right) { return object.Equals(left, right); } - public static bool operator!=(InitializerPath left, InitializerPath right) + public static bool operator!=(AccessPath left, AccessPath right) { return !object.Equals(left, right); } public override string ToString() { - if (MemberPath.Count > 0) - return string.Format("[InitializerPath: {0}.{1}]", RootName, - string.Join(".", MemberPath.Select(member => member.Name))); - else - return string.Format("[InitializerPath: {0}]", RootName); + string memberPathString = string.Join(".", MemberPath.Select(member => member.Name)); + if (VariableRoot == null) + return string.Format("[AccessPath: {0}]", memberPathString); + return string.Format("[AccessPath: {0}.{1}]", VariableRoot.Name, memberPathString); } } diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ConvertToInitializer/StatementsToInitializerConverter.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ConvertToInitializer/StatementsToInitializerConverter.cs index 6a282ad1d8..2eb2779d7e 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ConvertToInitializer/StatementsToInitializerConverter.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/ConvertToInitializer/StatementsToInitializerConverter.cs @@ -37,9 +37,9 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring { public class StatementsToInitializerConverter { - IDictionary initializers = new Dictionary(); + IDictionary accessPaths = new Dictionary(); IList comments = new List(); - InitializerPath mainInitializerPath; + AccessPath mainAccessPath; RefactoringContext context; public StatementsToInitializerConverter(RefactoringContext context) @@ -50,13 +50,11 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring void Initialize(AstNode targetNode) { var target = context.Resolve(targetNode); - if (target is LocalResolveResult) { - mainInitializerPath = new InitializerPath(((LocalResolveResult)target).Variable); - } else if (target is MemberResolveResult) { - mainInitializerPath = new InitializerPath(((MemberResolveResult)target).Member); - } else { - throw new ArgumentException("variableInitializer must target a variable or a member."); - } + var targetInitializerPath = AccessPath.FromResolveResult(target); + if (targetInitializerPath == null) + throw new ArgumentException(string.Format("Could not create the main initializer path from resolve result ({0})", target)); + + mainAccessPath = targetInitializerPath; } public VariableInitializer ConvertToInitializer(VariableInitializer variableInitializer, ref IList statements) @@ -67,11 +65,11 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring throw new ArgumentNullException("statements"); Initialize(variableInitializer); - initializers [mainInitializerPath] = variableInitializer.Initializer.Clone(); + accessPaths [mainAccessPath] = variableInitializer.Initializer.Clone(); Convert(statements); - statements = ReplacementNodeHelper.GetReplacedNodes(initializers [mainInitializerPath]); - return new VariableInitializer(mainInitializerPath.RootName, initializers [mainInitializerPath]); + statements = ReplacementNodeHelper.GetReplacedNodes(accessPaths [mainAccessPath]); + return new VariableInitializer(mainAccessPath.RootName, accessPaths [mainAccessPath]); } public AssignmentExpression ConvertToInitializer(AssignmentExpression assignmentExpression, ref IList statements) @@ -84,11 +82,11 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring throw new ArgumentException("assignmentExpression.Right must be an ObjectCreateExpression", "assignmentExpression"); Initialize(assignmentExpression.Left); - initializers [mainInitializerPath] = assignmentExpression.Right.Clone(); + accessPaths [mainAccessPath] = assignmentExpression.Right.Clone(); Convert(statements); - statements = ReplacementNodeHelper.GetReplacedNodes(initializers [mainInitializerPath]); - return new AssignmentExpression(new IdentifierExpression(mainInitializerPath.RootName), initializers [mainInitializerPath]); + statements = ReplacementNodeHelper.GetReplacedNodes(accessPaths [mainAccessPath]); + return new AssignmentExpression(new IdentifierExpression(mainAccessPath.RootName), accessPaths [mainAccessPath]); } void Convert(IList originalStatements) @@ -123,7 +121,6 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring VariableInitializer variableInitializer; var variableDeclarationStatement = node as VariableDeclarationStatement; if (variableDeclarationStatement == null) { - variableInitializer = VariableInitializer.Null; return false; } variableInitializer = variableDeclarationStatement.Variables.FirstOrNullObject(); @@ -142,7 +139,6 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring var invocationExpression = expressionStatement.Expression as InvocationExpression; if (invocationExpression == null) return false; - var target = invocationExpression.Target; var invocationResolveResult = context.Resolve(invocationExpression) as InvocationResolveResult; if (invocationResolveResult == null) return false; @@ -152,15 +148,15 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring if (targetResult is MemberResolveResult) return false; - ArrayInitializerExpression tuple = new ArrayInitializerExpression(); + var tuple = new ArrayInitializerExpression(); foreach (var argument in invocationExpression.Arguments) { var argumentLocalResolveResult = context.Resolve(argument) as LocalResolveResult; if (argumentLocalResolveResult != null) { - var initializerPath = InitializerPath.FromResolveResult(argumentLocalResolveResult); - if (initializerPath == null || !initializers.ContainsKey(initializerPath)) + var initializerPath = AccessPath.FromResolveResult(argumentLocalResolveResult); + if (initializerPath == null || !accessPaths.ContainsKey(initializerPath)) return false; // Add a clone, since we do not yet know if this is where the initializer will be used - var initializerClone = initializers[initializerPath].Clone(); + var initializerClone = accessPaths[initializerPath].Clone(); tuple.Elements.Add(initializerClone); } else { tuple.Elements.Add(argument.Clone()); @@ -168,11 +164,11 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring } ReplacementNodeHelper.AddReplacementAnnotation(tuple, expressionStatement); - var targetPath = InitializerPath.FromResolveResult(targetResult); - if (targetPath == null || !initializers.ContainsKey(targetPath)) + var targetPath = AccessPath.FromResolveResult(targetResult); + if (targetPath == null || !accessPaths.ContainsKey(targetPath)) return false; InsertImplicitInitializersForPath(targetPath); - var targetInitializer = initializers [targetPath]; + var targetInitializer = accessPaths [targetPath]; AddToInitializer(targetInitializer, tuple); return true; } @@ -196,29 +192,29 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring void AddNewVariable(IVariable variable, Expression initializer, AstNode node) { - var variablePath = new InitializerPath(variable); + var variablePath = new AccessPath(variable); var rightResolveResult = context.Resolve(initializer) as LocalResolveResult; if (rightResolveResult != null) { - var rightPath = InitializerPath.FromResolveResult(rightResolveResult); - if (rightPath != null && initializers.ContainsKey(rightPath)) { - var rightInitializer = initializers [rightPath]; + var rightPath = AccessPath.FromResolveResult(rightResolveResult); + if (rightPath != null && accessPaths.ContainsKey(rightPath)) { + var rightInitializer = accessPaths [rightPath]; ReplacementNodeHelper.AddReplacementAnnotation(rightInitializer, node); - initializers.Remove(rightPath); - initializers [variablePath] = rightInitializer; - if (rightPath == mainInitializerPath) { - mainInitializerPath = variablePath; + accessPaths.Remove(rightPath); + accessPaths [variablePath] = rightInitializer; + if (rightPath == mainAccessPath) { + mainAccessPath = variablePath; } } } else { - initializers [variablePath] = ReplacementNodeHelper.CloneWithReplacementAnnotation(initializer, node); + accessPaths [variablePath] = ReplacementNodeHelper.CloneWithReplacementAnnotation(initializer, node); } } - void AddOldAnnotationsToInitializer(InitializerPath targetPath, Expression initializer) + void AddOldAnnotationsToInitializer(AccessPath targetPath, IAnnotatable initializer) { if (targetPath != null) { - if (initializers.ContainsKey(targetPath)) { - foreach (var astNode in ReplacementNodeHelper.GetAllReplacementAnnotations(initializers[targetPath])) { + if (accessPaths.ContainsKey(targetPath)) { + foreach (var astNode in ReplacementNodeHelper.GetAllReplacementAnnotations(accessPaths[targetPath])) { initializer.AddAnnotation(astNode); } } @@ -231,16 +227,16 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring var leftResolveResult = context.Resolve(left); Expression initializer; if (rightResolveResult != null) { - var rightPath = InitializerPath.FromResolveResult(rightResolveResult); - if (initializers.ContainsKey(rightPath)) { - initializer = initializers [rightPath]; + var rightPath = AccessPath.FromResolveResult(rightResolveResult); + if (accessPaths.ContainsKey(rightPath)) { + initializer = accessPaths [rightPath]; } else { initializer = right.Clone(); } } else { initializer = right.Clone(); } - var leftPath = InitializerPath.FromResolveResult(leftResolveResult); + var leftPath = AccessPath.FromResolveResult(leftResolveResult); if (leftPath == null) { return false; } @@ -249,15 +245,15 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring // to the same variable/member. AddOldAnnotationsToInitializer(leftPath, initializer); - if (leftPath.MemberPath.Count == 0) { + if (leftPath.PartCount == 1) { ReplacementNodeHelper.AddReplacementAnnotation(initializer, node); - initializers [leftPath] = initializer; + accessPaths [leftPath] = initializer; return true; } if (!(leftResolveResult is MemberResolveResult)) return false; - Debug.Assert(leftPath.MemberPath.Count > 0, "No top level assignment should get here."); + Debug.Assert(leftPath.PartCount > 1, "No top level assignment should get here."); var parentKey = leftPath.GetParentPath(); var member = leftPath.MemberPath.Last(); @@ -266,13 +262,13 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring if (!success) return false; - var parentInitializer = initializers [parentKey]; + var parentInitializer = accessPaths [parentKey]; AddToInitializer(parentInitializer, comments.ToArray()); comments.Clear(); AddToInitializer(parentInitializer, new NamedExpression(member.Name, initializer)); ReplacementNodeHelper.AddReplacementAnnotation(initializer, node); - initializers [leftPath] = initializer; + accessPaths [leftPath] = initializer; return true; } @@ -347,8 +343,8 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring var resolveResult = context.Resolve(memberReference) as MemberResolveResult; if (resolveResult == null) continue; - var initializerPath = InitializerPath.FromResolveResult(resolveResult); - if (initializerPath != null && initializers.ContainsKey(initializerPath)) + var initializerPath = AccessPath.FromResolveResult(resolveResult); + if (initializerPath != null && accessPaths.ContainsKey(initializerPath)) return true; } return false; @@ -356,12 +352,12 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring bool VariableHasBeenConverted(IVariable variable) { - return initializers.Any(item => item.Key.VariableRoot.Equals(variable)); + return accessPaths.Any(item => item.Key.VariableRoot.Equals(variable)); } - bool InsertImplicitInitializersForPath(InitializerPath path) + bool InsertImplicitInitializersForPath(AccessPath path) { - if (initializers.ContainsKey(path)) + if (accessPaths.ContainsKey(path)) return true; if (path.MemberPath.Count == 0) @@ -371,11 +367,11 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring if (!success) return false; - var parentInitializer = initializers [parentPath]; + var parentInitializer = accessPaths [parentPath]; var initializer = new ArrayInitializerExpression(); var namedExpression = new NamedExpression(path.MemberPath [path.MemberPath.Count - 1].Name, initializer); AddToInitializer(parentInitializer, namedExpression); - initializers [path] = initializer; + accessPaths [path] = initializer; return true; } From b71617d8161460923e6935df6b634866db3f792e Mon Sep 17 00:00:00 2001 From: Simon Lindgren Date: Tue, 11 Sep 2012 02:08:31 +0200 Subject: [PATCH 09/37] [CodeIssues] Fix handling of member references in AssignmentMadeToSameVariableIssue. --- .../AssignmentMadeToSameVariableIssue.cs | 18 +++-- .../AssignmentMadeToSameVariableIssueTests.cs | 65 ++++++++++++++++++- 2 files changed, 75 insertions(+), 8 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/AssignmentMadeToSameVariableIssue.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/AssignmentMadeToSameVariableIssue.cs index 95a9c0a6d1..5cd23c17a9 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/AssignmentMadeToSameVariableIssue.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/AssignmentMadeToSameVariableIssue.cs @@ -29,6 +29,7 @@ using System.Collections.Generic; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.PatternMatching; +using System.Linq; namespace ICSharpCode.NRefactory.CSharp.Refactoring { @@ -62,14 +63,9 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring var resolveResult = ctx.Resolve (assignmentExpression.Left); var memberResolveResult = resolveResult as MemberResolveResult; if (memberResolveResult != null) { - if (!(memberResolveResult.Member is IField)) + var memberResolveResult2 = ctx.Resolve (assignmentExpression.Right) as MemberResolveResult; + if (memberResolveResult2 == null || !AreEquivalent(memberResolveResult, memberResolveResult2)) return; - if (!assignmentExpression.Left.Match (assignmentExpression.Right).Success) { - // in case: this.field = field - var memberResolveResult2 = ctx.Resolve (assignmentExpression.Right) as MemberResolveResult; - if (memberResolveResult2 == null || memberResolveResult.Member != memberResolveResult2.Member) - return; - } } else if (resolveResult is LocalResolveResult) { if (!assignmentExpression.Left.Match (assignmentExpression.Right).Success) return; @@ -89,6 +85,14 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring AddIssue (node, ctx.TranslateString ("CS1717:Assignment made to same variable"), new [] { new CodeAction (ctx.TranslateString ("Remove assignment"), action) }); } + + static bool AreEquivalent(ResolveResult first, ResolveResult second) + { + var firstPath = InitializerPath.FromResolveResult(first); + var secondPath = InitializerPath.FromResolveResult(second); + return firstPath != null && firstPath.Equals(secondPath) && !firstPath.MemberPath.Any(m => !(m is IField)) && + (firstPath.MemberRoot == null || firstPath.MemberRoot is IField); + } } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/AssignmentMadeToSameVariableIssueTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/AssignmentMadeToSameVariableIssueTests.cs index 687d84a299..b167903d4c 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/AssignmentMadeToSameVariableIssueTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/AssignmentMadeToSameVariableIssueTests.cs @@ -127,7 +127,7 @@ class TestClass }"; Test (input, 2, output); } - + [Test] public void TestNoIssue () { @@ -146,6 +146,69 @@ class TestClass this.a = a; Prop = Prop; } +}"; + Test (input, 0); + } + + [Test] + public void IgnoresAssignmentWithDifferentRootObjects () + { + var input = @" +class TestClass +{ + int a; + + void TestMethod (TestClass tc) + { + a = tc.a; + } +}"; + Test (input, 0); + } + + [Test] + public void NestedFieldAccess () + { + var input = @" +class TestClass +{ + int a; + + TestClass nested; + + void TestMethod () + { + nested.nested.a = nested.nested.a; + } +}"; + var output = @" +class TestClass +{ + int a; + + TestClass nested; + + void TestMethod () + { + } +}"; + Test (input, 1, output); + } + + [Test] + public void NestedPropertyAccess () + { + var input = @" +class TestClass +{ + int a; + + TestClass nested { get; set; } + + void TestMethod () + { + nested.nested.a = nested.nested.a; + } }"; Test (input, 0); } From fa3cf3b6e168229372fa27d349ffecde1ba913f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Tue, 11 Sep 2012 09:17:56 +0200 Subject: [PATCH 10/37] Fixed completion bug. --- .../Completion/CSharpCompletionEngine.cs | 43 +++++++------------ .../CodeCompletion/CodeCompletionBugTests.cs | 37 ++++++++++++++++ 2 files changed, 53 insertions(+), 27 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs index bec200f310..5356c0e3e6 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs @@ -627,6 +627,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } var contextList = new CompletionDataWrapper(this); var identifierStart = GetExpressionAtCursor(); + if (identifierStart != null) { if (identifierStart.Node is TypeParameterDeclaration) { return null; @@ -669,8 +670,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion if (!(Char.IsWhiteSpace(prevCh) || allowedChars.IndexOf(prevCh) >= 0)) { return null; } - - // Do not pop up completion on identifier identifier (should be handled by keyword completion). + // Do not pop up completion on identifier identifier (should be handled by keyword completion). tokenIndex = offset - 1; token = GetPreviousToken(ref tokenIndex, false); if (token == "class" || token == "interface" || token == "struct" || token == "enum" || token == "namespace") { @@ -688,7 +688,8 @@ namespace ICSharpCode.NRefactory.CSharp.Completion // after these always follows a name return null; } - + Console.WriteLine (1); + if (identifierStart == null && !string.IsNullOrEmpty(token) && !IsInsideCommentStringOrDirective() && (prevToken2 == ";" || prevToken2 == "{" || prevToken2 == "}")) { char last = token [token.Length - 1]; if (char.IsLetterOrDigit(last) || last == '_' || token == ">") { @@ -708,7 +709,8 @@ namespace ICSharpCode.NRefactory.CSharp.Completion if (n != null && n.Parent is AnonymousTypeCreateExpression) { AutoSelect = false; } - + Console.WriteLine (2); + // Handle foreach (type name _ if (n is IdentifierExpression) { var prev = n.GetPrevNode() as ForeachStatement; @@ -719,24 +721,10 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } return null; } - - // var astResolver = new CSharpAstResolver( - // GetState(), - // identifierStart.Unit, - // CSharpUnresolvedFile - // ); - // - // foreach (var type in CreateFieldAction.GetValidTypes(astResolver, (Expression)n)) { - // if (type.Kind == TypeKind.Delegate) { - // AddDelegateHandlers(contextList, type, false, false); - // AutoSelect = false; - // AutoCompleteEmptyMatch = false; - // } - // } } // Handle object/enumerable initialzer expressions: "new O () { P$" - if (n is IdentifierExpression && n.Parent is ArrayInitializerExpression) { + if (n is IdentifierExpression && n.Parent is ArrayInitializerExpression && !(n.Parent.Parent is ArrayCreateExpression)) { var result = HandleObjectInitializer(identifierStart.Unit, n); if (result != null) return result; @@ -777,7 +765,8 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } } } - + Console.WriteLine (3); + if (n != null && n.Parent is ObjectCreateExpression) { var invokeResult = ResolveExpression(n.Parent); var mgr = invokeResult != null ? invokeResult.Item1 as ResolveResult : null; @@ -2014,7 +2003,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion "delegate", "Creates anonymous delegate.", "delegate {" + EolMarker + thisLineIndent + IndentString + "|" + delegateEndString - ); + ); } var sb = new StringBuilder("("); var sbWithoutTypes = new StringBuilder("("); @@ -2026,10 +2015,10 @@ namespace ICSharpCode.NRefactory.CSharp.Completion sb.Append(", "); sbWithoutTypes.Append(", "); } - var convertedParameter = builder.ConvertParameter (delegateMethod.Parameters [k]); + var convertedParameter = builder.ConvertParameter(delegateMethod.Parameters [k]); if (convertedParameter.ParameterModifier == ParameterModifier.Params) convertedParameter.ParameterModifier = ParameterModifier.None; - sb.Append(convertedParameter.GetText (FormattingPolicy)); + sb.Append(convertedParameter.GetText(FormattingPolicy)); sbWithoutTypes.Append(delegateMethod.Parameters [k].Name); } @@ -2039,22 +2028,22 @@ namespace ICSharpCode.NRefactory.CSharp.Completion "delegate" + sb, "Creates anonymous delegate.", "delegate" + sb + " {" + EolMarker + thisLineIndent + IndentString + "|" + delegateEndString - ); + ); if (!completionList.Result.Any(data => data.DisplayText == sb.ToString())) { completionList.AddCustom( sb.ToString(), "Creates typed lambda expression.", sb + " => |" + (addSemicolon ? ";" : "") - ); + ); } - if (!delegateMethod.Parameters.Any (p => p.IsOut || p.IsRef) && !completionList.Result.Any(data => data.DisplayText == sbWithoutTypes.ToString())) { + if (!delegateMethod.Parameters.Any(p => p.IsOut || p.IsRef) && !completionList.Result.Any(data => data.DisplayText == sbWithoutTypes.ToString())) { completionList.AddCustom( sbWithoutTypes.ToString(), "Creates lambda expression.", sbWithoutTypes + " => |" + (addSemicolon ? ";" : "") - ); + ); } /* TODO:Make factory method out of it. // It's needed to temporarly disable inserting auto matching bracket because the anonymous delegates are selectable with '(' diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs index 36067ec24d..ce8136be99 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs @@ -5415,5 +5415,42 @@ public class FooBar }); } + /// + /// Bug 7041 - No completion inside new[] + /// + [Test()] + public void TestBug7041() + { + CombinedProviderTest( + @"using System; + + namespace ConsoleApplication2 + { + class Test + { + public string[] Foo { get; set; } + } + + class Program + { + static void Main(string[] args) + { + var a = new Test () + { + $Foo = new [] { S$ + } + + } + } + } + +", provider => { + Assert.IsNotNull(provider.Find("System")); + }); + } + + + + } } From 90bea8ed51e4ed38aa97047189b003330d61eabf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Tue, 11 Sep 2012 09:29:03 +0200 Subject: [PATCH 11/37] Fixed Issue #109 'Code Completion shows 'ushort' in global scope'. --- .../Completion/CSharpCompletionEngine.cs | 20 ++++++++++++------ .../CodeCompletion/CodeCompletionBugTests.cs | 21 +++++++++++++++++-- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs index 5356c0e3e6..b337f2948b 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs @@ -627,7 +627,6 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } var contextList = new CompletionDataWrapper(this); var identifierStart = GetExpressionAtCursor(); - if (identifierStart != null) { if (identifierStart.Node is TypeParameterDeclaration) { return null; @@ -1136,6 +1135,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion return wrapper.Result; } } + /* if (Unit != null && (node == null || node is TypeDeclaration)) { var constructor = Unit.GetNodeAt( location.Line, @@ -1181,6 +1181,9 @@ namespace ICSharpCode.NRefactory.CSharp.Completion void AddContextCompletion(CompletionDataWrapper wrapper, CSharpResolver state, AstNode node) { + int i = offset - 1; + var isInGlobalDelegate = node == null && state.CurrentTypeDefinition == null && GetPreviousToken(ref i, true) == "delegate"; + if (state != null && !(node is AstType)) { foreach (var variable in state.LocalVariables) { if (variable.Region.IsInside(location.Line, location.Column - 1)) { @@ -1211,10 +1214,12 @@ namespace ICSharpCode.NRefactory.CSharp.Completion return t.GetAllBaseTypeDefinitions().Any(bt => bt.Equals(attribute)) ? t : null; }; } - AddTypesAndNamespaces(wrapper, state, node, typePred); - - wrapper.Result.Add(factory.CreateLiteralCompletionData("global")); + if (node != null || state.CurrentTypeDefinition != null || isInGlobalDelegate) { + AddTypesAndNamespaces(wrapper, state, node, typePred); + wrapper.Result.Add(factory.CreateLiteralCompletionData("global")); + } + if (!(node is AstType)) { if (currentMember != null || node is Expression) { AddKeywords(wrapper, statementStartKeywords); @@ -1224,7 +1229,8 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } else if (currentType != null) { AddKeywords(wrapper, typeLevelKeywords); } else { - AddKeywords(wrapper, globalLevelKeywords); + if (!isInGlobalDelegate) + AddKeywords(wrapper, globalLevelKeywords); } var prop = currentMember as IUnresolvedProperty; if (prop != null && prop.Setter != null && prop.Setter.Region.IsInside(location)) { @@ -1242,7 +1248,9 @@ namespace ICSharpCode.NRefactory.CSharp.Completion AddKeywords(wrapper, parameterTypePredecessorKeywords); } } - AddKeywords(wrapper, primitiveTypesKeywords); + + if (node != null || state.CurrentTypeDefinition != null || isInGlobalDelegate) + AddKeywords(wrapper, primitiveTypesKeywords); if (currentMember != null && (node is IdentifierExpression || node is SimpleType) && (node.Parent is ExpressionStatement || node.Parent is ForeachStatement || node.Parent is UsingStatement)) { wrapper.AddCustom("var"); wrapper.AddCustom("dynamic"); diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs index ce8136be99..cdb5b8fa42 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs @@ -5449,8 +5449,25 @@ public class FooBar }); } + [Test()] + public void TestGlobalPrimitiveTypes() + { + CombinedProviderTest( + @"$u$", provider => { + Assert.IsNotNull(provider.Find("using")); + Assert.IsNull(provider.Find("ushort")); + }); + } - - + [Test()] + public void TestGlobalPrimitiveTypesCase2() + { + CombinedProviderTest( + @"$delegate u$", provider => { + Assert.IsNotNull(provider.Find("ushort")); + Assert.IsNotNull(provider.Find("System")); + Assert.IsNull(provider.Find("using")); + }); + } } } From ac21fc0b32f0c10f96fe8499d3e051333b37152b Mon Sep 17 00:00:00 2001 From: Simon Lindgren Date: Tue, 11 Sep 2012 09:43:41 +0200 Subject: [PATCH 12/37] [CodeIssues] Fix compilation errors in AssignmentMadeToSameVariable. --- .../CodeIssues/AssignmentMadeToSameVariableIssue.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/AssignmentMadeToSameVariableIssue.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/AssignmentMadeToSameVariableIssue.cs index 5cd23c17a9..7437e74f1c 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/AssignmentMadeToSameVariableIssue.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/AssignmentMadeToSameVariableIssue.cs @@ -88,10 +88,9 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring static bool AreEquivalent(ResolveResult first, ResolveResult second) { - var firstPath = InitializerPath.FromResolveResult(first); - var secondPath = InitializerPath.FromResolveResult(second); - return firstPath != null && firstPath.Equals(secondPath) && !firstPath.MemberPath.Any(m => !(m is IField)) && - (firstPath.MemberRoot == null || firstPath.MemberRoot is IField); + var firstPath = AccessPath.FromResolveResult(first); + var secondPath = AccessPath.FromResolveResult(second); + return firstPath != null && firstPath.Equals(secondPath) && !firstPath.MemberPath.Any(m => !(m is IField)); } } } From 12f2f2793ebbcb78e7a7258579cefef7c73cbe4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Tue, 11 Sep 2012 09:57:20 +0200 Subject: [PATCH 13/37] Removed debug messages. --- .../Completion/CSharpCompletionEngine.cs | 3 --- .../Refactoring/CodeActions/CheckIfParameterIsNullAction.cs | 1 - ICSharpCode.NRefactory.CSharp/Resolver/ResolveAtLocation.cs | 1 - 3 files changed, 5 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs index b337f2948b..957477f46d 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs @@ -687,7 +687,6 @@ namespace ICSharpCode.NRefactory.CSharp.Completion // after these always follows a name return null; } - Console.WriteLine (1); if (identifierStart == null && !string.IsNullOrEmpty(token) && !IsInsideCommentStringOrDirective() && (prevToken2 == ";" || prevToken2 == "{" || prevToken2 == "}")) { char last = token [token.Length - 1]; @@ -708,7 +707,6 @@ namespace ICSharpCode.NRefactory.CSharp.Completion if (n != null && n.Parent is AnonymousTypeCreateExpression) { AutoSelect = false; } - Console.WriteLine (2); // Handle foreach (type name _ if (n is IdentifierExpression) { @@ -764,7 +762,6 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } } } - Console.WriteLine (3); if (n != null && n.Parent is ObjectCreateExpression) { var invokeResult = ResolveExpression(n.Parent); diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/CheckIfParameterIsNullAction.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/CheckIfParameterIsNullAction.cs index 8a5edc864f..e32f327c1c 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/CheckIfParameterIsNullAction.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeActions/CheckIfParameterIsNullAction.cs @@ -54,7 +54,6 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring Condition = new BinaryOperatorExpression (new IdentifierExpression (parameter.Name), BinaryOperatorType.Equality, new NullReferenceExpression ()), TrueStatement = new ThrowStatement (new ObjectCreateExpression (context.CreateShortType("System", "ArgumentNullException"), new PrimitiveExpression (parameter.Name))) }; - System.Console.WriteLine(bodyStatement.StartLocation +"/" + bodyStatement.EndLocation); script.AddTo(bodyStatement, statement); }); } diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveAtLocation.cs b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveAtLocation.cs index 43cc5ff3d7..016fe07b78 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/ResolveAtLocation.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/ResolveAtLocation.cs @@ -58,7 +58,6 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver node = node.Parent; } else if (node.NodeType == NodeType.Token) { if (node.Parent is IndexerExpression || node.Parent is ConstructorInitializer) { - Console.WriteLine (2); // There's no other place where one could hover to see the indexer's tooltip, // so we need to resolve it when hovering over the '[' or ']'. // For constructor initializer, the same applies to the 'base'/'this' token. From 5670248de8ade23ad5f3f34e2b4745bfaafcf4c0 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 11 Sep 2012 09:56:25 +0200 Subject: [PATCH 14/37] Add 'FullTypeName' struct, and use it to represent type names. Contains some breaking API changes: - Renamed 'FullNameAndTypeParameterCount' to 'TopLevelTypeName'. - IAssembly.GetTypeDefinition(string, string, int) -> IAssembly.GetTypeDefinition(TopLevelTypeName) - IAssembly.GetTypeDefinition(IUnresolvedTypeDefinition) -> IAssembly.GetTypeDefinition(FullTypeName) - GetClassTypeReference now supports nested types --- .../Completion/CSharpCompletionEngine.cs | 4 +- .../Refactoring/RefactoringContext.cs | 2 +- .../Refactoring/TypeSystemAstBuilder.cs | 18 +- .../TypeSystem/AliasNamespaceReference.cs | 6 + .../TypeSystem/CSharpAssembly.cs | 23 +- .../MemberTypeOrNamespaceReference.cs | 15 +- .../SimpleTypeOrNamespaceReference.cs | 13 +- .../TypeSystem/TypeOrNamespaceReference.cs | 14 +- .../CSharp/CodeCompletion/TestBase.cs | 4 +- .../Refactoring/TypeSystemAstBuilderTests.cs | 2 +- .../CSharp/Resolver/ConversionsTest.cs | 4 +- .../CSharp/Resolver/MemberLookupTests.cs | 6 +- .../Documentation/IDStringTests.cs | 2 +- .../TypeSystem/GetAllBaseTypesTest.cs | 5 +- .../TypeSystem/GetMembersTests.cs | 10 +- .../TypeSystem/TypeParameterTests.cs | 6 +- .../TypeSystem/TypeSystemHelper.cs | 6 + .../TypeSystem/TypeSystemTests.cs | 9 +- .../GetPotentiallyNestedClassTypeReference.cs | 2 +- .../ICSharpCode.NRefactory.csproj | 4 +- .../TypeSystem/CecilLoader.cs | 10 +- .../TypeSystem/ExtensionMethods.cs | 57 +++- .../TypeSystem/FullTypeName.cs | 312 ++++++++++++++++++ .../TypeSystem/IAssembly.cs | 2 +- .../TypeSystem/ICompilation.cs | 3 + .../TypeSystem/ITypeDefinition.cs | 6 + .../DefaultResolvedTypeDefinition.cs | 4 + .../DefaultUnresolvedAssembly.cs | 26 +- .../DefaultUnresolvedTypeDefinition.cs | 24 +- .../FullNameAndTypeParameterCount.cs | 69 +--- .../Implementation/GetClassTypeReference.cs | 99 +++--- .../Implementation/KnownTypeCache.cs | 2 +- .../TypeSystem/Implementation/UnknownType.cs | 50 +-- .../TypeSystem/ReflectionHelper.cs | 12 +- .../TypeSystem/TopLevelTypeName.cs | 144 ++++++++ 35 files changed, 731 insertions(+), 244 deletions(-) create mode 100644 ICSharpCode.NRefactory/TypeSystem/FullTypeName.cs create mode 100644 ICSharpCode.NRefactory/TypeSystem/TopLevelTypeName.cs diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs index 957477f46d..2d0b66b4de 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs @@ -1336,7 +1336,9 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } } if (this.currentMember != null && !(node is AstType)) { - var def = ctx.CurrentTypeDefinition ?? Compilation.MainAssembly.GetTypeDefinition(currentType); + var def = ctx.CurrentTypeDefinition; + if (def == null && currentType != null) + def = Compilation.MainAssembly.GetTypeDefinition(currentType.FullTypeName); if (def != null) { bool isProtectedAllowed = true; foreach (var member in def.GetMembers ()) { diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/RefactoringContext.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/RefactoringContext.cs index 6b9c6fe82c..578b5a6838 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/RefactoringContext.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/RefactoringContext.cs @@ -60,7 +60,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring public virtual AstType CreateShortType(string ns, string name, int typeParameterCount = 0) { var builder = CreateTypeSytemAstBuilder(); - return builder.ConvertType(ns, name, typeParameterCount); + return builder.ConvertType(new TopLevelTypeName(ns, name, typeParameterCount)); } public virtual IEnumerable GetSelectedNodes() diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/TypeSystemAstBuilder.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/TypeSystemAstBuilder.cs index 78bca9521e..12faf6e8ab 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/TypeSystemAstBuilder.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/TypeSystemAstBuilder.cs @@ -142,17 +142,27 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring return astType; } - public AstType ConvertType(string ns, string name, int typeParameterCount = 0) + public AstType ConvertType(FullTypeName fullTypeName) { if (resolver != null) { foreach (var asm in resolver.Compilation.Assemblies) { - var def = asm.GetTypeDefinition(ns, name, typeParameterCount); + var def = asm.GetTypeDefinition(fullTypeName); if (def != null) { return ConvertType(def); } } } - return new MemberType(new SimpleType(ns), name); + TopLevelTypeName top = fullTypeName.TopLevelTypeName; + AstType type; + if (string.IsNullOrEmpty(top.Namespace)) { + type = new SimpleType(top.Name); + } else { + type = new SimpleType(top.Namespace).MemberType(top.Name); + } + for (int i = 0; i < fullTypeName.NestingLevel; i++) { + type = type.MemberType(fullTypeName.GetNestedTypeName(i)); + } + return type; } AstType ConvertTypeHelper(IType type) @@ -601,7 +611,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring { if (GenerateBody) { return new BlockStatement { - new ThrowStatement(new ObjectCreateExpression(ConvertType("System", "NotImplementedException"))) + new ThrowStatement(new ObjectCreateExpression(ConvertType(new TopLevelTypeName("System", "NotImplementedException", 0)))) }; } else { return BlockStatement.Null; diff --git a/ICSharpCode.NRefactory.CSharp/TypeSystem/AliasNamespaceReference.cs b/ICSharpCode.NRefactory.CSharp/TypeSystem/AliasNamespaceReference.cs index d214103436..03590b8c91 100644 --- a/ICSharpCode.NRefactory.CSharp/TypeSystem/AliasNamespaceReference.cs +++ b/ICSharpCode.NRefactory.CSharp/TypeSystem/AliasNamespaceReference.cs @@ -51,6 +51,12 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem return resolver.ResolveAlias(identifier); } + public override IType ResolveType(CSharpResolver resolver) + { + // alias cannot refer to types + return SpecialType.UnknownType; + } + public override string ToString() { return identifier + "::"; diff --git a/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpAssembly.cs b/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpAssembly.cs index 9b0f15a531..02bbc68f25 100644 --- a/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpAssembly.cs +++ b/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpAssembly.cs @@ -181,9 +181,9 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem return fullAssemblyName.Substring(0, pos); } - Dictionary typeDict; + Dictionary typeDict; - Dictionary GetTypes() + Dictionary GetTypes() { var dict = LazyInit.VolatileRead(ref this.typeDict); if (dict != null) { @@ -192,9 +192,9 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem // Always use the ordinal comparer for the main dictionary so that partial classes // get merged correctly. // The compilation's comparer will be used for the per-namespace dictionaries. - var comparer = FullNameAndTypeParameterCountComparer.Ordinal; + var comparer = TopLevelTypeNameComparer.Ordinal; dict = projectContent.TopLevelTypeDefinitions - .GroupBy(t => new FullNameAndTypeParameterCount(t.Namespace, t.Name, t.TypeParameters.Count), comparer) + .GroupBy(t => new TopLevelTypeName(t.Namespace, t.Name, t.TypeParameters.Count), comparer) .ToDictionary(g => g.Key, g => CreateResolvedTypeDefinition(g.ToArray()), comparer); return LazyInit.GetOrSet(ref this.typeDict, dict); } @@ -205,11 +205,10 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem return new DefaultResolvedTypeDefinition(context, parts); } - public ITypeDefinition GetTypeDefinition(string ns, string name, int typeParameterCount) + public ITypeDefinition GetTypeDefinition(TopLevelTypeName topLevelTypeName) { - var key = new FullNameAndTypeParameterCount(ns ?? string.Empty, name, typeParameterCount); ITypeDefinition def; - if (GetTypes().TryGetValue(key, out def)) + if (GetTypes().TryGetValue(topLevelTypeName, out def)) return def; else return null; @@ -233,7 +232,7 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem readonly string fullName; readonly string name; internal readonly List childNamespaces = new List(); - internal readonly Dictionary types; + internal readonly Dictionary types; public NS(CSharpAssembly assembly) { @@ -243,7 +242,7 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem // Our main dictionary for the CSharpAssembly is using an ordinal comparer. // If the compilation's comparer isn't ordinal, we need to create a new dictionary with the compilation's comparer. if (assembly.compilation.NameComparer != StringComparer.Ordinal) { - this.types = new Dictionary(new FullNameAndTypeParameterCountComparer(assembly.compilation.NameComparer)); + this.types = new Dictionary(new TopLevelTypeNameComparer(assembly.compilation.NameComparer)); } } @@ -254,7 +253,7 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem this.fullName = fullName; this.name = name; if (parentNamespace.types != null) - this.types = new Dictionary(parentNamespace.types.Comparer); + this.types = new Dictionary(parentNamespace.types.Comparer); } string INamespace.ExternAlias { @@ -310,15 +309,15 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem ITypeDefinition INamespace.GetTypeDefinition(string name, int typeParameterCount) { + var key = new TopLevelTypeName(fullName, name, typeParameterCount); if (types != null) { - var key = new FullNameAndTypeParameterCount(fullName, name, typeParameterCount); ITypeDefinition typeDef; if (types.TryGetValue(key, out typeDef)) return typeDef; else return null; } else { - return assembly.GetTypeDefinition(fullName, name, typeParameterCount); + return assembly.GetTypeDefinition(key); } } } diff --git a/ICSharpCode.NRefactory.CSharp/TypeSystem/MemberTypeOrNamespaceReference.cs b/ICSharpCode.NRefactory.CSharp/TypeSystem/MemberTypeOrNamespaceReference.cs index 6956472c59..5ec7eb0f91 100644 --- a/ICSharpCode.NRefactory.CSharp/TypeSystem/MemberTypeOrNamespaceReference.cs +++ b/ICSharpCode.NRefactory.CSharp/TypeSystem/MemberTypeOrNamespaceReference.cs @@ -22,6 +22,7 @@ using System.Collections.ObjectModel; using ICSharpCode.NRefactory.CSharp.Resolver; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; using ICSharpCode.NRefactory.Utils; namespace ICSharpCode.NRefactory.CSharp.TypeSystem @@ -58,7 +59,11 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem } public IList TypeArguments { - get { return new ReadOnlyCollection(typeArguments); } + get { return typeArguments; } + } + + public NameLookupMode LookupMode { + get { return lookupMode; } } /// @@ -67,7 +72,7 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem /// public MemberTypeOrNamespaceReference AddSuffix(string suffix) { - return new MemberTypeOrNamespaceReference(target, identifier + suffix, typeArguments); + return new MemberTypeOrNamespaceReference(target, identifier + suffix, typeArguments, lookupMode); } public override ResolveResult Resolve(CSharpResolver resolver) @@ -86,6 +91,12 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem } } + public override IType ResolveType(CSharpResolver resolver) + { + TypeResolveResult trr = Resolve(resolver) as TypeResolveResult; + return trr != null ? trr.Type : new UnknownType(null, identifier, typeArguments.Count); + } + public override string ToString() { if (typeArguments.Count == 0) diff --git a/ICSharpCode.NRefactory.CSharp/TypeSystem/SimpleTypeOrNamespaceReference.cs b/ICSharpCode.NRefactory.CSharp/TypeSystem/SimpleTypeOrNamespaceReference.cs index d0047ef6e3..a1ebe1b80b 100644 --- a/ICSharpCode.NRefactory.CSharp/TypeSystem/SimpleTypeOrNamespaceReference.cs +++ b/ICSharpCode.NRefactory.CSharp/TypeSystem/SimpleTypeOrNamespaceReference.cs @@ -23,6 +23,7 @@ using System.Collections.ObjectModel; using ICSharpCode.NRefactory.CSharp.Resolver; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.TypeSystem.Implementation; using ICSharpCode.NRefactory.Utils; namespace ICSharpCode.NRefactory.CSharp.TypeSystem @@ -51,7 +52,11 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem } public IList TypeArguments { - get { return new ReadOnlyCollection(typeArguments); } + get { return typeArguments; } + } + + public NameLookupMode LookupMode { + get { return lookupMode; } } /// @@ -69,6 +74,12 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem return resolver.LookupSimpleNameOrTypeName(identifier, typeArgs, lookupMode); } + public override IType ResolveType(CSharpResolver resolver) + { + TypeResolveResult trr = Resolve(resolver) as TypeResolveResult; + return trr != null ? trr.Type : new UnknownType(null, identifier, typeArguments.Count); + } + public override string ToString() { if (typeArguments.Count == 0) diff --git a/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeOrNamespaceReference.cs b/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeOrNamespaceReference.cs index a822b9a66d..ddbcea2cab 100644 --- a/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeOrNamespaceReference.cs +++ b/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeOrNamespaceReference.cs @@ -34,6 +34,11 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem /// public abstract ResolveResult Resolve(CSharpResolver resolver); + /// + /// Returns the type that is referenced; or an if the type isn't found. + /// + public abstract IType ResolveType(CSharpResolver resolver); + /// /// Returns the namespace that is referenced; or null if no such namespace is found. /// @@ -43,15 +48,6 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem return nrr != null ? nrr.Namespace : null; } - /// - /// Returns the type that is referenced; or if the type isn't found. - /// - public IType ResolveType(CSharpResolver resolver) - { - TypeResolveResult trr = Resolve(resolver) as TypeResolveResult; - return trr != null ? trr.Type : SpecialType.UnknownType; - } - IType ITypeReference.Resolve(ITypeResolveContext context) { // Strictly speaking, we might have to resolve the type in a nested compilation, similar diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/TestBase.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/TestBase.cs index 3ee87bfa77..72969a027f 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/TestBase.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/TestBase.cs @@ -1,4 +1,4 @@ -// +// // TestBase.cs // // Author: @@ -38,7 +38,7 @@ using System.Diagnostics; namespace ICSharpCode.NRefactory.CSharp.CodeCompletion { [TestFixture] - public class TestBase + public abstract class TestBase { class TestListener : TraceListener { diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Refactoring/TypeSystemAstBuilderTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Refactoring/TypeSystemAstBuilderTests.cs index 3f639b4c23..82a677cb15 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Refactoring/TypeSystemAstBuilderTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Refactoring/TypeSystemAstBuilderTests.cs @@ -217,7 +217,7 @@ namespace OtherNS { public void AmbiguousType() { Assert.AreEqual("System.Array", TypeToString(compilation.FindType(typeof(Array)))); - Assert.AreEqual("OtherNS.Array", TypeToString(compilation.MainAssembly.GetTypeDefinition("OtherNS", "Array", 0))); + Assert.AreEqual("OtherNS.Array", TypeToString(compilation.MainAssembly.GetTypeDefinition(new TopLevelTypeName("OtherNS", "Array")))); } } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs index 4702395891..d95d91605d 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs @@ -508,8 +508,8 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver ) } ) })); ICompilation compilation = TypeSystemHelper.CreateCompilation(a, b); - ITypeDefinition resolvedA = compilation.MainAssembly.GetTypeDefinition(a); - ITypeDefinition resolvedB = compilation.MainAssembly.GetTypeDefinition(b); + ITypeDefinition resolvedA = compilation.MainAssembly.GetTypeDefinition(a.FullTypeName); + ITypeDefinition resolvedB = compilation.MainAssembly.GetTypeDefinition(b.FullTypeName); IType type1 = new ParameterizedType(resolvedB, new [] { compilation.FindType(KnownTypeCode.Double) }); IType type2 = new ParameterizedType(resolvedA, new [] { new ParameterizedType(resolvedB, new[] { compilation.FindType(KnownTypeCode.String) }) }); diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/MemberLookupTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/MemberLookupTests.cs index 882273873d..83b196f067 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/MemberLookupTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/MemberLookupTests.cs @@ -61,7 +61,7 @@ class Derived : Middle { public override void Method() {} }"; var unresolvedFile = Parse(program); - ITypeDefinition derived = compilation.MainAssembly.GetTypeDefinition(unresolvedFile.TopLevelTypeDefinitions[2]); + ITypeDefinition derived = compilation.MainAssembly.GetTypeDefinition(unresolvedFile.TopLevelTypeDefinitions[2].FullTypeName); var rr = lookup.Lookup(new ResolveResult(derived), "Method", EmptyList.Instance, true) as MethodGroupResolveResult; Assert.AreEqual(2, rr.MethodsGroupedByDeclaringType.Count()); @@ -88,7 +88,7 @@ class Derived : Base { public override void Method(string a) {} }"; var unresolvedFile = Parse(program); - ITypeDefinition derived = compilation.MainAssembly.GetTypeDefinition(unresolvedFile.TopLevelTypeDefinitions[1]); + ITypeDefinition derived = compilation.MainAssembly.GetTypeDefinition(unresolvedFile.TopLevelTypeDefinitions[1].FullTypeName); var rr = lookup.Lookup(new ResolveResult(derived), "Method", EmptyList.Instance, true) as MethodGroupResolveResult; Assert.AreEqual(2, rr.MethodsGroupedByDeclaringType.Count()); @@ -116,7 +116,7 @@ class Derived : Base { public override void Method(S a) {} }"; var unresolvedFile = Parse(program); - ITypeDefinition derived = compilation.MainAssembly.GetTypeDefinition(unresolvedFile.TopLevelTypeDefinitions[1]); + ITypeDefinition derived = compilation.MainAssembly.GetTypeDefinition(unresolvedFile.TopLevelTypeDefinitions[1].FullTypeName); var rr = lookup.Lookup(new ResolveResult(derived), "Method", EmptyList.Instance, true) as MethodGroupResolveResult; Assert.AreEqual(1, rr.MethodsGroupedByDeclaringType.Count()); diff --git a/ICSharpCode.NRefactory.Tests/Documentation/IDStringTests.cs b/ICSharpCode.NRefactory.Tests/Documentation/IDStringTests.cs index 5d30b6e403..9bdbe5c04a 100644 --- a/ICSharpCode.NRefactory.Tests/Documentation/IDStringTests.cs +++ b/ICSharpCode.NRefactory.Tests/Documentation/IDStringTests.cs @@ -59,7 +59,7 @@ namespace ICSharpCode.NRefactory.Documentation ITypeDefinition GetTypeDefinition(string nameSpace, string name, int typeParameterCount = 0) { - return compilation.MainAssembly.GetTypeDefinition(nameSpace, name, typeParameterCount); + return compilation.MainAssembly.GetTypeDefinition(new TopLevelTypeName(nameSpace, name, typeParameterCount)); } [Test] diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/GetAllBaseTypesTest.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/GetAllBaseTypesTest.cs index b53ef9d30f..9200c50840 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/GetAllBaseTypesTest.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/GetAllBaseTypesTest.cs @@ -83,7 +83,7 @@ namespace System.Collections.Generic { ITypeDefinition Resolve(IUnresolvedTypeDefinition typeDef) { - return compilation.MainAssembly.GetTypeDefinition(typeDef); + return typeDef.Resolve(new SimpleTypeResolveContext(compilation.MainAssembly)).GetDefinition(); } [Test] @@ -135,8 +135,7 @@ namespace System.Collections.Generic { // class C : C {} var c = new DefaultUnresolvedTypeDefinition(string.Empty, "C"); c.BaseTypes.Add(c); - compilation = TypeSystemHelper.CreateCompilation(c); - ITypeDefinition resolvedC = Resolve(c); + ITypeDefinition resolvedC = TypeSystemHelper.CreateCompilationAndResolve(c); Assert.AreEqual(new [] { resolvedC }, resolvedC.GetAllBaseTypes().ToArray()); } diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/GetMembersTests.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/GetMembersTests.cs index a35c37883c..6628e2de96 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/GetMembersTests.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/GetMembersTests.cs @@ -31,8 +31,8 @@ namespace ICSharpCode.NRefactory.TypeSystem public void EmptyClassHasToString() { DefaultUnresolvedTypeDefinition c = new DefaultUnresolvedTypeDefinition(string.Empty, "C"); - var compilation = TypeSystemHelper.CreateCompilation(c); - Assert.AreEqual("System.Object.ToString", compilation.MainAssembly.GetTypeDefinition(c).GetMethods(m => m.Name == "ToString").Single().FullName); + var resolvedC = TypeSystemHelper.CreateCompilationAndResolve(c); + Assert.AreEqual("System.Object.ToString", resolvedC.GetMethods(m => m.Name == "ToString").Single().FullName); } [Test] @@ -52,7 +52,7 @@ namespace ICSharpCode.NRefactory.TypeSystem c.BaseTypes.Add(b2); var compilation = TypeSystemHelper.CreateCompilation(b1, b2, c); - ITypeDefinition resolvedC = compilation.MainAssembly.GetTypeDefinition(c); + ITypeDefinition resolvedC = compilation.MainAssembly.GetTypeDefinition(c.FullTypeName); Assert.AreEqual(new[] { "P1", "P2" }, resolvedC.GetProperties().Select(p => p.Name).ToArray()); // Test that there's only one copy of ToString(): Assert.AreEqual(1, resolvedC.GetMethods(m => m.Name == "ToString").Count()); @@ -91,8 +91,8 @@ namespace ICSharpCode.NRefactory.TypeSystem a.NestedTypes.Add(b); var compilation = TypeSystemHelper.CreateCompilation(a, b); - ITypeDefinition resolvedA = compilation.MainAssembly.GetTypeDefinition(a); - ITypeDefinition resolvedB = compilation.MainAssembly.GetTypeDefinition(b); + ITypeDefinition resolvedA = compilation.MainAssembly.GetTypeDefinition(a.FullTypeName); + ITypeDefinition resolvedB = compilation.MainAssembly.GetTypeDefinition(b.FullTypeName); // A<> gets self-parameterized, B<> stays unbound Assert.AreEqual("A`1+B`1[[`0],[]]", resolvedA.GetNestedTypes().Single().ReflectionName); diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeParameterTests.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeParameterTests.cs index 8dad6bb71c..e081eefbd8 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeParameterTests.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeParameterTests.cs @@ -36,7 +36,7 @@ namespace ICSharpCode.NRefactory.TypeSystem Constraints = { new TypeParameterReference(EntityType.TypeDefinition, 0) } }); - ITypeDefinition resolvedC = TypeSystemHelper.CreateCompilation(c).MainAssembly.GetTypeDefinition(c); + ITypeDefinition resolvedC = TypeSystemHelper.CreateCompilationAndResolve(c); // At runtime, we might have T=System.ValueType and U=int, so C# can't inherit the 'class' constraint // from one type parameter to another. Assert.AreEqual(true, resolvedC.TypeParameters[0].IsReferenceType); @@ -54,7 +54,7 @@ namespace ICSharpCode.NRefactory.TypeSystem Constraints = { new TypeParameterReference(EntityType.TypeDefinition, 0) } }); - ITypeDefinition resolvedC = TypeSystemHelper.CreateCompilation(c).MainAssembly.GetTypeDefinition(c); + ITypeDefinition resolvedC = TypeSystemHelper.CreateCompilationAndResolve(c); // At runtime, we might have T=System.ValueType and U=int, so C# can't inherit the 'class' constraint // from one type parameter to another. Assert.AreEqual(true, resolvedC.TypeParameters[0].IsReferenceType); @@ -73,7 +73,7 @@ namespace ICSharpCode.NRefactory.TypeSystem Constraints = { new TypeParameterReference(EntityType.TypeDefinition, 0) } }); - ITypeDefinition resolvedC = TypeSystemHelper.CreateCompilation(c).MainAssembly.GetTypeDefinition(c); + ITypeDefinition resolvedC = TypeSystemHelper.CreateCompilationAndResolve(c); Assert.AreEqual(true, resolvedC.TypeParameters[0].IsReferenceType); Assert.AreEqual(true, resolvedC.TypeParameters[1].IsReferenceType); Assert.AreEqual("System.Collections.Generic.List`1[[System.String]]", resolvedC.TypeParameters[0].EffectiveBaseClass.ReflectionName); diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemHelper.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemHelper.cs index d5c94561e4..f7671aa102 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemHelper.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemHelper.cs @@ -44,5 +44,11 @@ namespace ICSharpCode.NRefactory.TypeSystem pc = pc.AddAssemblyReferences(new [] { CecilLoaderTests.Mscorlib, CecilLoaderTests.SystemCore }); return pc.CreateCompilation(); } + + public static ITypeDefinition CreateCompilationAndResolve(IUnresolvedTypeDefinition unresolvedTypeDefinition) + { + var compilation = CreateCompilation(unresolvedTypeDefinition); + return unresolvedTypeDefinition.Resolve(new SimpleTypeResolveContext(compilation.MainAssembly)).GetDefinition(); + } } } diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs index 1a3538f9a9..40c1d00c59 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs @@ -137,8 +137,13 @@ namespace ICSharpCode.NRefactory.TypeSystem ParameterizedType crt = (ParameterizedType)rt.ReferencedType; Assert.AreEqual("System.Collections.Generic.IDictionary", crt.FullName); Assert.AreEqual("System.String", crt.TypeArguments[0].FullName); - // ? for NUnit.TestAttribute (because that assembly isn't in ctx) - Assert.AreEqual("System.Collections.Generic.IList`1[[?]]", crt.TypeArguments[1].ReflectionName); + // we know the name for TestAttribute, but not necessarily the namespace, as NUnit is not in the compilation + Assert.AreEqual("System.Collections.Generic.IList", crt.TypeArguments[1].FullName); + var testAttributeType = ((ParameterizedType)crt.TypeArguments[1]).TypeArguments.Single(); + Assert.AreEqual("TestAttribute", testAttributeType.Name); + Assert.AreEqual(TypeKind.Unknown, testAttributeType.Kind); + // (more accurately, we know the namespace and reflection name if the type was loaded by cecil, + // but not if we parsed it from C#) } [Test] diff --git a/ICSharpCode.NRefactory/Documentation/GetPotentiallyNestedClassTypeReference.cs b/ICSharpCode.NRefactory/Documentation/GetPotentiallyNestedClassTypeReference.cs index 0e5e7404c8..bb6cfd6bc3 100644 --- a/ICSharpCode.NRefactory/Documentation/GetPotentiallyNestedClassTypeReference.cs +++ b/ICSharpCode.NRefactory/Documentation/GetPotentiallyNestedClassTypeReference.cs @@ -51,7 +51,7 @@ namespace ICSharpCode.NRefactory.Documentation foreach (var asm in assemblies) { if (asm == null) continue; - ITypeDefinition typeDef = asm.GetTypeDefinition(ns, name, topLevelTPC); + ITypeDefinition typeDef = asm.GetTypeDefinition(new TopLevelTypeName(ns, name, topLevelTPC)); for (int j = i + 1; j < parts.Length && typeDef != null; j++) { int tpc = (j == parts.Length - 1 ? typeParameterCount : 0); typeDef = typeDef.NestedTypes.FirstOrDefault(n => n.Name == parts[j] && n.TypeParameterCount == tpc); diff --git a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj index 9b8e85e343..d20a2aceac 100644 --- a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj +++ b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj @@ -138,6 +138,7 @@ + @@ -223,6 +224,7 @@ + @@ -284,4 +286,4 @@ - + \ No newline at end of file diff --git a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs index 67099a30e7..32c15308ec 100644 --- a/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs +++ b/ICSharpCode.NRefactory/TypeSystem/CecilLoader.cs @@ -190,7 +190,7 @@ namespace ICSharpCode.NRefactory.TypeSystem name = interningProvider.Intern(name); var typeRef = new GetClassTypeReference(GetAssemblyReference(type.Scope), ns, name, typeParameterCount); typeRef = interningProvider.Intern(typeRef); - var key = new FullNameAndTypeParameterCount(ns, name, typeParameterCount); + var key = new TopLevelTypeName(ns, name, typeParameterCount); currentAssembly.AddTypeForwarder(key, typeRef); } } @@ -1793,6 +1793,12 @@ namespace ICSharpCode.NRefactory.TypeSystem get { return cecilTypeDef.FullName; } } + public FullTypeName FullTypeName { + get { + return new TopLevelTypeName(namespaceName, this.Name, typeParameters.Count); + } + } + public TypeKind Kind { get { return kind; } } @@ -1884,7 +1890,7 @@ namespace ICSharpCode.NRefactory.TypeSystem throw new ArgumentNullException("context"); if (context.CurrentAssembly == null) throw new ArgumentException("An ITypeDefinition cannot be resolved in a context without a current assembly."); - return context.CurrentAssembly.GetTypeDefinition(this) + return context.CurrentAssembly.GetTypeDefinition(this.FullTypeName) ?? (IType)new UnknownType(this.Namespace, this.Name, this.TypeParameters.Count); } diff --git a/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs b/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs index f15885817c..52f7ec258a 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs @@ -392,28 +392,59 @@ namespace ICSharpCode.NRefactory.TypeSystem #endregion #region IAssembly.GetTypeDefinition() + /// + /// Retrieves the specified type in this compilation. + /// Returns an if the type cannot be found in this compilation. + /// + /// + /// There can be multiple types with the same full name in a compilation, as a + /// full type name is only unique per assembly. + /// If there are multiple possible matches, this method will return just one of them. + /// When possible, use instead to + /// retrieve a type from a specific assembly. + /// + public static IType FindType(this ICompilation compilation, FullTypeName fullTypeName) + { + if (compilation == null) + throw new ArgumentNullException("compilation"); + foreach (IAssembly asm in compilation.Assemblies) { + ITypeDefinition def = asm.GetTypeDefinition(fullTypeName); + if (def != null) + return def; + } + return new UnknownType(fullTypeName); + } + /// /// Gets the type definition for the specified unresolved type. /// Returns null if the unresolved type does not belong to this assembly. /// - public static ITypeDefinition GetTypeDefinition(this IAssembly assembly, IUnresolvedTypeDefinition unresolved) + public static ITypeDefinition GetTypeDefinition(this IAssembly assembly, FullTypeName fullTypeName) { if (assembly == null) throw new ArgumentNullException("assembly"); - if (unresolved == null) + TopLevelTypeName topLevelTypeName = fullTypeName.TopLevelTypeName; + ITypeDefinition typeDef = assembly.GetTypeDefinition(topLevelTypeName); + if (typeDef == null) return null; - if (unresolved.DeclaringTypeDefinition != null) { - ITypeDefinition parentType = GetTypeDefinition(assembly, unresolved.DeclaringTypeDefinition); - if (parentType == null) - return null; - foreach (var nestedType in parentType.NestedTypes) { - if (nestedType.Name == unresolved.Name && nestedType.TypeParameterCount == unresolved.TypeParameters.Count) - return nestedType; - } - return null; - } else { - return assembly.GetTypeDefinition(unresolved.Namespace, unresolved.Name, unresolved.TypeParameters.Count); + int typeParameterCount = topLevelTypeName.TypeParameterCount; + for (int i = 0; i < fullTypeName.NestingLevel; i++) { + string name = fullTypeName.GetNestedTypeName(i); + typeParameterCount += fullTypeName.GetNestedTypeAdditionalTypeParameterCount(i); + typeDef = FindNestedType(typeDef, name, typeParameterCount); + if (typeDef == null) + break; + } + return typeDef; + } + + static ITypeDefinition FindNestedType(ITypeDefinition typeDef, string name, int typeParameterCount) + { + foreach (var nestedType in typeDef.NestedTypes) { + if (nestedType.Name == name && nestedType.TypeParameterCount == typeParameterCount) + return nestedType; } + return null; } #endregion diff --git a/ICSharpCode.NRefactory/TypeSystem/FullTypeName.cs b/ICSharpCode.NRefactory/TypeSystem/FullTypeName.cs new file mode 100644 index 0000000000..dd76eb22a5 --- /dev/null +++ b/ICSharpCode.NRefactory/TypeSystem/FullTypeName.cs @@ -0,0 +1,312 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace ICSharpCode.NRefactory.TypeSystem +{ + /// + /// Holds the full name of a type definition. + /// A full type name uniquely identifies a type definition within a single assembly. + /// + /// + /// A full type name can only represent type definitions, not arbitrary types. + /// It does not include any type arguments, and can not refer to array or pointer types. + /// + /// + /// + [Serializable] + public struct FullTypeName : IEquatable + { + [Serializable] + struct NestedTypeName + { + public readonly string Name; + public readonly int AdditionalTypeParameterCount; + + public NestedTypeName(string name, int additionalTypeParameterCount) + { + if (name == null) + throw new ArgumentNullException("name"); + this.Name = name; + this.AdditionalTypeParameterCount = additionalTypeParameterCount; + } + } + + readonly TopLevelTypeName topLevelType; + readonly NestedTypeName[] nestedTypes; + + FullTypeName(TopLevelTypeName topLevelTypeName, NestedTypeName[] nestedTypes) + { + this.topLevelType = topLevelTypeName; + this.nestedTypes = nestedTypes; + } + + /// + /// Constructs a FullTypeName representing the given top-level type. + /// + /// + /// FullTypeName has an implicit conversion operator from TopLevelTypeName, + /// so you can simply write: + /// FullTypeName f = new TopLevelTypeName(...); + /// + public FullTypeName(TopLevelTypeName topLevelTypeName) + { + this.topLevelType = topLevelTypeName; + this.nestedTypes = null; + } + + /// + /// Constructs a FullTypeName by parsing the given reflection name. + /// Note that FullTypeName can only represent type definition names. If the reflection name + /// might refer to a parameterized type or array etc., use + /// instead. + /// + /// + /// Expected syntax: NamespaceName '.' TopLevelTypeName ['`'#] { '+' NestedTypeName ['`'#] } + /// where # are type parameter counts + /// + public FullTypeName(string reflectionName) + { + int pos = reflectionName.IndexOf('+'); + if (pos < 0) { + // top-level type + this.topLevelType = new TopLevelTypeName(reflectionName); + this.nestedTypes = null; + } else { + // nested type + string[] parts = reflectionName.Split('+'); + this.topLevelType = new TopLevelTypeName(parts[0]); + this.nestedTypes = new NestedTypeName[parts.Length - 1]; + for (int i = 0; i < nestedTypes.Length; i++) { + int tpc; + string name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(parts[i + 1], out tpc); + nestedTypes[i] = new NestedTypeName(name, tpc); + } + } + } + + /// + /// Gets the top-level type name. + /// + public TopLevelTypeName TopLevelTypeName { + get { return topLevelType; } + } + + /// + /// Gets whether this is a nested type. + /// + public bool IsNested { + get { + return nestedTypes != null; + } + } + + /// + /// Gets the nesting level. + /// + public int NestingLevel { + get { + return nestedTypes != null ? nestedTypes.Length : 0; + } + } + + /// + /// Gets the name of the type. + /// For nested types, this is the name of the innermost type. + /// + public string Name { + get { + if (nestedTypes != null) + return nestedTypes[nestedTypes.Length - 1].Name; + else + return topLevelType.Name; + } + } + + public string ReflectionName { + get { + if (nestedTypes == null) + return topLevelType.ReflectionName; + StringBuilder b = new StringBuilder(topLevelType.ReflectionName); + foreach (NestedTypeName nt in nestedTypes) { + b.Append('+'); + b.Append(nt.Name); + if (nt.AdditionalTypeParameterCount > 0) { + b.Append('`'); + b.Append(nt.AdditionalTypeParameterCount); + } + } + return b.ToString(); + } + } + + /// + /// Gets the total type parameter count. + /// + public int TypeParameterCount { + get { + int tpc = topLevelType.TypeParameterCount; + if (nestedTypes != null) { + foreach (var nt in nestedTypes) { + tpc += nt.AdditionalTypeParameterCount; + } + } + return tpc; + } + } + + /// + /// Gets the name of the nested type at the given level. + /// + public string GetNestedTypeName(int nestingLevel) + { + if (nestedTypes == null) + throw new InvalidOperationException(); + return nestedTypes[nestingLevel].Name; + } + + /// + /// Gets the number of additional type parameters of the nested type at the given level. + /// + public int GetNestedTypeAdditionalTypeParameterCount(int nestingLevel) + { + if (nestedTypes == null) + throw new InvalidOperationException(); + return nestedTypes[nestingLevel].AdditionalTypeParameterCount; + } + + /// + /// Gets the declaring type name. + /// + /// This is a top-level type name. + public FullTypeName GetDeclaringType() + { + if (nestedTypes == null) + throw new InvalidOperationException(); + if (nestedTypes.Length == 1) + return topLevelType; + NestedTypeName[] outerNestedTypeNames = new NestedTypeName[nestedTypes.Length - 1]; + Array.Copy(nestedTypes, 0, outerNestedTypeNames, 0, outerNestedTypeNames.Length); + return new FullTypeName(topLevelType, nestedTypes); + } + + /// + /// Gets a nested type name. + /// + public FullTypeName NestedType(string name, int additionalTypeParameterCount) + { + if (name == null) + throw new ArgumentNullException("name"); + var newNestedType = new NestedTypeName(name, additionalTypeParameterCount); + if (nestedTypes == null) + return new FullTypeName(topLevelType, new[] { newNestedType }); + NestedTypeName[] newNestedTypeNames = new NestedTypeName[nestedTypes.Length + 1]; + nestedTypes.CopyTo(newNestedTypeNames, 0); + newNestedTypeNames[newNestedTypeNames.Length - 1] = newNestedType; + return new FullTypeName(topLevelType, newNestedTypeNames); + } + + public static implicit operator FullTypeName(TopLevelTypeName topLevelTypeName) + { + return new FullTypeName(topLevelTypeName); + } + + public override string ToString() + { + return this.ReflectionName; + } + + #region Equals and GetHashCode implementation + public override bool Equals(object obj) + { + return obj is FullTypeName && Equals((FullTypeName)obj); + } + + public bool Equals(FullTypeName other) + { + return FullTypeNameComparer.Ordinal.Equals(this, other); + } + + public override int GetHashCode() + { + return FullTypeNameComparer.Ordinal.GetHashCode(this); + } + + public static bool operator ==(FullTypeName left, FullTypeName right) + { + return left.Equals(right); + } + + public static bool operator !=(FullTypeName left, FullTypeName right) + { + return !left.Equals(right); + } + #endregion + } + + [Serializable] + public sealed class FullTypeNameComparer : IEqualityComparer + { + public static readonly FullTypeNameComparer Ordinal = new FullTypeNameComparer(StringComparer.Ordinal); + public static readonly FullTypeNameComparer OrdinalIgnoreCase = new FullTypeNameComparer(StringComparer.OrdinalIgnoreCase); + + public readonly StringComparer NameComparer; + + public FullTypeNameComparer(StringComparer nameComparer) + { + this.NameComparer = nameComparer; + } + + public bool Equals(FullTypeName x, FullTypeName y) + { + if (x.NestingLevel != y.NestingLevel) + return false; + TopLevelTypeName topX = x.TopLevelTypeName; + TopLevelTypeName topY = y.TopLevelTypeName; + if (topX.TypeParameterCount == topY.TypeParameterCount + && NameComparer.Equals(topX.Name, topY.Name) + && NameComparer.Equals(topX.Namespace, topY.Namespace)) + { + for (int i = 0; i < x.NestingLevel; i++) { + if (x.GetNestedTypeAdditionalTypeParameterCount(i) != y.GetNestedTypeAdditionalTypeParameterCount(i)) + return false; + if (!NameComparer.Equals(x.GetNestedTypeName(i), y.GetNestedTypeName(i))) + return false; + } + return true; + } + return false; + } + + public int GetHashCode(FullTypeName obj) + { + TopLevelTypeName top = obj.TopLevelTypeName; + int hash = NameComparer.GetHashCode(top.Name) ^ NameComparer.GetHashCode(top.Namespace) ^ top.TypeParameterCount; + unchecked { + for (int i = 0; i < obj.NestingLevel; i++) { + hash *= 31; + hash += NameComparer.GetHashCode(obj.Name) ^ obj.TypeParameterCount; + } + } + return hash; + } + } +} diff --git a/ICSharpCode.NRefactory/TypeSystem/IAssembly.cs b/ICSharpCode.NRefactory/TypeSystem/IAssembly.cs index c93484300a..1c31e69942 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IAssembly.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IAssembly.cs @@ -115,7 +115,7 @@ namespace ICSharpCode.NRefactory.TypeSystem /// Gets the type definition for a top-level type. /// /// This method uses ordinal name comparison, not the compilation's name comparer. - ITypeDefinition GetTypeDefinition(string ns, string name, int typeParameterCount); + ITypeDefinition GetTypeDefinition(TopLevelTypeName topLevelTypeName); /// /// Gets all non-nested types in the assembly. diff --git a/ICSharpCode.NRefactory/TypeSystem/ICompilation.cs b/ICSharpCode.NRefactory/TypeSystem/ICompilation.cs index 478776aa30..44e4647ec6 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ICompilation.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ICompilation.cs @@ -37,6 +37,9 @@ namespace ICSharpCode.NRefactory.TypeSystem /// /// Gets the list of all assemblies in the compilation. /// + /// + /// This main assembly is the first entry in the list. + /// IList Assemblies { get; } /// diff --git a/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs b/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs index 7cd98c8d23..ccc627f45e 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ITypeDefinition.cs @@ -30,6 +30,7 @@ namespace ICSharpCode.NRefactory.TypeSystem { TypeKind Kind { get; } + FullTypeName FullTypeName { get; } IList BaseTypes { get; } IList TypeParameters { get; } @@ -119,6 +120,11 @@ namespace ICSharpCode.NRefactory.TypeSystem /// IType EnumUnderlyingType { get; } + /// + /// Gets the full name of this type. + /// + FullTypeName FullTypeName { get; } + /// /// Gets/Sets the declaring type (incl. type arguments, if any). /// This property never returns null -- for top-level entities, it returns SharedTypes.UnknownType. diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs index af7df8f1c5..b6cd1e09ce 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultResolvedTypeDefinition.cs @@ -566,6 +566,10 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation get { return parts[0].Namespace; } } + public FullTypeName FullTypeName { + get { return parts[0].FullTypeName; } + } + public DomRegion Region { get { return parts[0].Region; } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs index 1111ef638f..2016ee1f30 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs @@ -39,8 +39,8 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation string fullAssemblyName; IList assemblyAttributes; IList moduleAttributes; - Dictionary typeDefinitions = new Dictionary(FullNameAndTypeParameterCountComparer.Ordinal); - Dictionary typeForwarders = new Dictionary(FullNameAndTypeParameterCountComparer.Ordinal); + Dictionary typeDefinitions = new Dictionary(TopLevelTypeNameComparer.Ordinal); + Dictionary typeForwarders = new Dictionary(TopLevelTypeNameComparer.Ordinal); protected override void FreezeInternal() { @@ -144,7 +144,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation if (typeDefinition.DeclaringTypeDefinition != null) throw new ArgumentException("Cannot add nested types."); FreezableHelper.ThrowIfFrozen(this); - var key = new FullNameAndTypeParameterCount(typeDefinition.Namespace, typeDefinition.Name, typeDefinition.TypeParameters.Count); + var key = new TopLevelTypeName(typeDefinition.Namespace, typeDefinition.Name, typeDefinition.TypeParameters.Count); typeDefinitions.Add(key, typeDefinition); } @@ -157,7 +157,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation /// /// The name of the type. /// The reference used to look up the type in the target assembly. - public void AddTypeForwarder(FullNameAndTypeParameterCount typeName, ITypeReference referencedType) + public void AddTypeForwarder(TopLevelTypeName typeName, ITypeReference referencedType) { if (referencedType == null) throw new ArgumentNullException("referencedType"); @@ -187,7 +187,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation public IUnresolvedTypeDefinition GetTypeDefinition(string ns, string name, int typeParameterCount) { - var key = new FullNameAndTypeParameterCount(ns ?? string.Empty, name, typeParameterCount); + var key = new TopLevelTypeName(ns ?? string.Empty, name, typeParameterCount); IUnresolvedTypeDefinition td; if (typeDefinitions.TryGetValue(key, out td)) return td; @@ -216,9 +216,9 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } //[NonSerialized] - //List> cachedTypeDictionariesPerNameComparer; + //List> cachedTypeDictionariesPerNameComparer; - Dictionary GetTypeDictionary(StringComparer nameComparer) + Dictionary GetTypeDictionary(StringComparer nameComparer) { Debug.Assert(IsFrozen); if (nameComparer == StringComparer.Ordinal) @@ -291,7 +291,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation readonly DefaultUnresolvedAssembly unresolvedAssembly; readonly ICompilation compilation; readonly ITypeResolveContext context; - readonly Dictionary unresolvedTypeDict; + readonly Dictionary unresolvedTypeDict; readonly ConcurrentDictionary typeDict = new ConcurrentDictionary(); readonly INamespace rootNamespace; @@ -338,15 +338,13 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return assembly == this; } - public ITypeDefinition GetTypeDefinition(string ns, string name, int typeParameterCount) + public ITypeDefinition GetTypeDefinition(TopLevelTypeName topLevelTypeName) { - var key = new FullNameAndTypeParameterCount(ns ?? string.Empty, name, typeParameterCount); - IUnresolvedTypeDefinition td; ITypeReference typeRef; - if (unresolvedAssembly.typeDefinitions.TryGetValue(key, out td)) + if (unresolvedAssembly.typeDefinitions.TryGetValue(topLevelTypeName, out td)) return GetTypeDefinition(td); - else if (unresolvedAssembly.typeForwarders.TryGetValue(key, out typeRef)) + else if (unresolvedAssembly.typeForwarders.TryGetValue(topLevelTypeName, out typeRef)) return typeRef.Resolve(compilation.TypeResolveContext).GetDefinition(); else return null; @@ -453,7 +451,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation ITypeDefinition INamespace.GetTypeDefinition(string name, int typeParameterCount) { - var key = new FullNameAndTypeParameterCount(ns.FullName, name, typeParameterCount); + var key = new TopLevelTypeName(ns.FullName, name, typeParameterCount); IUnresolvedTypeDefinition unresolvedTypeDef; if (assembly.unresolvedTypeDict.TryGetValue(key, out unresolvedTypeDef)) return assembly.GetTypeDefinition(unresolvedTypeDef); diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedTypeDefinition.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedTypeDefinition.cs index 39c99cb4c9..42e7cefb03 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedTypeDefinition.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedTypeDefinition.cs @@ -141,24 +141,18 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } public override string ReflectionName { + get { + return this.FullTypeName.ReflectionName; + } + } + + public FullTypeName FullTypeName { get { IUnresolvedTypeDefinition declaringTypeDef = this.DeclaringTypeDefinition; if (declaringTypeDef != null) { - if (this.TypeParameters.Count > declaringTypeDef.TypeParameters.Count) { - return declaringTypeDef.ReflectionName + "+" + this.Name + "`" + (this.TypeParameters.Count - declaringTypeDef.TypeParameters.Count).ToString(CultureInfo.InvariantCulture); - } else { - return declaringTypeDef.ReflectionName + "+" + this.Name; - } - } else if (string.IsNullOrEmpty(namespaceName)) { - if (this.TypeParameters.Count > 0) - return this.Name + "`" + this.TypeParameters.Count.ToString(CultureInfo.InvariantCulture); - else - return this.Name; + return declaringTypeDef.FullTypeName.NestedType(this.Name, this.TypeParameters.Count - declaringTypeDef.TypeParameters.Count); } else { - if (this.TypeParameters.Count > 0) - return namespaceName + "." + this.Name + "`" + this.TypeParameters.Count.ToString(CultureInfo.InvariantCulture); - else - return namespaceName + "." + this.Name; + return new TopLevelTypeName(namespaceName, this.Name, this.TypeParameters.Count); } } } @@ -226,7 +220,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation throw new ArgumentNullException("context"); if (context.CurrentAssembly == null) throw new ArgumentException("An ITypeDefinition cannot be resolved in a context without a current assembly."); - return context.CurrentAssembly.GetTypeDefinition(this) + return context.CurrentAssembly.GetTypeDefinition(this.FullTypeName) ?? (IType)new UnknownType(this.Namespace, this.Name, this.TypeParameters.Count); } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/FullNameAndTypeParameterCount.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/FullNameAndTypeParameterCount.cs index 1057d9403f..6180328bf0 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/FullNameAndTypeParameterCount.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/FullNameAndTypeParameterCount.cs @@ -21,73 +21,8 @@ using System.Collections.Generic; namespace ICSharpCode.NRefactory.TypeSystem.Implementation { - [Serializable] - public struct FullNameAndTypeParameterCount : IEquatable + [Obsolete("This struct was renamed to 'TopLevelTypeName'.", true)] + public struct FullNameAndTypeParameterCount { - public readonly string Namespace; - public readonly string Name; - public readonly int TypeParameterCount; - - public FullNameAndTypeParameterCount(string nameSpace, string name, int typeParameterCount) - { - if (nameSpace == null) - throw new ArgumentNullException("nameSpace"); - if (name == null) - throw new ArgumentNullException("name"); - this.Namespace = nameSpace; - this.Name = name; - this.TypeParameterCount = typeParameterCount; - } - - public override bool Equals(object obj) - { - return (obj is FullNameAndTypeParameterCount) && Equals((FullNameAndTypeParameterCount)obj); - } - - public bool Equals(FullNameAndTypeParameterCount other) - { - return this.Namespace == other.Namespace && this.Name == other.Name && this.TypeParameterCount == other.TypeParameterCount; - } - - public override int GetHashCode() - { - return Name.GetHashCode() ^ Namespace.GetHashCode() ^ TypeParameterCount; - } - - public static bool operator ==(FullNameAndTypeParameterCount lhs, FullNameAndTypeParameterCount rhs) - { - return lhs.Equals(rhs); - } - - public static bool operator !=(FullNameAndTypeParameterCount lhs, FullNameAndTypeParameterCount rhs) - { - return !lhs.Equals(rhs); - } - } - - [Serializable] - public sealed class FullNameAndTypeParameterCountComparer : IEqualityComparer - { - public static readonly FullNameAndTypeParameterCountComparer Ordinal = new FullNameAndTypeParameterCountComparer(StringComparer.Ordinal); - public static readonly FullNameAndTypeParameterCountComparer OrdinalIgnoreCase = new FullNameAndTypeParameterCountComparer(StringComparer.OrdinalIgnoreCase); - - public readonly StringComparer NameComparer; - - public FullNameAndTypeParameterCountComparer(StringComparer nameComparer) - { - this.NameComparer = nameComparer; - } - - public bool Equals(FullNameAndTypeParameterCount x, FullNameAndTypeParameterCount y) - { - return x.TypeParameterCount == y.TypeParameterCount - && NameComparer.Equals(x.Name, y.Name) - && NameComparer.Equals(x.Namespace, y.Namespace); - } - - public int GetHashCode(FullNameAndTypeParameterCount obj) - { - return NameComparer.GetHashCode(obj.Name) ^ NameComparer.GetHashCode(obj.Namespace) ^ obj.TypeParameterCount; - } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/GetClassTypeReference.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/GetClassTypeReference.cs index e561b71acf..527eac9972 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/GetClassTypeReference.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/GetClassTypeReference.cs @@ -28,24 +28,31 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation public sealed class GetClassTypeReference : ITypeReference, ISupportsInterning { readonly IAssemblyReference assembly; - readonly string nameSpace, name; - readonly int typeParameterCount; + readonly FullTypeName fullTypeName; /// - /// Creates a new GetClassTypeReference that searches a top-level type. + /// Creates a new GetClassTypeReference that searches a type definition. /// - /// The namespace name containing the type, e.g. "System.Collections.Generic". + /// The full name of the type. + /// A reference to the assembly containing this type. + /// If this parameter is null, the GetClassTypeReference will search in all + /// assemblies belonging to the compilation. + /// + public GetClassTypeReference(FullTypeName fullTypeName, IAssemblyReference assembly = null) + { + this.fullTypeName = fullTypeName; + this.assembly = assembly; + } + + /// + /// Creates a new GetClassTypeReference that searches a top-level type in all assemblies. + /// + /// The namespace name containing the type, e.g. "System.Collections.Generic". /// The name of the type, e.g. "List". /// The number of type parameters, (e.g. 1 for List<T>). - public GetClassTypeReference(string nameSpace, string name, int typeParameterCount = 0) + public GetClassTypeReference(string namespaceName, string name, int typeParameterCount = 0) { - if (nameSpace == null) - throw new ArgumentNullException("nameSpace"); - if (name == null) - throw new ArgumentNullException("name"); - this.nameSpace = nameSpace; - this.name = name; - this.typeParameterCount = typeParameterCount; + this.fullTypeName = new TopLevelTypeName(namespaceName, name, typeParameterCount); } /// @@ -53,25 +60,33 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation /// /// A reference to the assembly containing this type. /// If this parameter is null, the GetClassTypeReference will search in all assemblies belonging to the ICompilation. - /// The namespace name containing the type, e.g. "System.Collections.Generic". + /// The namespace name containing the type, e.g. "System.Collections.Generic". /// The name of the type, e.g. "List". /// The number of type parameters, (e.g. 1 for List<T>). - public GetClassTypeReference(IAssemblyReference assembly, string nameSpace, string name, int typeParameterCount = 0) + public GetClassTypeReference(IAssemblyReference assembly, string namespaceName, string name, int typeParameterCount = 0) { - if (nameSpace == null) - throw new ArgumentNullException("nameSpace"); - if (name == null) - throw new ArgumentNullException("name"); this.assembly = assembly; - this.nameSpace = nameSpace; - this.name = name; - this.typeParameterCount = typeParameterCount; + this.fullTypeName = new TopLevelTypeName(namespaceName, name, typeParameterCount); } + /// + /// Gets the assembly reference. + /// This property returns null if the GetClassTypeReference is searching in all assemblies + /// of the compilation. + /// public IAssemblyReference Assembly { get { return assembly; } } - public string Namespace { get { return nameSpace; } } - public string Name { get { return name; } } - public int TypeParameterCount { get { return typeParameterCount; } } + + /// + /// Gets the full name of the type this reference is searching for. + /// + public FullTypeName FullTypeName { get { return fullTypeName; } } + + [Obsolete("Use the FullTypeName property instead. GetClassTypeReference now supports nested types, where the Namespace/Name/TPC tripel isn't sufficient for identifying the type.")] + public string Namespace { get { return fullTypeName.TopLevelTypeName.Namespace; } } + [Obsolete("Use the FullTypeName property instead. GetClassTypeReference now supports nested types, where the Namespace/Name/TPC tripel isn't sufficient for identifying the type.")] + public string Name { get { return fullTypeName.Name; } } + [Obsolete("Use the FullTypeName property instead. GetClassTypeReference now supports nested types, where the Namespace/Name/TPC tripel isn't sufficient for identifying the type.")] + public int TypeParameterCount { get { return fullTypeName.TypeParameterCount; } } public IType Resolve(ITypeResolveContext context) { @@ -80,52 +95,42 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation IType type = null; if (assembly == null) { - var compilation = context.Compilation; - foreach (var asm in new[] { context.CurrentAssembly }.Concat(compilation.Assemblies)) { - if (asm != null) { - type = asm.GetTypeDefinition(nameSpace, name, typeParameterCount); + if (context.CurrentAssembly != null) { + type = context.CurrentAssembly.GetTypeDefinition(fullTypeName); + } + if (type == null) { + var compilation = context.Compilation; + foreach (var asm in new[] { context.CurrentAssembly }.Concat(compilation.Assemblies)) { + type = asm.GetTypeDefinition(fullTypeName); if (type != null) - return type; + break; } } } else { IAssembly asm = assembly.Resolve(context); if (asm != null) { - type = asm.GetTypeDefinition(nameSpace, name, typeParameterCount); + type = asm.GetTypeDefinition(fullTypeName); } } - return type ?? new UnknownType(nameSpace, name, typeParameterCount); + return type ?? new UnknownType(fullTypeName); } public override string ToString() { - string asmSuffix = (assembly != null ? ", " + assembly.ToString() : null); - if (typeParameterCount == 0) - return BuildQualifiedName(nameSpace, name) + asmSuffix; - else - return BuildQualifiedName(nameSpace, name) + "`" + typeParameterCount + asmSuffix; - } - - static string BuildQualifiedName (string name1, string name2) - { - if (string.IsNullOrEmpty (name1)) - return name2; - if (string.IsNullOrEmpty (name2)) - return name1; - return name1 + "." + name2; + return fullTypeName.ToString() + (assembly != null ? ", " + assembly.ToString() : null); } int ISupportsInterning.GetHashCodeForInterning() { unchecked { - return 33 * assembly.GetHashCode() + 27 * nameSpace.GetHashCode() + name.GetHashCode() + typeParameterCount; + return 33 * assembly.GetHashCode() + fullTypeName.GetHashCode(); } } bool ISupportsInterning.EqualsForInterning(ISupportsInterning other) { GetClassTypeReference o = other as GetClassTypeReference; - return o != null && assembly == o.assembly && name == o.name && nameSpace == o.nameSpace && typeParameterCount == o.typeParameterCount; + return o != null && assembly == o.assembly && fullTypeName == o.fullTypeName; } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/KnownTypeCache.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/KnownTypeCache.cs index f17c4f2664..adcd1a8e34 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/KnownTypeCache.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/KnownTypeCache.cs @@ -49,7 +49,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation if (typeRef == null) return SpecialType.UnknownType; foreach (IAssembly asm in compilation.Assemblies) { - var typeDef = asm.GetTypeDefinition(typeRef.Namespace, typeRef.Name, typeRef.TypeParameterCount); + var typeDef = asm.GetTypeDefinition(new TopLevelTypeName(typeRef.Namespace, typeRef.Name, typeRef.TypeParameterCount)); if (typeDef != null) return typeDef; } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/UnknownType.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/UnknownType.cs index dc915306f3..3ae0b3d42b 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/UnknownType.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/UnknownType.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Diagnostics; namespace ICSharpCode.NRefactory.TypeSystem.Implementation { @@ -26,9 +27,8 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation [Serializable] public class UnknownType : AbstractType, ITypeReference { - readonly string namespaceName; - readonly string name; - readonly int typeParameterCount; + readonly bool namespaceKnown; + readonly FullTypeName fullTypeName; /// /// Creates a new unknown type. @@ -40,9 +40,24 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation { if (name == null) throw new ArgumentNullException("name"); - this.namespaceName = namespaceName; - this.name = name; - this.typeParameterCount = typeParameterCount; + this.namespaceKnown = namespaceName != null; + this.fullTypeName = new TopLevelTypeName(namespaceName ?? string.Empty, name, typeParameterCount); + } + + /// + /// Creates a new unknown type. + /// + /// Full name of the unknown type. + public UnknownType(FullTypeName fullTypeName) + { + if (fullTypeName.Name == null) { + Debug.Assert(fullTypeName == default(FullTypeName)); + this.namespaceKnown = false; + this.fullTypeName = new TopLevelTypeName(string.Empty, "?", 0); + } else { + this.namespaceKnown = true; + this.fullTypeName = fullTypeName; + } } public override TypeKind Kind { @@ -62,15 +77,19 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } public override string Name { - get { return name; } + get { return fullTypeName.Name; } } public override string Namespace { - get { return namespaceName ?? string.Empty; } + get { return fullTypeName.TopLevelTypeName.Namespace; } } public override string ReflectionName { - get { return "?"; } + get { return namespaceKnown ? fullTypeName.ReflectionName : "?"; } + } + + public override int TypeParameterCount { + get { return fullTypeName.TypeParameterCount; } } public override bool? IsReferenceType { @@ -79,14 +98,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation public override int GetHashCode() { - int hashCode = 0; - unchecked { - if (namespaceName != null) - hashCode += 1000000007 * namespaceName.GetHashCode(); - hashCode += 1000000009 * name.GetHashCode(); - hashCode += 1000000021 * typeParameterCount.GetHashCode(); - } - return hashCode; + return (namespaceKnown ? 812571 : 12651) ^ fullTypeName.GetHashCode(); } public override bool Equals(IType other) @@ -94,12 +106,12 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation UnknownType o = other as UnknownType; if (o == null) return false; - return this.namespaceName == o.namespaceName && this.name == o.name && this.typeParameterCount == o.typeParameterCount; + return this.namespaceKnown == o.namespaceKnown && this.fullTypeName == o.fullTypeName; } public override string ToString() { - return "[UnknownType " + this.FullName + "]"; + return "[UnknownType " + fullTypeName.ReflectionName + "]"; } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/ReflectionHelper.cs b/ICSharpCode.NRefactory/TypeSystem/ReflectionHelper.cs index 142b715990..cee12ff034 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ReflectionHelper.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ReflectionHelper.cs @@ -56,17 +56,6 @@ namespace ICSharpCode.NRefactory.TypeSystem { return type.ToTypeReference().Resolve(compilation.TypeResolveContext); } - - /// - /// Retrieves the specified type in this compilation. - /// Returns if the type cannot be found in this compilation. - /// - [Obsolete("Use ReflectionHelper.ParseReflectionName(reflectionTypeName).Resolve(compilation.TypeResolveContext) instead. " + - "Make sure to read the ParseReflectionName() documentation for caveats.")] - public static IType FindType(this ICompilation compilation, string reflectionTypeName) - { - return ParseReflectionName(reflectionTypeName).Resolve(compilation.TypeResolveContext); - } #endregion #region Type.ToTypeReference() @@ -218,6 +207,7 @@ namespace ICSharpCode.NRefactory.TypeSystem /// first, and if the type is not found there, /// it will look in all other assemblies of the compilation. /// + /// public static ITypeReference ParseReflectionName(string reflectionTypeName) { if (reflectionTypeName == null) diff --git a/ICSharpCode.NRefactory/TypeSystem/TopLevelTypeName.cs b/ICSharpCode.NRefactory/TypeSystem/TopLevelTypeName.cs new file mode 100644 index 0000000000..00fd98d303 --- /dev/null +++ b/ICSharpCode.NRefactory/TypeSystem/TopLevelTypeName.cs @@ -0,0 +1,144 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace ICSharpCode.NRefactory.TypeSystem +{ + /// + /// Holds the name of a top-level type. + /// This struct cannot refer to nested classes. + /// + [Serializable] + public struct TopLevelTypeName : IEquatable + { + readonly string namespaceName; + readonly string name; + readonly int typeParameterCount; + + public TopLevelTypeName(string namespaceName, string name, int typeParameterCount = 0) + { + if (namespaceName == null) + throw new ArgumentNullException("namespaceName"); + if (name == null) + throw new ArgumentNullException("name"); + this.namespaceName = namespaceName; + this.name = name; + this.typeParameterCount = typeParameterCount; + } + + public TopLevelTypeName(string reflectionName) + { + int pos = reflectionName.LastIndexOf('.'); + if (pos < 0) { + namespaceName = string.Empty; + name = reflectionName; + } else { + namespaceName = reflectionName.Substring(0, pos); + name = reflectionName.Substring(pos + 1); + } + name = ReflectionHelper.SplitTypeParameterCountFromReflectionName(name, out typeParameterCount); + } + + public string Namespace { + get { return namespaceName; } + } + + public string Name { + get { return name; } + } + + public int TypeParameterCount { + get { return typeParameterCount; } + } + + public string ReflectionName { + get { + StringBuilder b = new StringBuilder(); + if (!string.IsNullOrEmpty(namespaceName)) { + b.Append(namespaceName); + b.Append('.'); + } + b.Append(name); + if (typeParameterCount > 0) { + b.Append('`'); + b.Append(typeParameterCount); + } + return b.ToString(); + } + } + + public override string ToString() + { + return this.ReflectionName; + } + + public override bool Equals(object obj) + { + return (obj is TopLevelTypeName) && Equals((TopLevelTypeName)obj); + } + + public bool Equals(TopLevelTypeName other) + { + return this.namespaceName == other.namespaceName && this.name == other.name && this.typeParameterCount == other.typeParameterCount; + } + + public override int GetHashCode() + { + return (name != null ? name.GetHashCode() : 0) ^ (namespaceName != null ? namespaceName.GetHashCode() : 0) ^ typeParameterCount; + } + + public static bool operator ==(TopLevelTypeName lhs, TopLevelTypeName rhs) + { + return lhs.Equals(rhs); + } + + public static bool operator !=(TopLevelTypeName lhs, TopLevelTypeName rhs) + { + return !lhs.Equals(rhs); + } + } + + [Serializable] + public sealed class TopLevelTypeNameComparer : IEqualityComparer + { + public static readonly TopLevelTypeNameComparer Ordinal = new TopLevelTypeNameComparer(StringComparer.Ordinal); + public static readonly TopLevelTypeNameComparer OrdinalIgnoreCase = new TopLevelTypeNameComparer(StringComparer.OrdinalIgnoreCase); + + public readonly StringComparer NameComparer; + + public TopLevelTypeNameComparer(StringComparer nameComparer) + { + this.NameComparer = nameComparer; + } + + public bool Equals(TopLevelTypeName x, TopLevelTypeName y) + { + return x.TypeParameterCount == y.TypeParameterCount + && NameComparer.Equals(x.Name, y.Name) + && NameComparer.Equals(x.Namespace, y.Namespace); + } + + public int GetHashCode(TopLevelTypeName obj) + { + return NameComparer.GetHashCode(obj.Name) ^ NameComparer.GetHashCode(obj.Namespace) ^ obj.TypeParameterCount; + } + } +} From 904af4bfd28dff080ba0c6bfa2fce43d7829f2c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Tue, 11 Sep 2012 10:11:19 +0200 Subject: [PATCH 15/37] [Completion] Paremeter completion offset checks now for comments. --- .../Completion/CSharpCompletionEngineBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngineBase.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngineBase.cs index 85cf16368f..b938064dc9 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngineBase.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngineBase.cs @@ -99,7 +99,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion // current member, instead of the beginning of the file. cpos = offset - 1; var mem = currentMember; - if (mem == null || (mem is IType)) { + if (mem == null || (mem is IType) || IsInsideCommentStringOrDirective ()) { return false; } int startPos = document.GetOffset (mem.Region.BeginLine, mem.Region.BeginColumn); From a8f4a606e23c4ae7fd131d4502fa239f5ab7a9fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Tue, 11 Sep 2012 10:36:54 +0200 Subject: [PATCH 16/37] [TypeSystem] Added old GetTypeDefinition method for compatibility reasons. --- .../TypeSystem/ExtensionMethods.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs b/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs index 52f7ec258a..83a43be5ba 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs @@ -468,5 +468,19 @@ namespace ICSharpCode.NRefactory.TypeSystem return reference.Resolve (compilation.TypeResolveContext); } #endregion + + + #region IAssembly.GetTypeDefinition(string,string,int) + /// + /// Gets the type definition for a top-level type. + /// + /// This method uses ordinal name comparison, not the compilation's name comparer. + public static ITypeDefinition GetTypeDefinition(this IAssembly assembly, string namespaceName, string name, int typeParameterCount = 0) + { + if (assembly == null) + throw new ArgumentNullException ("assembly"); + return assembly.GetTypeDefinition (new TopLevelTypeName (namespaceName, name, typeParameterCount)); + } + #endregion } } From 72f6449a5e16f62faf905e4f2b2d1be19628ecd3 Mon Sep 17 00:00:00 2001 From: mkrueger Date: Tue, 11 Sep 2012 12:08:49 +0200 Subject: [PATCH 17/37] [Completion] Fixed little completion issue. --- .../Completion/CSharpCompletionEngine.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs index 2d0b66b4de..f44e46953d 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs @@ -1787,8 +1787,10 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } if (hintType is ParameterizedType && hintType.TypeParameterCount == 1 && hintType.FullName == "System.Collections.Generic.IEnumerable") { var arg = ((ParameterizedType)hintType).TypeArguments.FirstOrDefault(); - var array = new ArrayTypeReference(arg.ToTypeReference(), 1).Resolve(ctx); - wrapper.AddType(array, amb.ConvertType(array)); + if (arg.Kind != TypeKind.TypeParameter) { + var array = new ArrayTypeReference(arg.ToTypeReference(), 1).Resolve(ctx); + wrapper.AddType(array, amb.ConvertType(array)); + } } } else { var hint = wrapper.AddType(hintType, DefaultCompletionString); From b37698b39be6d669598469a28f8fd5861bade80e Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 11 Sep 2012 17:53:37 +0200 Subject: [PATCH 18/37] ICompilation.Import(IType): added support for importing open generic types Renamed 'IResolved' to 'ICompilationProvider'. --- .../TypeSystem/CSharpAssembly.cs | 2 +- .../TypeSystem/ResolvedUsingScope.cs | 2 +- .../TypeSystem/TypeParameterTests.cs | 35 +++++++++++++++ .../TypeSystem/TypeSystemHelper.cs | 2 +- .../TypeSystem/ExtensionMethods.cs | 45 ++++++++++++++++++- .../TypeSystem/FullTypeName.cs | 3 +- .../TypeSystem/IAssembly.cs | 2 +- .../TypeSystem/ICompilation.cs | 2 +- ICSharpCode.NRefactory/TypeSystem/IEntity.cs | 2 +- .../TypeSystem/INamespace.cs | 2 +- .../DefaultUnresolvedAssembly.cs | 2 +- .../DefaultUnresolvedAttribute.cs | 2 +- .../TypeSystem/ParameterizedType.cs | 2 +- 13 files changed, 91 insertions(+), 12 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpAssembly.cs b/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpAssembly.cs index 02bbc68f25..bda16fc326 100644 --- a/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpAssembly.cs +++ b/ICSharpCode.NRefactory.CSharp/TypeSystem/CSharpAssembly.cs @@ -289,7 +289,7 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem } } - ICompilation IResolved.Compilation { + ICompilation ICompilationProvider.Compilation { get { return assembly.Compilation; } } diff --git a/ICSharpCode.NRefactory.CSharp/TypeSystem/ResolvedUsingScope.cs b/ICSharpCode.NRefactory.CSharp/TypeSystem/ResolvedUsingScope.cs index 0af450a259..43a81a5635 100644 --- a/ICSharpCode.NRefactory.CSharp/TypeSystem/ResolvedUsingScope.cs +++ b/ICSharpCode.NRefactory.CSharp/TypeSystem/ResolvedUsingScope.cs @@ -176,7 +176,7 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem get { return EmptyList.Instance; } } - ICompilation IResolved.Compilation { + ICompilation ICompilationProvider.Compilation { get { return parentNamespace.Compilation; } } diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeParameterTests.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeParameterTests.cs index e081eefbd8..90630413cb 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeParameterTests.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeParameterTests.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Linq; using ICSharpCode.NRefactory.TypeSystem.Implementation; using NUnit.Framework; @@ -79,5 +80,39 @@ namespace ICSharpCode.NRefactory.TypeSystem Assert.AreEqual("System.Collections.Generic.List`1[[System.String]]", resolvedC.TypeParameters[0].EffectiveBaseClass.ReflectionName); Assert.AreEqual("System.Collections.Generic.List`1[[System.String]]", resolvedC.TypeParameters[1].EffectiveBaseClass.ReflectionName); } + + [Test] + public void ImportOpenGenericType() + { + // class C { void M() {} } + + var c = new DefaultUnresolvedTypeDefinition(string.Empty, "C"); + c.TypeParameters.Add(new DefaultUnresolvedTypeParameter(EntityType.TypeDefinition, 0, "T")); + c.TypeParameters.Add(new DefaultUnresolvedTypeParameter(EntityType.TypeDefinition, 1, "U")); + var m = new DefaultUnresolvedMethod(c, "M"); + m.TypeParameters.Add(new DefaultUnresolvedTypeParameter(EntityType.Method, 0, "X")); + c.Members.Add(m); + + var resolvedC1 = TypeSystemHelper.CreateCompilationAndResolve(c); + var resolvedM1 = resolvedC1.Methods.Single(method => method.Name == "M"); + + var resolvedC2 = TypeSystemHelper.CreateCompilationAndResolve(c); + var resolvedM2 = resolvedC2.Methods.Single(method => method.Name == "M"); + + // the types, methods and type parameters differ in the two compilations: + Assert.AreNotEqual(resolvedC1, resolvedC2); + Assert.AreNotEqual(resolvedM1, resolvedM2); + Assert.AreNotEqual(resolvedC1.TypeParameters[1], resolvedC2.TypeParameters[1]); + Assert.AreNotEqual(resolvedM1.TypeParameters[0], resolvedM2.TypeParameters[0]); + + // C + var pt1 = new ParameterizedType(resolvedC1, new[] { resolvedC1.TypeParameters[1], resolvedM1.TypeParameters[0] }); + var pt2 = (ParameterizedType)resolvedC2.Compilation.Import(pt1); + + // importing resulted in C in the new compilation: + Assert.AreEqual(resolvedC2, pt2.GetDefinition()); + Assert.AreEqual(resolvedC2.TypeParameters[1], pt2.TypeArguments[0]); + Assert.AreEqual(resolvedM2.TypeParameters[0], pt2.TypeArguments[1]); + } } } diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemHelper.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemHelper.cs index f7671aa102..e0623efdc3 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemHelper.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemHelper.cs @@ -48,7 +48,7 @@ namespace ICSharpCode.NRefactory.TypeSystem public static ITypeDefinition CreateCompilationAndResolve(IUnresolvedTypeDefinition unresolvedTypeDefinition) { var compilation = CreateCompilation(unresolvedTypeDefinition); - return unresolvedTypeDefinition.Resolve(new SimpleTypeResolveContext(compilation.MainAssembly)).GetDefinition(); + return compilation.MainAssembly.GetTypeDefinition(unresolvedTypeDefinition.FullTypeName); } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs b/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs index 83a43be5ba..732e9789f1 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs @@ -96,12 +96,31 @@ namespace ICSharpCode.NRefactory.TypeSystem sealed class TypeClassificationVisitor : TypeVisitor { internal bool isOpen; + internal IEntity typeParameterOwner; + int typeParameterOwnerNestingLevel; public override IType VisitTypeParameter(ITypeParameter type) { isOpen = true; + // If both classes and methods, or different classes (nested types) + // are involved, find the most specific one + int newNestingLevel = GetNestingLevel(type.Owner); + if (newNestingLevel > typeParameterOwnerNestingLevel) { + typeParameterOwner = type.Owner; + typeParameterOwnerNestingLevel = newNestingLevel; + } return base.VisitTypeParameter(type); } + + static int GetNestingLevel(IEntity entity) + { + int level = 0; + while (entity != null) { + level++; + entity = entity.DeclaringTypeDefinition; + } + return level; + } } /// @@ -126,6 +145,21 @@ namespace ICSharpCode.NRefactory.TypeSystem return v.isOpen; } + /// + /// Gets the entity that owns the type parameters occurring in the specified type. + /// If both class and method type parameters are present, the method is returned. + /// Returns null if the specified type is closed. + /// + /// + static IEntity GetTypeParameterOwner(IType type) + { + if (type == null) + throw new ArgumentNullException("type"); + TypeClassificationVisitor v = new TypeClassificationVisitor(); + type.AcceptVisitor(v); + return v.typeParameterOwner; + } + /// /// Gets whether the type is unbound (is a generic type, but no type arguments were provided). /// @@ -162,7 +196,16 @@ namespace ICSharpCode.NRefactory.TypeSystem throw new ArgumentNullException("compilation"); if (type == null) return null; - return type.ToTypeReference().Resolve(compilation.TypeResolveContext); + var compilationProvider = type as ICompilationProvider; + if (compilationProvider != null && compilationProvider.Compilation == compilation) + return type; + IEntity typeParameterOwner = GetTypeParameterOwner(type); + IEntity importedTypeParameterOwner = compilation.Import(typeParameterOwner); + if (importedTypeParameterOwner != null) { + return type.ToTypeReference().Resolve(new SimpleTypeResolveContext(importedTypeParameterOwner)); + } else { + return type.ToTypeReference().Resolve(compilation.TypeResolveContext); + } } /// diff --git a/ICSharpCode.NRefactory/TypeSystem/FullTypeName.cs b/ICSharpCode.NRefactory/TypeSystem/FullTypeName.cs index dd76eb22a5..ea745c6ab5 100644 --- a/ICSharpCode.NRefactory/TypeSystem/FullTypeName.cs +++ b/ICSharpCode.NRefactory/TypeSystem/FullTypeName.cs @@ -30,7 +30,8 @@ namespace ICSharpCode.NRefactory.TypeSystem /// A full type name can only represent type definitions, not arbitrary types. /// It does not include any type arguments, and can not refer to array or pointer types. /// - /// + /// A full type name represented as reflection name has the syntax: + /// NamespaceName '.' TopLevelTypeName ['`'#] { '+' NestedTypeName ['`'#] } /// [Serializable] public struct FullTypeName : IEquatable diff --git a/ICSharpCode.NRefactory/TypeSystem/IAssembly.cs b/ICSharpCode.NRefactory/TypeSystem/IAssembly.cs index 1c31e69942..c38cfe82fe 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IAssembly.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IAssembly.cs @@ -69,7 +69,7 @@ namespace ICSharpCode.NRefactory.TypeSystem /// /// Represents an assembly. /// - public interface IAssembly : IResolved + public interface IAssembly : ICompilationProvider { /// /// Gets the original unresolved assembly. diff --git a/ICSharpCode.NRefactory/TypeSystem/ICompilation.cs b/ICSharpCode.NRefactory/TypeSystem/ICompilation.cs index 44e4647ec6..aa72c8a133 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ICompilation.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ICompilation.cs @@ -72,7 +72,7 @@ namespace ICSharpCode.NRefactory.TypeSystem CacheManager CacheManager { get; } } - public interface IResolved + public interface ICompilationProvider { ICompilation Compilation { get; } } diff --git a/ICSharpCode.NRefactory/TypeSystem/IEntity.cs b/ICSharpCode.NRefactory/TypeSystem/IEntity.cs index 236ad4b944..d2ef20b03d 100644 --- a/ICSharpCode.NRefactory/TypeSystem/IEntity.cs +++ b/ICSharpCode.NRefactory/TypeSystem/IEntity.cs @@ -93,7 +93,7 @@ namespace ICSharpCode.NRefactory.TypeSystem /// /// Represents a resolved entity. /// - public interface IEntity : IResolved, INamedElement, IHasAccessibility + public interface IEntity : ICompilationProvider, INamedElement, IHasAccessibility { /// /// Gets the entity type. diff --git a/ICSharpCode.NRefactory/TypeSystem/INamespace.cs b/ICSharpCode.NRefactory/TypeSystem/INamespace.cs index b3631fe060..301554a5bd 100644 --- a/ICSharpCode.NRefactory/TypeSystem/INamespace.cs +++ b/ICSharpCode.NRefactory/TypeSystem/INamespace.cs @@ -24,7 +24,7 @@ namespace ICSharpCode.NRefactory.TypeSystem /// /// Represents a resolved namespace. /// - public interface INamespace : IResolved + public interface INamespace : ICompilationProvider { // No pointer back to unresolved namespace: // multiple unresolved namespaces (from different assemblies) get diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs index 2016ee1f30..875e7d0099 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAssembly.cs @@ -429,7 +429,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return null; } - ICompilation IResolved.Compilation { + ICompilation ICompilationProvider.Compilation { get { return assembly.compilation; } } diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAttribute.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAttribute.cs index c18321b59e..4545f9221e 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAttribute.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/DefaultUnresolvedAttribute.cs @@ -187,7 +187,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation return true; } - sealed class DefaultResolvedAttribute : IAttribute, IResolved + sealed class DefaultResolvedAttribute : IAttribute, ICompilationProvider { readonly DefaultUnresolvedAttribute unresolved; readonly ITypeResolveContext context; diff --git a/ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs b/ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs index f62e27769c..2cab3135a5 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs @@ -57,7 +57,7 @@ namespace ICSharpCode.NRefactory.TypeSystem for (int i = 0; i < this.typeArguments.Length; i++) { if (this.typeArguments[i] == null) throw new ArgumentNullException("typeArguments[" + i + "]"); - IResolved r = this.typeArguments[i] as IResolved; + ICompilationProvider r = this.typeArguments[i] as ICompilationProvider; if (r != null && r.Compilation != genericType.Compilation) throw new InvalidOperationException("Cannot parameterize a type with type arguments from a different compilation."); } From d01a22564a819461c58cef0a07dc252060260ee2 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Tue, 11 Sep 2012 18:04:30 +0200 Subject: [PATCH 19/37] Implement ICompilationProvider in a few more places where we have the compilation available. --- .../TypeSystem/TypeOrNamespaceReference.cs | 2 +- ICSharpCode.NRefactory/TypeSystem/ArrayType.cs | 10 +++++++++- ICSharpCode.NRefactory/TypeSystem/ICompilation.cs | 4 ++++ ICSharpCode.NRefactory/TypeSystem/ITypeReference.cs | 10 ++-------- .../Implementation/AbstractResolvedTypeParameter.cs | 2 +- ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs | 6 +++--- 6 files changed, 20 insertions(+), 14 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeOrNamespaceReference.cs b/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeOrNamespaceReference.cs index ddbcea2cab..937f2874ec 100644 --- a/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeOrNamespaceReference.cs +++ b/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeOrNamespaceReference.cs @@ -35,7 +35,7 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem public abstract ResolveResult Resolve(CSharpResolver resolver); /// - /// Returns the type that is referenced; or an if the type isn't found. + /// Returns the type that is referenced; or an UnknownType if the type isn't found. /// public abstract IType ResolveType(CSharpResolver resolver); diff --git a/ICSharpCode.NRefactory/TypeSystem/ArrayType.cs b/ICSharpCode.NRefactory/TypeSystem/ArrayType.cs index 373ebaf666..40e33d8ad9 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ArrayType.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ArrayType.cs @@ -25,7 +25,7 @@ namespace ICSharpCode.NRefactory.TypeSystem /// /// Represents an array type. /// - public sealed class ArrayType : TypeWithElementType + public sealed class ArrayType : TypeWithElementType, ICompilationProvider { readonly int dimensions; readonly ICompilation compilation; @@ -38,12 +38,20 @@ namespace ICSharpCode.NRefactory.TypeSystem throw new ArgumentOutOfRangeException("dimensions", dimensions, "dimensions must be positive"); this.compilation = compilation; this.dimensions = dimensions; + + ICompilationProvider p = elementType as ICompilationProvider; + if (p != null && p.Compilation != compilation) + throw new InvalidOperationException("Cannot create an array type using a different compilation from the element type."); } public override TypeKind Kind { get { return TypeKind.Array; } } + public ICompilation Compilation { + get { return compilation; } + } + public int Dimensions { get { return dimensions; } } diff --git a/ICSharpCode.NRefactory/TypeSystem/ICompilation.cs b/ICSharpCode.NRefactory/TypeSystem/ICompilation.cs index aa72c8a133..9ab382b440 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ICompilation.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ICompilation.cs @@ -74,6 +74,10 @@ namespace ICSharpCode.NRefactory.TypeSystem public interface ICompilationProvider { + /// + /// Gets the parent compilation. + /// This property never returns null. + /// ICompilation Compilation { get; } } } diff --git a/ICSharpCode.NRefactory/TypeSystem/ITypeReference.cs b/ICSharpCode.NRefactory/TypeSystem/ITypeReference.cs index fc22826c37..c13a90dd2c 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ITypeReference.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ITypeReference.cs @@ -43,20 +43,14 @@ namespace ICSharpCode.NRefactory.TypeSystem /// /// /// Returns the resolved type. - /// In case of an error, returns . + /// In case of an error, returns an unknown type (). /// Never returns null. /// IType Resolve(ITypeResolveContext context); } - public interface ITypeResolveContext + public interface ITypeResolveContext : ICompilationProvider { - /// - /// Gets the parent compilation. - /// This property never returns null. - /// - ICompilation Compilation { get; } - /// /// Gets the current assembly. /// This property may return null if this context does not specify any assembly. diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs index d9dd27c16d..8b12e0e920 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/AbstractResolvedTypeParameter.cs @@ -24,7 +24,7 @@ using ICSharpCode.NRefactory.Utils; namespace ICSharpCode.NRefactory.TypeSystem.Implementation { - public abstract class AbstractTypeParameter : ITypeParameter + public abstract class AbstractTypeParameter : ITypeParameter, ICompilationProvider { readonly ICompilation compilation; readonly EntityType ownerType; diff --git a/ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs b/ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs index 2cab3135a5..74332d02e5 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs +++ b/ICSharpCode.NRefactory/TypeSystem/ParameterizedType.cs @@ -37,7 +37,7 @@ namespace ICSharpCode.NRefactory.TypeSystem /// the type arguments. /// [Serializable] - public sealed class ParameterizedType : IType + public sealed class ParameterizedType : IType, ICompilationProvider { readonly ITypeDefinition genericType; readonly IType[] typeArguments; @@ -57,8 +57,8 @@ namespace ICSharpCode.NRefactory.TypeSystem for (int i = 0; i < this.typeArguments.Length; i++) { if (this.typeArguments[i] == null) throw new ArgumentNullException("typeArguments[" + i + "]"); - ICompilationProvider r = this.typeArguments[i] as ICompilationProvider; - if (r != null && r.Compilation != genericType.Compilation) + ICompilationProvider p = this.typeArguments[i] as ICompilationProvider; + if (p != null && p.Compilation != genericType.Compilation) throw new InvalidOperationException("Cannot parameterize a type with type arguments from a different compilation."); } } From 02ff35d1870889d49db60c59b2d13d3c9c0306fc Mon Sep 17 00:00:00 2001 From: mkrueger <> Date: Wed, 12 Sep 2012 09:58:57 +0200 Subject: [PATCH 20/37] [Completion] Cleaned up some code. --- .../Completion/CSharpCompletionEngine.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs index f44e46953d..575d39785f 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs @@ -1788,7 +1788,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion if (hintType is ParameterizedType && hintType.TypeParameterCount == 1 && hintType.FullName == "System.Collections.Generic.IEnumerable") { var arg = ((ParameterizedType)hintType).TypeArguments.FirstOrDefault(); if (arg.Kind != TypeKind.TypeParameter) { - var array = new ArrayTypeReference(arg.ToTypeReference(), 1).Resolve(ctx); + var array = new ArrayType (ctx.Compilation, arg, 1); wrapper.AddType(array, amb.ConvertType(array)); } } From bb8df032111e4f370d9de55dd5dfb8f2a646258d Mon Sep 17 00:00:00 2001 From: mkrueger <> Date: Wed, 12 Sep 2012 10:11:37 +0200 Subject: [PATCH 21/37] [CodeIssue] Fixed bug in redundant type cast issue. --- .../CodeIssues/RedundantTypeCastIssue.cs | 36 +++++++++++++++++-- .../CodeIssues/RedundantTypeCastIssueTests.cs | 24 +++++++++++++ 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/RedundantTypeCastIssue.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/RedundantTypeCastIssue.cs index 64f372731f..fe59fba675 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/RedundantTypeCastIssue.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/CodeIssues/RedundantTypeCastIssue.cs @@ -28,6 +28,7 @@ using System.Collections.Generic; using System.Linq; using ICSharpCode.NRefactory.Semantics; using ICSharpCode.NRefactory.TypeSystem; +using ICSharpCode.NRefactory.CSharp.Resolver; namespace ICSharpCode.NRefactory.CSharp.Refactoring { @@ -73,17 +74,45 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring var invocationExpr = memberRefExpr.Parent as InvocationExpression; if (invocationExpr != null && invocationExpr.Target == memberRefExpr) { var invocationResolveResult = ctx.Resolve (invocationExpr) as InvocationResolveResult; - if (invocationResolveResult != null) + if (invocationResolveResult != null) { return invocationResolveResult.Member.DeclaringType; + } } else { var memberResolveResult = ctx.Resolve (memberRefExpr) as MemberResolveResult; - if (memberResolveResult != null) + if (memberResolveResult != null) { return memberResolveResult.Member.DeclaringType; + } } } return ctx.GetExpectedType (typeCastNode); } + bool IsExplicitImplementation(IType exprType, IType interfaceType, Expression typeCastNode) + { + var memberRefExpr = typeCastNode.Parent as MemberReferenceExpression; + if (memberRefExpr != null) { + var rr = ctx.Resolve(memberRefExpr); + var memberResolveResult = rr as MemberResolveResult; + if (memberResolveResult != null) { + foreach (var member in exprType.GetMembers (m => m.EntityType == memberResolveResult.Member.EntityType)) { + if (member.IsExplicitInterfaceImplementation && member.ImplementedInterfaceMembers.Contains (memberResolveResult.Member)) { + return true; + } + } + } + + var methodGroupResolveResult = rr as MethodGroupResolveResult; + if (methodGroupResolveResult != null) { + foreach (var member in exprType.GetMethods ()) { + if (member.IsExplicitInterfaceImplementation && member.ImplementedInterfaceMembers.Any (m => methodGroupResolveResult.Methods.Contains ((IMethod)m))) { + return true; + } + } + } + } + return false; + } + void AddIssue (Expression typeCastNode, Expression expr, TextLocation start, TextLocation end) { AddIssue (start, end, ctx.TranslateString ("Remove redundant type cast"), @@ -94,9 +123,10 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring { while (typeCastNode.Parent != null && typeCastNode.Parent is ParenthesizedExpression) typeCastNode = (Expression)typeCastNode.Parent; - var expectedType = GetExpectedType (typeCastNode); var exprType = ctx.Resolve (expr).Type; + if (expectedType.Kind == TypeKind.Interface && IsExplicitImplementation (exprType, expectedType, typeCastNode)) + return; if (exprType.GetAllBaseTypes ().Any (t => t.Equals(expectedType))) AddIssue (typeCastNode, expr, castStart, castEnd); } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/RedundantTypeCastIssueTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/RedundantTypeCastIssueTests.cs index 55d39a2810..57023e0d38 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/RedundantTypeCastIssueTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/RedundantTypeCastIssueTests.cs @@ -146,5 +146,29 @@ class TestClass }"; Test (input, 0); } + + /// + /// Bug 7065 - "remove redundant type cast" false positive for explicit interface implementation + /// + [Test] + public void TestBug7065 () + { + var input = @" +using System; +public class TestClass : IDisposable +{ + void IDisposable.Dispose() + { + } + + void Foo() + { + ((IDisposable)this).Dispose(); + } +} +"; + Test (input, 0); + } + } } From 1f780132580e1d8497f278b90265d5b28dcf3edf Mon Sep 17 00:00:00 2001 From: mkrueger <> Date: Wed, 12 Sep 2012 10:49:03 +0200 Subject: [PATCH 22/37] [Ast] GetTypes now includes delegate declarations as well. --- ICSharpCode.NRefactory.CSharp/Ast/SyntaxTree.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Ast/SyntaxTree.cs b/ICSharpCode.NRefactory.CSharp/Ast/SyntaxTree.cs index 2906696b36..dfe4357ec5 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/SyntaxTree.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/SyntaxTree.cs @@ -105,14 +105,20 @@ namespace ICSharpCode.NRefactory.CSharp { } - public IEnumerable GetTypes(bool includeInnerTypes = false) + /// + /// Gets all defined types in this syntax tree. + /// + /// + /// A list containing or nodes. + /// + public IEnumerable GetTypes(bool includeInnerTypes = false) { Stack nodeStack = new Stack (); nodeStack.Push(this); while (nodeStack.Count > 0) { var curNode = nodeStack.Pop(); - if (curNode is TypeDeclaration) { - yield return (TypeDeclaration)curNode; + if (curNode is TypeDeclaration || curNode is DelegateDeclaration) { + yield return (EntityDeclaration)curNode; } foreach (var child in curNode.Children) { if (!(child is Statement || child is Expression) && @@ -121,6 +127,7 @@ namespace ICSharpCode.NRefactory.CSharp } } } + protected internal override bool DoMatch(AstNode other, PatternMatching.Match match) { From bf62230dc10a59ca660fac32c5c8fb272514bfd2 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 12 Sep 2012 09:53:37 +0200 Subject: [PATCH 23/37] Rename 'ExtensionMethods' to 'TypeSystemExtensions'. This avoids a naming conflict with other extension method classes in the solution. --- ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj | 2 +- .../TypeSystem/{ExtensionMethods.cs => TypeSystemExtensions.cs} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename ICSharpCode.NRefactory/TypeSystem/{ExtensionMethods.cs => TypeSystemExtensions.cs} (99%) diff --git a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj index d20a2aceac..e646565ee2 100644 --- a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj +++ b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj @@ -137,7 +137,7 @@ - + diff --git a/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs b/ICSharpCode.NRefactory/TypeSystem/TypeSystemExtensions.cs similarity index 99% rename from ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs rename to ICSharpCode.NRefactory/TypeSystem/TypeSystemExtensions.cs index 732e9789f1..f04a6206ce 100644 --- a/ICSharpCode.NRefactory/TypeSystem/ExtensionMethods.cs +++ b/ICSharpCode.NRefactory/TypeSystem/TypeSystemExtensions.cs @@ -28,7 +28,7 @@ namespace ICSharpCode.NRefactory.TypeSystem /// /// Contains extension methods for the type system. /// - public static class ExtensionMethods + public static class TypeSystemExtensions { #region GetAllBaseTypes /// From 07c8929aa23792de352d4bfbbade86782e2e3b8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Wed, 12 Sep 2012 11:32:13 +0200 Subject: [PATCH 24/37] [Ast] GetTypes now includes inner delegates as well. --- ICSharpCode.NRefactory.CSharp/Ast/SyntaxTree.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.NRefactory.CSharp/Ast/SyntaxTree.cs b/ICSharpCode.NRefactory.CSharp/Ast/SyntaxTree.cs index dfe4357ec5..f8cbf02593 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/SyntaxTree.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/SyntaxTree.cs @@ -122,7 +122,7 @@ namespace ICSharpCode.NRefactory.CSharp } foreach (var child in curNode.Children) { if (!(child is Statement || child is Expression) && - (child.Role != Roles.TypeMemberRole || (child is TypeDeclaration && includeInnerTypes))) + (child.Role != Roles.TypeMemberRole || ((child is TypeDeclaration || child is DelegateDeclaration) && includeInnerTypes))) nodeStack.Push (child); } } From fc72147b884c39faaf1afe6f348e9a7c42332e31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Wed, 12 Sep 2012 15:09:45 +0200 Subject: [PATCH 25/37] [Refactoring] Script now corrects the formatting of inserted & replaced nodes. --- .../Formatter/AstFormattingVisitor.cs | 12 +++- .../Formatter/FormattingOptionsFactory.cs | 1 + .../Refactoring/Script.cs | 15 ++++ .../CSharp/CodeActions/AddCatchTypeTests.cs | 6 +- .../ConvertAnonymousDelegateToLambdaTests.cs | 12 ++-- .../ConvertConditionalToIfTests.cs | 8 +-- .../ConvertLamdaToAnonymousDelegateTests.cs | 12 ++-- .../CodeActions/ConvertSwitchToIfTests.cs | 21 ++---- .../CodeActions/DeclareLocalVariableTests.cs | 2 +- ...InvocationToStaticMethodInvocationTests.cs | 4 +- .../CodeActions/MoveToOuterScopeTests.cs | 68 +++++++++---------- .../RemoveRedundantCatchTypeTests.cs | 6 +- .../SplitDeclarationAndAssignmentTests.cs | 3 +- .../AccessToModifiedClosureTests.cs | 12 ++-- .../CodeIssues/ConstantConditionIssueTests.cs | 9 ++- ...ressionIsAlwaysOfProvidedTypeIssueTests.cs | 12 ++-- .../CodeIssues/InspectionActionTestBase.cs | 3 + .../CodeIssues/RedundantToStringTests.cs | 12 ++-- ...erenceToStaticMemberViaDerivedTypeTests.cs | 10 +-- .../FormattingTests/TextEditorTestAdapter.cs | 3 + 20 files changed, 128 insertions(+), 103 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Formatter/AstFormattingVisitor.cs b/ICSharpCode.NRefactory.CSharp/Formatter/AstFormattingVisitor.cs index 8a12bc84a1..06c1f6e4b6 100644 --- a/ICSharpCode.NRefactory.CSharp/Formatter/AstFormattingVisitor.cs +++ b/ICSharpCode.NRefactory.CSharp/Formatter/AstFormattingVisitor.cs @@ -739,15 +739,15 @@ namespace ICSharpCode.NRefactory.CSharp } var lastLoc = fieldDeclaration.StartLocation; - curIndent.Push(IndentType.Block); foreach (var initializer in fieldDeclaration.Variables) { if (lastLoc.Line != initializer.StartLocation.Line) { + curIndent.Push(IndentType.Block); FixStatementIndentation(initializer.StartLocation); + curIndent.Pop (); lastLoc = initializer.StartLocation; } initializer.AcceptVisitor(this); } - curIndent.Pop (); } public override void VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration) @@ -1179,8 +1179,10 @@ namespace ICSharpCode.NRefactory.CSharp nextStatementIndent = " "; } } + bool pushed = false; if (policy.IndentBlocks && !(policy.AlignEmbeddedIfStatements && node is IfElseStatement && node.Parent is IfElseStatement || policy.AlignEmbeddedUsingStatements && node is UsingStatement && node.Parent is UsingStatement)) { curIndent.Push(IndentType.Block); + pushed = true; } if (isBlock) { VisitBlockWithoutFixingBraces((BlockStatement)node, false); @@ -1190,7 +1192,7 @@ namespace ICSharpCode.NRefactory.CSharp } node.AcceptVisitor(this); } - if (policy.IndentBlocks && !(policy.AlignEmbeddedIfStatements && node is IfElseStatement && node.Parent is IfElseStatement || policy.AlignEmbeddedUsingStatements && node is UsingStatement && node.Parent is UsingStatement)) { + if (pushed) { curIndent.Pop(); } switch (braceForcement) { @@ -2105,6 +2107,10 @@ namespace ICSharpCode.NRefactory.CSharp void FixStatementIndentation(TextLocation location) { + if (location.Line < 1 || location.Column < 1) { + Console.WriteLine("invalid location!"); + return; + } int offset = document.GetOffset(location); if (offset <= 0) { Console.WriteLine("possible wrong offset"); diff --git a/ICSharpCode.NRefactory.CSharp/Formatter/FormattingOptionsFactory.cs b/ICSharpCode.NRefactory.CSharp/Formatter/FormattingOptionsFactory.cs index 354d5865ff..c430717a4f 100644 --- a/ICSharpCode.NRefactory.CSharp/Formatter/FormattingOptionsFactory.cs +++ b/ICSharpCode.NRefactory.CSharp/Formatter/FormattingOptionsFactory.cs @@ -82,6 +82,7 @@ namespace ICSharpCode.NRefactory.CSharp StatementBraceStyle = BraceStyle.EndOfLine, ElseNewLinePlacement = NewLinePlacement.SameLine, + ElseIfNewLinePlacement = NewLinePlacement.SameLine, CatchNewLinePlacement = NewLinePlacement.SameLine, FinallyNewLinePlacement = NewLinePlacement.SameLine, WhileNewLinePlacement = NewLinePlacement.SameLine, diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/Script.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/Script.cs index 574fafe817..51897bf50e 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/Script.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/Script.cs @@ -152,6 +152,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring text += Options.EolMarker; InsertText(startOffset, text); output.RegisterTrackedSegments(this, startOffset); + CorrectFormatting (node, insertNode); } public void InsertAfter(AstNode node, AstNode insertNode) @@ -164,6 +165,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring var insertOffset = GetCurrentOffset(node.EndLocation); InsertText(insertOffset, text); output.RegisterTrackedSegments(this, insertOffset); + CorrectFormatting (node, insertNode); } public void AddTo(BlockStatement bodyStatement, AstNode insertNode) @@ -172,6 +174,7 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring var output = OutputNode(1 + GetIndentLevelAt(startOffset), insertNode, true); InsertText(startOffset, output.Text); output.RegisterTrackedSegments(this, startOffset); + CorrectFormatting (null, insertNode); } public virtual Task Link (params AstNode[] nodes) @@ -196,6 +199,18 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring output.TrimStart (); Replace (startOffset, segment.Length, output.Text); output.RegisterTrackedSegments(this, startOffset); + CorrectFormatting (node, node); + } + + void CorrectFormatting(AstNode node, AstNode newNode) + { + if (node is Identifier || node is IdentifierExpression || node is CSharpTokenNode || node is AstType) + return; + if (node == null || node.Parent is BlockStatement) { + FormatText(newNode); + } else { + FormatText((node.Parent != null && (node.Parent is Statement || node.Parent is Expression || node.Parent is VariableInitializer)) ? node.Parent : newNode); + } } public abstract void Remove (AstNode node, bool removeEmptyLine = true); diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/AddCatchTypeTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/AddCatchTypeTests.cs index f414b2c747..dd217ecffa 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/AddCatchTypeTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/AddCatchTypeTests.cs @@ -50,8 +50,7 @@ class TestClass public void F() { try { - } - catch (System.Exception e) { + } catch (System.Exception e) { } } }"); @@ -107,8 +106,7 @@ class TestClass public void F() { try { - } - catch (Exception e) { + } catch (Exception e) { } } }"); diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ConvertAnonymousDelegateToLambdaTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ConvertAnonymousDelegateToLambdaTests.cs index 7768848404..f1f81024a2 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ConvertAnonymousDelegateToLambdaTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ConvertAnonymousDelegateToLambdaTests.cs @@ -47,9 +47,9 @@ class A { void F () { - System.Action action = (i1, i2) => { - System.Console.WriteLine (i1); -}; + System.Action action = (i1, i2) => { + System.Console.WriteLine (i1); + }; } }"); } @@ -69,9 +69,9 @@ class A { void F () { - var action = (int i1, int i2) => { - System.Console.WriteLine (i1); -}; + var action = (int i1, int i2) => { + System.Console.WriteLine (i1); + }; } }"); } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ConvertConditionalToIfTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ConvertConditionalToIfTests.cs index d897580de6..ac075ddae3 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ConvertConditionalToIfTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ConvertConditionalToIfTests.cs @@ -239,10 +239,10 @@ class TestClass { int a; if (i < 10) - if (i > 0) - a = 0; - else - a = 1; + if (i > 0) + a = 0; + else + a = 1; } }"); } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ConvertLamdaToAnonymousDelegateTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ConvertLamdaToAnonymousDelegateTests.cs index b55d90dedc..ab23167efd 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ConvertLamdaToAnonymousDelegateTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ConvertLamdaToAnonymousDelegateTests.cs @@ -47,8 +47,8 @@ class A void F () { System.Action = delegate (int i1, int i2) { - System.Console.WriteLine (i1); -}; + System.Console.WriteLine (i1); + }; } }"); } @@ -69,8 +69,8 @@ class A void F () { System.Action = delegate (int i1, int i2) { - System.Console.WriteLine (i1); -}; + System.Console.WriteLine (i1); + }; } }"); } @@ -91,8 +91,8 @@ class A void F () { System.Action = delegate { - System.Console.WriteLine (); -}; + System.Console.WriteLine (); + }; } }"); } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ConvertSwitchToIfTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ConvertSwitchToIfTests.cs index 4a1a91e389..5f2bc4180b 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ConvertSwitchToIfTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ConvertSwitchToIfTests.cs @@ -61,11 +61,9 @@ class TestClass { if (a == 0) { return 0; - } else - if (a == 1 || a == 2) { + } else if (a == 1 || a == 2) { return 1; - } else - if (a == 3 || a == 4 || a == 5) { + } else if (a == 3 || a == 4 || a == 5) { return 1; } else { return 2; @@ -101,11 +99,9 @@ class TestClass { if (a == 0) { return 0; - } else - if (a == 1 || a == 2) { + } else if (a == 1 || a == 2) { return 1; - } else - if (a == 3 || a == 4 || a == 5) { + } else if (a == 3 || a == 4 || a == 5) { return 1; } } @@ -142,10 +138,8 @@ class TestClass { if (a == 0) { int b = 1; - } else - if (a == 1 || a == 2) { - } else - if (a == 3 || a == 4 || a == 5) { + } else if (a == 1 || a == 2) { + } else if (a == 3 || a == 4 || a == 5) { } else { } } @@ -176,8 +170,7 @@ class TestClass { if (a == 0) { return 0; - } else - if (a == (1 == 1 ? 1 : 2)) { + } else if (a == (1 == 1 ? 1 : 2)) { return 1; } else { return 2; diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/DeclareLocalVariableTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/DeclareLocalVariableTests.cs index e93b5f2395..5247afcf9d 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/DeclareLocalVariableTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/DeclareLocalVariableTests.cs @@ -206,7 +206,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions void DoStuff() { System.Func getInt = GetInt; - if (getInt() == 0) { + if (getInt () == 0) { } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ExtensionMethodInvocationToStaticMethodInvocationTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ExtensionMethodInvocationToStaticMethodInvocationTests.cs index 64a1b65802..48c94d88a4 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ExtensionMethodInvocationToStaticMethodInvocationTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/ExtensionMethodInvocationToStaticMethodInvocationTests.cs @@ -82,7 +82,7 @@ class C void F() { A a = new A(); - if(a.$Ext (1)) + if (a.$Ext (1)) return; } }", @" @@ -99,7 +99,7 @@ class C void F() { A a = new A(); - if(B.Ext (a, 1)) + if (B.Ext (a, 1)) return; } }"); diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/MoveToOuterScopeTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/MoveToOuterScopeTests.cs index 14a7c5b074..c9f40e5503 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/MoveToOuterScopeTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/MoveToOuterScopeTests.cs @@ -54,13 +54,13 @@ class A public void SimpleCase() { TestStatements(@" -while (true) { - int $i = 2; -} + while (true) { + int $i = 2; + } ", @" -int i = 2; -while (true) { -} + int i = 2; + while (true) { + } "); } @@ -81,14 +81,14 @@ class A public void MovesOnlyTheCurrentVariableInitialization() { TestStatements(@" -while (true) { - int $i = 2, j = 3; -} + while (true) { + int $i = 2, j = 3; + } ", @" -int i = 2; -while (true) { - int j = 3; -} + int i = 2; + while (true) { + int j = 3; + } "); } @@ -96,13 +96,13 @@ while (true) { public void MovesAllInitializersWhenOnType() { TestStatements(@" -while (true) { - i$nt i = 2, j = 3; -} + while (true) { + i$nt i = 2, j = 3; + } ", @" -int i = 2, j = 3; -while (true) { -} + int i = 2, j = 3; + while (true) { + } "); } @@ -110,16 +110,16 @@ while (true) { public void OnlyMovesDeclarationWhenInitializerDependsOnOtherStatements() { TestStatements(@" -while (true) { - int i = 2; - int j$ = i; -} + while (true) { + int i = 2; + int j$ = i; + } ", @" -int j; -while (true) { - int i = 2; - j = i; -} + int j; + while (true) { + int i = 2; + j = i; + } "); } @@ -127,13 +127,13 @@ while (true) { public void HandlesLambdaDelegate() { TestStatements(@" -var action = new Action(i => { - int j$ = 2; -}); + var action = new Action(i => { + int j$ = 2; + }); ", @" -int j = 2; -var action = new Action(i => { -}); + int j = 2; + var action = new Action(i => { + }); "); } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/RemoveRedundantCatchTypeTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/RemoveRedundantCatchTypeTests.cs index 492bbd21c9..932ef86923 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/RemoveRedundantCatchTypeTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/RemoveRedundantCatchTypeTests.cs @@ -52,8 +52,7 @@ class TestClass public void F() { try { - } - catch { + } catch { } } }"); @@ -79,8 +78,7 @@ class TestClass public void F() { try { - } - catch { + } catch { System.Console.WriteLine (""Hi""); } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/SplitDeclarationAndAssignmentTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/SplitDeclarationAndAssignmentTests.cs index cc2a6a488a..b0eb3566e9 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/SplitDeclarationAndAssignmentTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/SplitDeclarationAndAssignmentTests.cs @@ -100,7 +100,8 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions " void Test ()" + Environment.NewLine + " {" + Environment.NewLine + " int i;" + Environment.NewLine + - " for (i = 1; i < 10; i++) {}" + Environment.NewLine + + " for (i = 1; i < 10; i++) {" + Environment.NewLine + + " }" + Environment.NewLine + " }" + Environment.NewLine + "}", result); } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/AccessToModifiedClosureTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/AccessToModifiedClosureTests.cs index d73cd6b156..1aa87dee80 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/AccessToModifiedClosureTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/AccessToModifiedClosureTests.cs @@ -403,7 +403,7 @@ class TestClass foreach (var i in a) { var f = new System.Func (x => { var f2 = new System.Func (y => y - i); - return f2(x) + i; + return f2 (x) + i; }); } } @@ -417,7 +417,7 @@ class TestClass var i1 = i; var f = new System.Func (x => { var f2 = new System.Func (y => y - i1); - return f2(x) + i1; + return f2 (x) + i1; }); } } @@ -432,7 +432,7 @@ class TestClass var f = new System.Func (x => { var i1 = i; var f2 = new System.Func (y => y - i1); - return f2(x) + i; + return f2 (x) + i; }); } } @@ -579,7 +579,7 @@ class TestClass { void TestMethod2 (int b, System.Func a) { - TestMethod2 (b++, c => c + b); + TestMethod2 (b++, c => c + b); } }"; var input2 = @" @@ -587,7 +587,7 @@ class TestClass { void TestMethod3 (System.Func a, int b) { - TestMethod3 (c => c + b, b++); + TestMethod3 (c => c + b, b++); } }"; var output2 = @" @@ -596,7 +596,7 @@ class TestClass void TestMethod3 (System.Func a, int b) { var b1 = b; - TestMethod3 (c => c + b1, b++); + TestMethod3 (c => c + b1, b++); } }"; Test (input1, 0); diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ConstantConditionIssueTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ConstantConditionIssueTests.cs index 9578343015..17fd361b62 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ConstantConditionIssueTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ConstantConditionIssueTests.cs @@ -114,7 +114,8 @@ class TestClass { void TestMethod () { - for (int i = 0; true; i++) ; + for (int i = 0; true; i++) + ; } }"; Test (input, 1, output); @@ -128,7 +129,8 @@ class TestClass { void TestMethod () { - while (1 > 0) ; + while (1 > 0) + ; } }"; var output = @" @@ -136,7 +138,8 @@ class TestClass { void TestMethod () { - while (true) ; + while (true) + ; } }"; Test (input, 1, output); diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ExpressionIsAlwaysOfProvidedTypeIssueTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ExpressionIsAlwaysOfProvidedTypeIssueTests.cs index 3bb9c8f750..bd7db047ff 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ExpressionIsAlwaysOfProvidedTypeIssueTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ExpressionIsAlwaysOfProvidedTypeIssueTests.cs @@ -39,7 +39,8 @@ class TestClass { void TestMethod (" + variableType + @" x) { - if (x is " + providedType + @") ; + if (x is " + providedType + @") + ; } }"; var output = @" @@ -47,7 +48,8 @@ class TestClass { void TestMethod (" + variableType + @" x) { - if (x != null) ; + if (x != null) + ; } }"; Test (input, 1, output); @@ -73,7 +75,8 @@ class TestClass { void TestMethod (T x) where T : TestClass { - if (x is TestClass) ; + if (x is TestClass) + ; } }"; var output = @" @@ -81,7 +84,8 @@ class TestClass { void TestMethod (T x) where T : TestClass { - if (x != null) ; + if (x != null) + ; } }"; Test (input, 1, output); diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/InspectionActionTestBase.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/InspectionActionTestBase.cs index 88cb013619..465bce52cb 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/InspectionActionTestBase.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/InspectionActionTestBase.cs @@ -59,6 +59,9 @@ namespace ICSharpCode.NRefactory.CSharp.CodeIssues } bool pass = expectedOutput == ctx.Text; if (!pass) { + Console.WriteLine ("expected:"); + Console.WriteLine (expectedOutput); + Console.WriteLine ("got:"); Console.WriteLine (ctx.Text); } Assert.AreEqual (expectedOutput, ctx.Text); diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/RedundantToStringTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/RedundantToStringTests.cs index 7925e405c9..570db1b618 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/RedundantToStringTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/RedundantToStringTests.cs @@ -140,7 +140,7 @@ class Foo { void Bar (int i) { - string s = string.Format(""{0}"", i); + string s = string.Format (""{0}"", i); } }"); } @@ -167,7 +167,7 @@ class Foo void Bar (int i) { string format = ""{0}""; - string s = string.Format(format, i); + string s = string.Format (format, i); } }"); } @@ -196,7 +196,7 @@ class Foo { void Bar (int i) { - string s = FakeFormat(""{0} {1}"", i.ToString(), i); + string s = FakeFormat (""{0} {1}"", i.ToString (), i); } void FakeFormat(string format, string arg0, object arg1) @@ -229,7 +229,7 @@ class Foo { void Bar (int i) { - string s = FakeFormat(""{0} {1}"", i, i); + string s = FakeFormat (""{0} {1}"", i, i); } void FakeFormat(string format, params object[] args) @@ -261,8 +261,8 @@ class Foo void Bar (int i) { var w = new System.IO.StringWriter(); - w.Write(i); - w.WriteLine(i); + w.Write (i); + w.WriteLine (i); } }"); } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ReferenceToStaticMemberViaDerivedTypeTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ReferenceToStaticMemberViaDerivedTypeTests.cs index 1deccbf2b4..c46e2b872e 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ReferenceToStaticMemberViaDerivedTypeTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeIssues/ReferenceToStaticMemberViaDerivedTypeTests.cs @@ -46,7 +46,7 @@ class C { void Main() { - B.F(); + B.F (); } }"; TestRefactoringContext context; @@ -64,7 +64,7 @@ class C { void Main() { - A.F(); + A.F (); } }" ); @@ -181,7 +181,7 @@ class D { void Main() { - A.B.F(); + A.B.F (); } }" ); @@ -205,7 +205,7 @@ namespace Second { void Main() { - B.F(); + B.F (); } } }"; @@ -229,7 +229,7 @@ namespace Second { void Main() { - First.A.F(); + First.A.F (); } } }" diff --git a/ICSharpCode.NRefactory.Tests/FormattingTests/TextEditorTestAdapter.cs b/ICSharpCode.NRefactory.Tests/FormattingTests/TextEditorTestAdapter.cs index ad256d7b6b..6d0efcd797 100644 --- a/ICSharpCode.NRefactory.Tests/FormattingTests/TextEditorTestAdapter.cs +++ b/ICSharpCode.NRefactory.Tests/FormattingTests/TextEditorTestAdapter.cs @@ -48,6 +48,9 @@ namespace ICSharpCode.NRefactory.CSharp.FormattingTests expectedOutput = NormalizeNewlines(expectedOutput); IDocument doc = GetResult(policy, input, mode); if (expectedOutput != doc.Text) { + Console.WriteLine ("expected:"); + Console.WriteLine (expectedOutput); + Console.WriteLine ("got:"); Console.WriteLine (doc.Text); } Assert.AreEqual (expectedOutput, doc.Text); From b5ad2882caecaf14f36f6e7a1746b321d1fd83e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Thu, 13 Sep 2012 17:39:19 +0200 Subject: [PATCH 26/37] [AST] Handled protected and/or internal on AST level. --- .../Ast/CSharpModifierToken.cs | 4 +- .../Ast/Modifiers.cs | 6 ++- .../Ast/TypeMembers/EntityDeclaration.cs | 37 ++++++++++++++++--- .../Parser/CSharpParser.cs | 8 ++++ .../Refactoring/TypeSystemAstBuilder.cs | 3 +- .../TypeSystem/TypeSystemConvertVisitor.cs | 6 ++- .../CodeActions/DeclareLocalVariableTests.cs | 2 +- .../TypeMembers/FieldDeclarationTests.cs | 36 +++++++++++++++--- 8 files changed, 84 insertions(+), 18 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Ast/CSharpModifierToken.cs b/ICSharpCode.NRefactory.CSharp/Ast/CSharpModifierToken.cs index ac59c72460..8236be4cfd 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/CSharpModifierToken.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/CSharpModifierToken.cs @@ -61,7 +61,7 @@ namespace ICSharpCode.NRefactory.CSharp // Not worth using a dictionary for such few elements. // This table is sorted in the order that modifiers should be output when generating code. static readonly Modifiers[] allModifiers = { - Modifiers.Public, Modifiers.Protected, Modifiers.Private, Modifiers.Internal, + Modifiers.Public, Modifiers.Internal, Modifiers.Protected, Modifiers.Private, Modifiers.ProtectedAndInternal, Modifiers.New, Modifiers.Unsafe, Modifiers.Abstract, Modifiers.Virtual, Modifiers.Sealed, Modifiers.Static, Modifiers.Override, @@ -89,6 +89,8 @@ namespace ICSharpCode.NRefactory.CSharp return "internal"; case Modifiers.Protected: return "protected"; + case Modifiers.ProtectedAndInternal: + return "internal protected"; case Modifiers.Public: return "public"; case Modifiers.Abstract: diff --git a/ICSharpCode.NRefactory.CSharp/Ast/Modifiers.cs b/ICSharpCode.NRefactory.CSharp/Ast/Modifiers.cs index eb320495c3..9809cbda52 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/Modifiers.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/Modifiers.cs @@ -54,8 +54,10 @@ namespace ICSharpCode.NRefactory.CSharp Volatile = 0x4000, Unsafe = 0x8000, Async = 0x10000, - - VisibilityMask = Private | Internal | Protected | Public, + + ProtectedOrInternal = Protected | Internal, + ProtectedAndInternal = 0x20000, + VisibilityMask = Private | Internal | Protected | Public | ProtectedAndInternal, /// /// Special value used to match any modifiers during pattern matching. diff --git a/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EntityDeclaration.cs b/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EntityDeclaration.cs index ca8f69338d..0e7c8bae23 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EntityDeclaration.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EntityDeclaration.cs @@ -76,7 +76,11 @@ namespace ICSharpCode.NRefactory.CSharp { Modifiers m = 0; foreach (CSharpModifierToken t in node.GetChildrenByRole (ModifierRole)) { - m |= t.Modifier; + if (t.Modifier == Modifiers.Internal && m.HasFlag (Modifiers.Protected)) { + m = (m & ~Modifiers.Protected) | Modifiers.ProtectedAndInternal; + } else { + m |= t.Modifier; + } } return m; } @@ -89,17 +93,38 @@ namespace ICSharpCode.NRefactory.CSharp if ((m & newValue) != 0) { if ((m & oldValue) == 0) { // Modifier was added - var newToken = new CSharpModifierToken(TextLocation.Empty, m); - node.InsertChildAfter(insertionPos, newToken, ModifierRole); - insertionPos = newToken; + if (m == Modifiers.ProtectedAndInternal) { + // This modifier is a special case - it consists out of 2 tokens. + // note that protected or internal is handled by the ordering of the AllModifiers array. + var newToken = new CSharpModifierToken(TextLocation.Empty, Modifiers.Protected); + node.InsertChildAfter(insertionPos, newToken, ModifierRole); + insertionPos = newToken; + + newToken = new CSharpModifierToken(TextLocation.Empty, Modifiers.Internal); + node.InsertChildAfter(insertionPos, newToken, ModifierRole); + insertionPos = newToken; + } else { + var newToken = new CSharpModifierToken(TextLocation.Empty, m); + node.InsertChildAfter(insertionPos, newToken, ModifierRole); + insertionPos = newToken; + } } else { // Modifier already exists - insertionPos = node.GetChildrenByRole(ModifierRole).First(t => t.Modifier == m); + if (m == Modifiers.ProtectedAndInternal) { + insertionPos = node.GetChildrenByRole(ModifierRole).First(t => t.Modifier == Modifiers.Internal); + } else { + insertionPos = node.GetChildrenByRole(ModifierRole).First(t => t.Modifier == m); + } } } else { if ((m & oldValue) != 0) { // Modifier was removed - node.GetChildrenByRole (ModifierRole).First(t => t.Modifier == m).Remove(); + if (m == Modifiers.ProtectedAndInternal) { + node.GetChildrenByRole (ModifierRole).First(t => t.Modifier == Modifiers.Protected).Remove(); + node.GetChildrenByRole (ModifierRole).First(t => t.Modifier == Modifiers.Internal).Remove(); + } else { + node.GetChildrenByRole (ModifierRole).First(t => t.Modifier == m).Remove(); + } } } } diff --git a/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs b/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs index 2ac9f86e4f..ca48ead9bb 100644 --- a/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs +++ b/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs @@ -1093,14 +1093,22 @@ namespace ICSharpCode.NRefactory.CSharp { if (location == null || location.Modifiers == null) return; + Modifiers entityModifierMask = Modifiers.None; foreach (var modifier in location.Modifiers) { ICSharpCode.NRefactory.CSharp.Modifiers mod; if (!modifierTable.TryGetValue (modifier.Item1, out mod)) { Console.WriteLine ("modifier " + modifier.Item1 + " can't be converted,"); } + + if (mod == Modifiers.Internal && entityModifierMask.HasFlag (Modifiers.Protected)) { + entityModifierMask = (entityModifierMask & ~Modifiers.Protected) | Modifiers.ProtectedAndInternal; + } else { + entityModifierMask |= mod; + } parent.AddChild (new CSharpModifierToken (Convert (modifier.Item2), mod), EntityDeclaration.ModifierRole); } + parent.Modifiers |= entityModifierMask; } public override void Visit (Property p) diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/TypeSystemAstBuilder.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/TypeSystemAstBuilder.cs index 12faf6e8ab..a4c1bd4a27 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/TypeSystemAstBuilder.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/TypeSystemAstBuilder.cs @@ -753,8 +753,9 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring case Accessibility.Internal: return Modifiers.Internal; case Accessibility.ProtectedOrInternal: + return Modifiers.ProtectedOrInternal; case Accessibility.ProtectedAndInternal: - return Modifiers.Protected | Modifiers.Internal; + return Modifiers.ProtectedAndInternal; default: return Modifiers.None; } diff --git a/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeSystemConvertVisitor.cs b/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeSystemConvertVisitor.cs index cfc9e421f3..81e8377d21 100644 --- a/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeSystemConvertVisitor.cs +++ b/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeSystemConvertVisitor.cs @@ -829,10 +829,12 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem switch (modifiers & Modifiers.VisibilityMask) { case Modifiers.Private: return Accessibility.Private; + case Modifiers.ProtectedOrInternal: // Modifiers.Protected | Modifiers.Internal + return Accessibility.ProtectedOrInternal; + case Modifiers.ProtectedAndInternal: + return Accessibility.ProtectedAndInternal; case Modifiers.Internal: return Accessibility.Internal; - case Modifiers.Protected | Modifiers.Internal: - return Accessibility.ProtectedOrInternal; case Modifiers.Protected: return Accessibility.Protected; case Modifiers.Public: diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/DeclareLocalVariableTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/DeclareLocalVariableTests.cs index 5247afcf9d..e93b5f2395 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/DeclareLocalVariableTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/DeclareLocalVariableTests.cs @@ -206,7 +206,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions void DoStuff() { System.Func getInt = GetInt; - if (getInt () == 0) { + if (getInt() == 0) { } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/FieldDeclarationTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/FieldDeclarationTests.cs index ab851606ab..513e4dcb69 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/FieldDeclarationTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/FieldDeclarationTests.cs @@ -30,9 +30,9 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.TypeMembers ParseUtilCSharp.AssertTypeMember( "int[,,,] myField;", new FieldDeclaration { - ReturnType = new PrimitiveType("int").MakeArrayType(4), - Variables = { new VariableInitializer("myField") } - }); + ReturnType = new PrimitiveType("int").MakeArrayType(4), + Variables = { new VariableInitializer("myField") } + }); } [Test] @@ -77,8 +77,8 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.TypeMembers ParseUtilCSharp.AssertTypeMember( "public unsafe fixed int Field[100];", new FixedFieldDeclaration() { - Modifiers = Modifiers.Public | Modifiers.Unsafe, - ReturnType = new PrimitiveType("int"), + Modifiers = Modifiers.Public | Modifiers.Unsafe, + ReturnType = new PrimitiveType("int"), Variables = { new FixedVariableInitializer { Name = "Field", @@ -87,5 +87,31 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.TypeMembers } }); } + + [Test] + public void InternalAndProtectedFieldTest() + { + ParseUtilCSharp.AssertTypeMember( + "protected internal int myField;", + new FieldDeclaration { + Modifiers = Modifiers.ProtectedAndInternal, + ReturnType = new PrimitiveType("int"), + Variables = { new VariableInitializer("myField") } + } + ); + } + + [Test] + public void InternalOrProtectedFieldTest() + { + ParseUtilCSharp.AssertTypeMember( + "internal protected int myField;", + new FieldDeclaration { + Modifiers = Modifiers.ProtectedOrInternal, + ReturnType = new PrimitiveType("int"), + Variables = { new VariableInitializer("myField") } + } + ); + } } } From ab6f86fa9c8cad75a91af0d44a33e158aa14024f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Thu, 13 Sep 2012 18:19:01 +0200 Subject: [PATCH 27/37] [TypeSystem] GetClassTypeReference: Fixed CurrentAssembly == null case. --- .../TypeSystem/Implementation/GetClassTypeReference.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/GetClassTypeReference.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/GetClassTypeReference.cs index 527eac9972..666b17f4b3 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/GetClassTypeReference.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/GetClassTypeReference.cs @@ -100,7 +100,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } if (type == null) { var compilation = context.Compilation; - foreach (var asm in new[] { context.CurrentAssembly }.Concat(compilation.Assemblies)) { + foreach (var asm in (context.CurrentAssembly != null ? new[] { context.CurrentAssembly }.Concat(compilation.Assemblies) : compilation.Assemblies)) { type = asm.GetTypeDefinition(fullTypeName); if (type != null) break; From 359bb7b848ebfad456bfdbf7d50eaf3d963f9140 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 13 Sep 2012 23:50:33 +0200 Subject: [PATCH 28/37] GetClassTypeReference: remove redundant code --- .../TypeSystem/Implementation/GetClassTypeReference.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ICSharpCode.NRefactory/TypeSystem/Implementation/GetClassTypeReference.cs b/ICSharpCode.NRefactory/TypeSystem/Implementation/GetClassTypeReference.cs index 666b17f4b3..011b5635bc 100644 --- a/ICSharpCode.NRefactory/TypeSystem/Implementation/GetClassTypeReference.cs +++ b/ICSharpCode.NRefactory/TypeSystem/Implementation/GetClassTypeReference.cs @@ -100,7 +100,7 @@ namespace ICSharpCode.NRefactory.TypeSystem.Implementation } if (type == null) { var compilation = context.Compilation; - foreach (var asm in (context.CurrentAssembly != null ? new[] { context.CurrentAssembly }.Concat(compilation.Assemblies) : compilation.Assemblies)) { + foreach (var asm in compilation.Assemblies) { type = asm.GetTypeDefinition(fullTypeName); if (type != null) break; From 89ef0aef0e5f72b68f441c43cf7c5b2c6e0c0789 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 14 Sep 2012 00:02:07 +0200 Subject: [PATCH 29/37] Add type system accessibility tests. --- .../TypeSystem/TypeSystemTests.TestCase.cs | 10 ++++++++++ .../TypeSystem/TypeSystemTests.cs | 13 +++++++++++++ 2 files changed, 23 insertions(+) diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs index 34cdac5d68..a924905106 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.TestCase.cs @@ -332,4 +332,14 @@ namespace ICSharpCode.NRefactory.TypeSystem.TestCase public void Foo (int? bar = 42) { } } + public class AccessibilityTest + { + public void Public() {} + internal void Internal() {} + protected internal void ProtectedInternal() {} + internal protected void InternalProtected() {} + protected void Protected() {} + private void Private() {} + void None() {} + } } diff --git a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs index 40c1d00c59..95c5d0881d 100644 --- a/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs +++ b/ICSharpCode.NRefactory.Tests/TypeSystem/TypeSystemTests.cs @@ -1213,5 +1213,18 @@ namespace ICSharpCode.NRefactory.TypeSystem var method = type.GetMethods ().Single (m => m.Name == "Foo"); Assert.AreEqual(42, method.Parameters.Single ().ConstantValue); } + + [Test] + public void AccessibilityTests() + { + ITypeDefinition type = GetTypeDefinition(typeof(AccessibilityTest)); + Assert.AreEqual(Accessibility.Public, type.Methods.Single(m => m.Name == "Public").Accessibility); + Assert.AreEqual(Accessibility.Internal, type.Methods.Single(m => m.Name == "Internal").Accessibility); + Assert.AreEqual(Accessibility.ProtectedOrInternal, type.Methods.Single(m => m.Name == "ProtectedInternal").Accessibility); + Assert.AreEqual(Accessibility.ProtectedOrInternal, type.Methods.Single(m => m.Name == "InternalProtected").Accessibility); + Assert.AreEqual(Accessibility.Protected, type.Methods.Single(m => m.Name == "Protected").Accessibility); + Assert.AreEqual(Accessibility.Private, type.Methods.Single(m => m.Name == "Private").Accessibility); + Assert.AreEqual(Accessibility.Private, type.Methods.Single(m => m.Name == "None").Accessibility); + } } } From f558b300e7ec40343e4256d49516ca57f0202b85 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 14 Sep 2012 00:02:25 +0200 Subject: [PATCH 30/37] Revert "[AST] Handled protected and/or internal on AST level." "protected internal" and "internal protected" are the same thing in C#. Both map to ProtectedOrInternal; the ProtectedAndInternal accessibility is not usable from C#. This reverts commit b5ad2882caecaf14f36f6e7a1746b321d1fd83e3. --- .../Ast/CSharpModifierToken.cs | 4 +- .../Ast/Modifiers.cs | 6 +-- .../Ast/TypeMembers/EntityDeclaration.cs | 37 +++---------------- .../Parser/CSharpParser.cs | 8 ---- .../Refactoring/TypeSystemAstBuilder.cs | 3 +- .../TypeSystem/TypeSystemConvertVisitor.cs | 6 +-- .../CodeActions/DeclareLocalVariableTests.cs | 2 +- .../TypeMembers/FieldDeclarationTests.cs | 36 +++--------------- 8 files changed, 18 insertions(+), 84 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Ast/CSharpModifierToken.cs b/ICSharpCode.NRefactory.CSharp/Ast/CSharpModifierToken.cs index 8236be4cfd..ac59c72460 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/CSharpModifierToken.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/CSharpModifierToken.cs @@ -61,7 +61,7 @@ namespace ICSharpCode.NRefactory.CSharp // Not worth using a dictionary for such few elements. // This table is sorted in the order that modifiers should be output when generating code. static readonly Modifiers[] allModifiers = { - Modifiers.Public, Modifiers.Internal, Modifiers.Protected, Modifiers.Private, Modifiers.ProtectedAndInternal, + Modifiers.Public, Modifiers.Protected, Modifiers.Private, Modifiers.Internal, Modifiers.New, Modifiers.Unsafe, Modifiers.Abstract, Modifiers.Virtual, Modifiers.Sealed, Modifiers.Static, Modifiers.Override, @@ -89,8 +89,6 @@ namespace ICSharpCode.NRefactory.CSharp return "internal"; case Modifiers.Protected: return "protected"; - case Modifiers.ProtectedAndInternal: - return "internal protected"; case Modifiers.Public: return "public"; case Modifiers.Abstract: diff --git a/ICSharpCode.NRefactory.CSharp/Ast/Modifiers.cs b/ICSharpCode.NRefactory.CSharp/Ast/Modifiers.cs index 9809cbda52..eb320495c3 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/Modifiers.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/Modifiers.cs @@ -54,10 +54,8 @@ namespace ICSharpCode.NRefactory.CSharp Volatile = 0x4000, Unsafe = 0x8000, Async = 0x10000, - - ProtectedOrInternal = Protected | Internal, - ProtectedAndInternal = 0x20000, - VisibilityMask = Private | Internal | Protected | Public | ProtectedAndInternal, + + VisibilityMask = Private | Internal | Protected | Public, /// /// Special value used to match any modifiers during pattern matching. diff --git a/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EntityDeclaration.cs b/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EntityDeclaration.cs index 0e7c8bae23..ca8f69338d 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EntityDeclaration.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/EntityDeclaration.cs @@ -76,11 +76,7 @@ namespace ICSharpCode.NRefactory.CSharp { Modifiers m = 0; foreach (CSharpModifierToken t in node.GetChildrenByRole (ModifierRole)) { - if (t.Modifier == Modifiers.Internal && m.HasFlag (Modifiers.Protected)) { - m = (m & ~Modifiers.Protected) | Modifiers.ProtectedAndInternal; - } else { - m |= t.Modifier; - } + m |= t.Modifier; } return m; } @@ -93,38 +89,17 @@ namespace ICSharpCode.NRefactory.CSharp if ((m & newValue) != 0) { if ((m & oldValue) == 0) { // Modifier was added - if (m == Modifiers.ProtectedAndInternal) { - // This modifier is a special case - it consists out of 2 tokens. - // note that protected or internal is handled by the ordering of the AllModifiers array. - var newToken = new CSharpModifierToken(TextLocation.Empty, Modifiers.Protected); - node.InsertChildAfter(insertionPos, newToken, ModifierRole); - insertionPos = newToken; - - newToken = new CSharpModifierToken(TextLocation.Empty, Modifiers.Internal); - node.InsertChildAfter(insertionPos, newToken, ModifierRole); - insertionPos = newToken; - } else { - var newToken = new CSharpModifierToken(TextLocation.Empty, m); - node.InsertChildAfter(insertionPos, newToken, ModifierRole); - insertionPos = newToken; - } + var newToken = new CSharpModifierToken(TextLocation.Empty, m); + node.InsertChildAfter(insertionPos, newToken, ModifierRole); + insertionPos = newToken; } else { // Modifier already exists - if (m == Modifiers.ProtectedAndInternal) { - insertionPos = node.GetChildrenByRole(ModifierRole).First(t => t.Modifier == Modifiers.Internal); - } else { - insertionPos = node.GetChildrenByRole(ModifierRole).First(t => t.Modifier == m); - } + insertionPos = node.GetChildrenByRole(ModifierRole).First(t => t.Modifier == m); } } else { if ((m & oldValue) != 0) { // Modifier was removed - if (m == Modifiers.ProtectedAndInternal) { - node.GetChildrenByRole (ModifierRole).First(t => t.Modifier == Modifiers.Protected).Remove(); - node.GetChildrenByRole (ModifierRole).First(t => t.Modifier == Modifiers.Internal).Remove(); - } else { - node.GetChildrenByRole (ModifierRole).First(t => t.Modifier == m).Remove(); - } + node.GetChildrenByRole (ModifierRole).First(t => t.Modifier == m).Remove(); } } } diff --git a/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs b/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs index ca48ead9bb..2ac9f86e4f 100644 --- a/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs +++ b/ICSharpCode.NRefactory.CSharp/Parser/CSharpParser.cs @@ -1093,22 +1093,14 @@ namespace ICSharpCode.NRefactory.CSharp { if (location == null || location.Modifiers == null) return; - Modifiers entityModifierMask = Modifiers.None; foreach (var modifier in location.Modifiers) { ICSharpCode.NRefactory.CSharp.Modifiers mod; if (!modifierTable.TryGetValue (modifier.Item1, out mod)) { Console.WriteLine ("modifier " + modifier.Item1 + " can't be converted,"); } - - if (mod == Modifiers.Internal && entityModifierMask.HasFlag (Modifiers.Protected)) { - entityModifierMask = (entityModifierMask & ~Modifiers.Protected) | Modifiers.ProtectedAndInternal; - } else { - entityModifierMask |= mod; - } parent.AddChild (new CSharpModifierToken (Convert (modifier.Item2), mod), EntityDeclaration.ModifierRole); } - parent.Modifiers |= entityModifierMask; } public override void Visit (Property p) diff --git a/ICSharpCode.NRefactory.CSharp/Refactoring/TypeSystemAstBuilder.cs b/ICSharpCode.NRefactory.CSharp/Refactoring/TypeSystemAstBuilder.cs index a4c1bd4a27..12faf6e8ab 100644 --- a/ICSharpCode.NRefactory.CSharp/Refactoring/TypeSystemAstBuilder.cs +++ b/ICSharpCode.NRefactory.CSharp/Refactoring/TypeSystemAstBuilder.cs @@ -753,9 +753,8 @@ namespace ICSharpCode.NRefactory.CSharp.Refactoring case Accessibility.Internal: return Modifiers.Internal; case Accessibility.ProtectedOrInternal: - return Modifiers.ProtectedOrInternal; case Accessibility.ProtectedAndInternal: - return Modifiers.ProtectedAndInternal; + return Modifiers.Protected | Modifiers.Internal; default: return Modifiers.None; } diff --git a/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeSystemConvertVisitor.cs b/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeSystemConvertVisitor.cs index 81e8377d21..cfc9e421f3 100644 --- a/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeSystemConvertVisitor.cs +++ b/ICSharpCode.NRefactory.CSharp/TypeSystem/TypeSystemConvertVisitor.cs @@ -829,12 +829,10 @@ namespace ICSharpCode.NRefactory.CSharp.TypeSystem switch (modifiers & Modifiers.VisibilityMask) { case Modifiers.Private: return Accessibility.Private; - case Modifiers.ProtectedOrInternal: // Modifiers.Protected | Modifiers.Internal - return Accessibility.ProtectedOrInternal; - case Modifiers.ProtectedAndInternal: - return Accessibility.ProtectedAndInternal; case Modifiers.Internal: return Accessibility.Internal; + case Modifiers.Protected | Modifiers.Internal: + return Accessibility.ProtectedOrInternal; case Modifiers.Protected: return Accessibility.Protected; case Modifiers.Public: diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/DeclareLocalVariableTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/DeclareLocalVariableTests.cs index e93b5f2395..5247afcf9d 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/DeclareLocalVariableTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeActions/DeclareLocalVariableTests.cs @@ -206,7 +206,7 @@ namespace ICSharpCode.NRefactory.CSharp.CodeActions void DoStuff() { System.Func getInt = GetInt; - if (getInt() == 0) { + if (getInt () == 0) { } } diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/FieldDeclarationTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/FieldDeclarationTests.cs index 513e4dcb69..ab851606ab 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/FieldDeclarationTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Parser/TypeMembers/FieldDeclarationTests.cs @@ -30,9 +30,9 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.TypeMembers ParseUtilCSharp.AssertTypeMember( "int[,,,] myField;", new FieldDeclaration { - ReturnType = new PrimitiveType("int").MakeArrayType(4), - Variables = { new VariableInitializer("myField") } - }); + ReturnType = new PrimitiveType("int").MakeArrayType(4), + Variables = { new VariableInitializer("myField") } + }); } [Test] @@ -77,8 +77,8 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.TypeMembers ParseUtilCSharp.AssertTypeMember( "public unsafe fixed int Field[100];", new FixedFieldDeclaration() { - Modifiers = Modifiers.Public | Modifiers.Unsafe, - ReturnType = new PrimitiveType("int"), + Modifiers = Modifiers.Public | Modifiers.Unsafe, + ReturnType = new PrimitiveType("int"), Variables = { new FixedVariableInitializer { Name = "Field", @@ -87,31 +87,5 @@ namespace ICSharpCode.NRefactory.CSharp.Parser.TypeMembers } }); } - - [Test] - public void InternalAndProtectedFieldTest() - { - ParseUtilCSharp.AssertTypeMember( - "protected internal int myField;", - new FieldDeclaration { - Modifiers = Modifiers.ProtectedAndInternal, - ReturnType = new PrimitiveType("int"), - Variables = { new VariableInitializer("myField") } - } - ); - } - - [Test] - public void InternalOrProtectedFieldTest() - { - ParseUtilCSharp.AssertTypeMember( - "internal protected int myField;", - new FieldDeclaration { - Modifiers = Modifiers.ProtectedOrInternal, - ReturnType = new PrimitiveType("int"), - Variables = { new VariableInitializer("myField") } - } - ); - } } } From 979151a7c274d605f424223b0021f0cba349efe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Fri, 14 Sep 2012 08:27:42 +0200 Subject: [PATCH 31/37] Added indexer parameter name test. --- .../CSharp/CodeCompletion/NameContextTests.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/NameContextTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/NameContextTests.cs index 70427c1187..bca62a6288 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/NameContextTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/NameContextTests.cs @@ -187,6 +187,20 @@ namespace ICSharpCode.NRefactory.CSharp.CodeCompletion Assert.AreEqual (0, provider.Count, "provider needs to be empty"); }); } + [Ignore("TODO")] + [Test()] + public void TestIndexerParameterName () + { + var provider = CodeCompletionBugTests.CreateProvider (@"class MyClass { + $public int this [int f$ +}"); + Assert.IsTrue (provider == null || provider.Count == 0, "provider should be empty."); + provider = CodeCompletionBugTests.CreateProvider (@"class MyClass { + $public int this [int f, string x$ +}"); + Assert.IsTrue (provider == null || provider.Count == 0, "provider should be empty."); + } + } } From 1ed352ca30bd28560ba4c26cb62df2c47cc7ab89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Fri, 14 Sep 2012 08:28:21 +0200 Subject: [PATCH 32/37] [Completion] Added a case to prevent recursive constructor calls: 'public A() : this()'. --- .../CSharpParameterCompletionEngine.cs | 46 +++++++++---------- .../IParameterCompletionDataFactory.cs | 5 ++ .../ParameterCompletionTests.cs | 8 ++++ 3 files changed, 36 insertions(+), 23 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs index 721c0f0a8f..bf61e27f7b 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpParameterCompletionEngine.cs @@ -120,7 +120,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } yield return method; } - + foreach (var extMethods in resolveResult.GetEligibleExtensionMethods (true)) { foreach (var method in extMethods) { yield return method; @@ -152,7 +152,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion if (invoke.Node is ConstructorInitializer) { var init = (ConstructorInitializer)invoke.Node; if (init.ConstructorInitializerType == ConstructorInitializerType.This) { - return factory.CreateConstructorProvider(document.GetOffset(invoke.Node.StartLocation), ctx.CurrentTypeDefinition); + return factory.CreateConstructorProvider(document.GetOffset(invoke.Node.StartLocation), ctx.CurrentTypeDefinition, init); } else { var baseType = ctx.CurrentTypeDefinition.DirectBaseTypes.FirstOrDefault(bt => bt.Kind != TypeKind.Interface); if (baseType == null) { @@ -167,7 +167,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion return null; return factory.CreateConstructorProvider(document.GetOffset(invoke.Node.StartLocation), createType.Item1.Type); } - + if (invoke.Node is ICSharpCode.NRefactory.CSharp.Attribute) { var attribute = ResolveExpression(invoke); if (attribute == null || attribute.Item1 == null) { @@ -189,22 +189,22 @@ namespace ICSharpCode.NRefactory.CSharp.Completion return factory.CreateMethodDataProvider(document.GetOffset(invoke.Node.StartLocation), new [] { (IMethod)mr.Member }); } } - + if (resolveResult.Type.Kind == TypeKind.Delegate) { return factory.CreateDelegateDataProvider(document.GetOffset(invoke.Node.StartLocation), resolveResult.Type); } - -// -// if (result.ExpressionContext == ExpressionContext.BaseConstructorCall) { -// if (resolveResult is ThisResolveResult) -// return new NRefactoryParameterDataProvider (textEditorData, resolver, resolveResult as ThisResolveResult); -// if (resolveResult is BaseResolveResult) -// return new NRefactoryParameterDataProvider (textEditorData, resolver, resolveResult as BaseResolveResult); -// } -// IType resolvedType = resolver.SearchType (resolveResult.ResolvedType); -// if (resolvedType != null && resolvedType.ClassType == ClassType.Delegate) { -// return new NRefactoryParameterDataProvider (textEditorData, result.Expression, resolvedType); -// } + + // + // if (result.ExpressionContext == ExpressionContext.BaseConstructorCall) { + // if (resolveResult is ThisResolveResult) + // return new NRefactoryParameterDataProvider (textEditorData, resolver, resolveResult as ThisResolveResult); + // if (resolveResult is BaseResolveResult) + // return new NRefactoryParameterDataProvider (textEditorData, resolver, resolveResult as BaseResolveResult); + // } + // IType resolvedType = resolver.SearchType (resolveResult.ResolvedType); + // if (resolvedType != null && resolvedType.ClassType == ClassType.Delegate) { + // return new NRefactoryParameterDataProvider (textEditorData, result.Expression, resolvedType); + // } break; case ',': invoke = GetInvocationBeforeCursor(true) ?? GetIndexerBeforeCursor(); @@ -217,7 +217,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion if (typeExpression == null || typeExpression.Item1 == null || typeExpression.Item1.IsError) { return null; } - + return factory.CreateTypeParameterDataProvider(document.GetOffset(invoke.Node.StartLocation), CollectAllTypes(typeExpression.Item1.Type)); } return null; @@ -228,7 +228,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion var createType = ResolveExpression(((ObjectCreateExpression)invoke.Node).Type); return factory.CreateConstructorProvider(document.GetOffset(invoke.Node.StartLocation), createType.Item1.Type); } - + if (invoke.Node is ICSharpCode.NRefactory.CSharp.Attribute) { var attribute = ResolveExpression(invoke); if (attribute == null || attribute.Item1 == null) { @@ -236,13 +236,13 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } return factory.CreateConstructorProvider(document.GetOffset(invoke.Node.StartLocation), attribute.Item1.Type); } - + invocationExpression = ResolveExpression(invoke); - + if (invocationExpression == null || invocationExpression.Item1 == null || invocationExpression.Item1.IsError) { return null; } - + resolveResult = invocationExpression.Item1; if (resolveResult is MethodGroupResolveResult) { return factory.CreateMethodDataProvider(document.GetOffset(invoke.Node.StartLocation), CollectMethods(invoke.Node, resolveResult as MethodGroupResolveResult)); @@ -269,7 +269,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion if (tExpr == null || tExpr.Item1 == null || tExpr.Item1.IsError) { return null; } - + return factory.CreateTypeParameterDataProvider(document.GetOffset(invoke.Node.StartLocation), CollectAllTypes(tExpr.Item1.Type)); case '[': invoke = GetIndexerBeforeCursor(); @@ -320,7 +320,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion return result; } - + } } diff --git a/ICSharpCode.NRefactory.CSharp/Completion/IParameterCompletionDataFactory.cs b/ICSharpCode.NRefactory.CSharp/Completion/IParameterCompletionDataFactory.cs index 54ebe1c84e..90ecab6261 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/IParameterCompletionDataFactory.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/IParameterCompletionDataFactory.cs @@ -35,6 +35,11 @@ namespace ICSharpCode.NRefactory.CSharp.Completion { IParameterDataProvider CreateConstructorProvider (int startOffset, IType type); + /// + /// Creates a constructor provider skipping the parent of thisInitializer. + /// + IParameterDataProvider CreateConstructorProvider (int startOffset, IType type, AstNode thisInitializer); + IParameterDataProvider CreateMethodDataProvider (int startOffset, IEnumerable methods); IParameterDataProvider CreateDelegateDataProvider (int startOffset, IType type); diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/ParameterCompletionTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/ParameterCompletionTests.cs index 85bfea561a..6cbfc5c57b 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/ParameterCompletionTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/ParameterCompletionTests.cs @@ -220,6 +220,14 @@ namespace ICSharpCode.NRefactory.CSharp.CodeCompletion }; } + public IParameterDataProvider CreateConstructorProvider(int startOffset, ICSharpCode.NRefactory.TypeSystem.IType type, AstNode skipNode) + { + Assert.IsTrue(type.Kind != TypeKind.Unknown); + return new Provider () { + Data = type.GetConstructors (m => m.Accessibility == Accessibility.Public) + }; + } + public IParameterDataProvider CreateMethodDataProvider (int startOffset, IEnumerable methods) { return new Provider () { From 47aa23e828ab41d524c60b45ef70a922a85b2657 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 14 Sep 2012 10:40:52 +0200 Subject: [PATCH 33/37] Add unit tests that check that a class hiding a field depends on the generics. --- .../CSharp/Resolver/MemberLookupTests.cs | 39 +++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/MemberLookupTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/MemberLookupTests.cs index 83b196f067..dc3159fae9 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/MemberLookupTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/MemberLookupTests.cs @@ -44,6 +44,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver CSharpUnresolvedFile unresolvedFile = syntaxTree.ToTypeSystem(); project = project.AddOrUpdateFiles(unresolvedFile); compilation = project.CreateCompilation(); + lookup = new MemberLookup(null, compilation.MainAssembly); return unresolvedFile; } @@ -61,7 +62,7 @@ class Derived : Middle { public override void Method() {} }"; var unresolvedFile = Parse(program); - ITypeDefinition derived = compilation.MainAssembly.GetTypeDefinition(unresolvedFile.TopLevelTypeDefinitions[2].FullTypeName); + ITypeDefinition derived = compilation.MainAssembly.GetTypeDefinition(new TopLevelTypeName("Derived")); var rr = lookup.Lookup(new ResolveResult(derived), "Method", EmptyList.Instance, true) as MethodGroupResolveResult; Assert.AreEqual(2, rr.MethodsGroupedByDeclaringType.Count()); @@ -88,7 +89,7 @@ class Derived : Base { public override void Method(string a) {} }"; var unresolvedFile = Parse(program); - ITypeDefinition derived = compilation.MainAssembly.GetTypeDefinition(unresolvedFile.TopLevelTypeDefinitions[1].FullTypeName); + ITypeDefinition derived = compilation.MainAssembly.GetTypeDefinition(new TopLevelTypeName("Derived")); var rr = lookup.Lookup(new ResolveResult(derived), "Method", EmptyList.Instance, true) as MethodGroupResolveResult; Assert.AreEqual(2, rr.MethodsGroupedByDeclaringType.Count()); @@ -116,7 +117,7 @@ class Derived : Base { public override void Method(S a) {} }"; var unresolvedFile = Parse(program); - ITypeDefinition derived = compilation.MainAssembly.GetTypeDefinition(unresolvedFile.TopLevelTypeDefinitions[1].FullTypeName); + ITypeDefinition derived = compilation.MainAssembly.GetTypeDefinition(new TopLevelTypeName("Derived")); var rr = lookup.Lookup(new ResolveResult(derived), "Method", EmptyList.Instance, true) as MethodGroupResolveResult; Assert.AreEqual(1, rr.MethodsGroupedByDeclaringType.Count()); @@ -456,5 +457,37 @@ class TestClass { var mrr = Resolve(program); Assert.AreEqual("TestClass.B", mrr.Member.FullName); } + + [Test] + public void GenericClassDoesNotHideField() + { + string program = @"using System; +class A { public int F; } +class B : A { public class F {} } +class C : B { + public void M() + { + $F$ = 1; + } +}"; + var mrr = Resolve(program); + Assert.AreEqual("A.F", mrr.Member.FullName); + } + + [Test] + public void NonGenericClassHidesField_WithExplicitThisAccess() + { + string program = @"using System; +class A { public int F; } +class B : A { public class F {} } +class C : B { + public void M() + { + $this.F$ = 1; + } +}"; + var trr = Resolve(program); + Assert.AreEqual("B+F", trr.Type.ReflectionName); + } } } From 6b977c69a7f840c2d15aeffd7b184886adc39b48 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 14 Sep 2012 10:41:04 +0200 Subject: [PATCH 34/37] Add MemberLookup.GetAccessibleMembers(). --- .../Resolver/MemberLookup.cs | 82 ++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/MemberLookup.cs b/ICSharpCode.NRefactory.CSharp/Resolver/MemberLookup.cs index f652ef4c1e..c490f9b72b 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/MemberLookup.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/MemberLookup.cs @@ -17,7 +17,6 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -141,6 +140,87 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver } #endregion + #region GetAccessibleMembers + /// + /// Retrieves all members that are accessible and not hidden (by being overridden or shadowed). + /// Returns both members and nested type definitions. Does not include extension methods. + /// + public IEnumerable GetAccessibleMembers(ResolveResult targetResolveResult) + { + if (targetResolveResult == null) + throw new ArgumentNullException("targetResolveResult"); + + bool targetIsTypeParameter = targetResolveResult.Type.Kind == TypeKind.TypeParameter; + bool allowProtectedAccess = (targetResolveResult is ThisResolveResult || IsProtectedAccessAllowed(targetResolveResult.Type)); + + // maps the member name to the list of lookup groups + var lookupGroupDict = new Dictionary>(); + + // This loop will handle base types before derived types. + // The loop performs three jobs: + // 1) It marks entries in lookup groups from base classes as removed when those members + // are hidden by a derived class. + // 2) It adds a new lookup group with the members from a declaring type. + // 3) It replaces virtual members with the overridden version, placing the override in the + // lookup group belonging to the base class. + foreach (IType type in targetResolveResult.Type.GetNonInterfaceBaseTypes()) { + + List entities = new List(); + entities.AddRange(type.GetMembers(options: GetMemberOptions.IgnoreInheritedMembers)); + if (!targetIsTypeParameter) + entities.AddRange(type.GetNestedTypes(options: GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions).Select(t => t.GetDefinition())); + + foreach (var entityGroup in entities.GroupBy(e => e.Name)) { + + List lookupGroups = new List(); + if (!lookupGroupDict.TryGetValue(entityGroup.Key, out lookupGroups)) + lookupGroupDict.Add(entityGroup.Key, lookupGroups = new List()); + + List newNestedTypes = null; + List newMethods = null; + IMember newNonMethod = null; + + IEnumerable typeBaseTypes = null; + + if (!targetIsTypeParameter) { + AddNestedTypes(type, entityGroup.OfType(), 0, lookupGroups, ref typeBaseTypes, ref newNestedTypes); + } + AddMembers(type, entityGroup.OfType(), allowProtectedAccess, lookupGroups, false, ref typeBaseTypes, ref newMethods, ref newNonMethod); + + if (newNestedTypes != null || newMethods != null || newNonMethod != null) + lookupGroups.Add(new LookupGroup(type, newNestedTypes, newMethods, newNonMethod)); + } + } + + foreach (List lookupGroups in lookupGroupDict.Values) { + // Remove interface members hidden by class members. + if (targetIsTypeParameter) { + // This can happen only with type parameters. + RemoveInterfaceMembersHiddenByClassMembers(lookupGroups); + } + + // Now report the results: + foreach (LookupGroup lookupGroup in lookupGroups) { + if (!lookupGroup.MethodsAreHidden) { + foreach (IMethod method in lookupGroup.Methods) { + yield return method; + } + } + if (!lookupGroup.NonMethodIsHidden) { + yield return lookupGroup.NonMethod; + } + if (lookupGroup.NestedTypes != null) { + foreach (IType type in lookupGroup.NestedTypes) { + ITypeDefinition typeDef = type.GetDefinition(); + if (typeDef != null) + yield return typeDef; + } + } + } + } + } + #endregion + #region class LookupGroup sealed class LookupGroup { From eb2d78e29a8ad83ec4b12a6232c2142c39701837 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Fri, 14 Sep 2012 11:49:18 +0200 Subject: [PATCH 35/37] Used new GetAccessibleMembers method in code completion. --- .../Completion/CSharpCompletionEngine.cs | 45 +++++------ .../CodeCompletionAccessibleTests.cs | 76 +++++++++++++++++++ .../CodeCompletion/CodeCompletionBugTests.cs | 15 +++- 3 files changed, 107 insertions(+), 29 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs index 575d39785f..d090e8f102 100644 --- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs +++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs @@ -2296,7 +2296,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion var lookup = new MemberLookup( ctx.CurrentTypeDefinition, Compilation.MainAssembly - ); + ); if (resolveResult is NamespaceResolveResult) { var nr = (NamespaceResolveResult)resolveResult; @@ -2315,10 +2315,9 @@ namespace ICSharpCode.NRefactory.CSharp.Completion } return namespaceContents.Result; } - IType type = resolveResult.Type; if (resolvedNode.Parent is PointerReferenceExpression && (type is PointerType)) { - type = ((PointerType)type).ElementType; + resolveResult = new OperatorResolveResult (((PointerType)type).ElementType, System.Linq.Expressions.ExpressionType.Extension, resolveResult); } //var typeDef = resolveResult.Type.GetDefinition(); @@ -2352,7 +2351,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion resolveResult, ((IdentifierExpression)resolvedNode).Identifier, out trr - )) { + )) { if (currentMember != null && mrr.Member.IsStatic ^ currentMember.IsStatic) { skipNonStaticMembers = true; @@ -2396,20 +2395,21 @@ namespace ICSharpCode.NRefactory.CSharp.Completion if (resolvedNode.Annotation() == null) { //tags the created expression as part of an object create expression. - + /* var filteredList = new List(); foreach (var member in type.GetMembers ()) { + filteredList.Add(member); + } + + foreach (var member in filteredList) { + // Console.WriteLine ("add:" + member + "/" + member.IsStatic); + result.AddMember(member); + }*/ + foreach (var member in lookup.GetAccessibleMembers (resolveResult)) { + if (member.EntityType == EntityType.Indexer || member.EntityType == EntityType.Operator || member.EntityType == EntityType.Constructor || member.EntityType == EntityType.Destructor) { continue; } - if (member.IsExplicitInterfaceImplementation) { - continue; - } - // Console.WriteLine ("member:" + member + member.IsShadowing); - if (!lookup.IsAccessible(member, isProtectedAllowed)) { - // Console.WriteLine ("skip access: " + member.FullName); - continue; - } if (resolvedNode is BaseReferenceExpression && member.IsAbstract) { continue; } @@ -2433,18 +2433,13 @@ namespace ICSharpCode.NRefactory.CSharp.Completion if (member.EntityType == EntityType.Operator) { continue; } - if (member.IsExplicitInterfaceImplementation) { - continue; - } - if (member.IsShadowing) { - filteredList.RemoveAll(m => m.Name == member.Name); + + if (member is IMember) { + result.AddMember ((IMember)member); + } else { + if (resolveResult is TypeResolveResult || includeStaticMembers) + result.AddType ((IType)member, member.Name); } - filteredList.Add(member); - } - - foreach (var member in filteredList) { - // Console.WriteLine ("add:" + member + "/" + member.IsStatic); - result.AddMember(member); } } @@ -2490,7 +2485,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion return result.Result; } - + IEnumerable CreateCaseCompletionData(TextLocation location) { var unit = ParseStub("a: break;"); diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionAccessibleTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionAccessibleTests.cs index 409112a298..d7b4b29013 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionAccessibleTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionAccessibleTests.cs @@ -29,6 +29,7 @@ using System; using NUnit.Framework; using System.Diagnostics; +using System.Linq; namespace ICSharpCode.NRefactory.CSharp.CodeCompletion { @@ -1345,5 +1346,80 @@ public class Foo Assert.IsNotNull (provider.Find ("Bar"), "'Bar' not found."); }); } + + [Test()] + public void TestImplicitShadowing () + { + CodeCompletionBugTests.CombinedProviderTest (@" +using System; + + namespace ConsoleApplication2 + { + class A + { + public int Foo; + } + + class B : A + { + public string Foo; + } + + class Program + { + static void Main (string[] args) + { + var b = new B (); + $b.$ + } + } + }", provider => { + int count = 0; + foreach (var data in provider.Data) + if (data.DisplayText == "Foo") + count += data.HasOverloads ? data.OverloadedData.Count () : 1; + Assert.AreEqual (1, count); + }); + } + + [Test()] + public void TestOverrideFiltering () + { + CodeCompletionBugTests.CombinedProviderTest (@" +using System; + +namespace ConsoleApplication2 +{ + class A + { + public virtual int Foo { set {} } + } + + class B : A + { + public override int Foo { + set { + base.Foo = value; + } + } + } + + class Program + { + static void Main (string[] args) + { + var b = new B (); + $b.$ + } + } +} +", provider => { + int count = 0; + foreach (var data in provider.Data) + if (data.DisplayText == "Foo") + count += data.HasOverloads ? data.OverloadedData.Count () : 1; + Assert.AreEqual (1, count); + }); + } } } \ No newline at end of file diff --git a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs index cdb5b8fa42..ae18efe4cd 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/CodeCompletion/CodeCompletionBugTests.cs @@ -74,6 +74,9 @@ namespace ICSharpCode.NRefactory.CSharp.CodeCompletion #region ICompletionData implementation public void AddOverload (ICompletionData data) { + if (overloadedData.Count == 0) + overloadedData.Add (this); + overloadedData.Add (data); } public CompletionCategory CompletionCategory { @@ -103,13 +106,17 @@ namespace ICSharpCode.NRefactory.CSharp.CodeCompletion public bool HasOverloads { get { - throw new NotImplementedException (); + return overloadedData.Count > 0; } } - + List overloadedData = new List (); public System.Collections.Generic.IEnumerable OverloadedData { - get; - set; + get { + return overloadedData; + } + set { + throw new NotImplementedException (); + } } #endregion From 42f5055b47deea1d85d9d547a92db427165b07be Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 14 Sep 2012 23:35:58 +0200 Subject: [PATCH 36/37] Fix potential NullReferenceException in GetAccessibleMembers() if GetDefinition() returns null --- ICSharpCode.NRefactory.CSharp/Resolver/MemberLookup.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/MemberLookup.cs b/ICSharpCode.NRefactory.CSharp/Resolver/MemberLookup.cs index c490f9b72b..ead362d155 100644 --- a/ICSharpCode.NRefactory.CSharp/Resolver/MemberLookup.cs +++ b/ICSharpCode.NRefactory.CSharp/Resolver/MemberLookup.cs @@ -167,8 +167,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver List entities = new List(); entities.AddRange(type.GetMembers(options: GetMemberOptions.IgnoreInheritedMembers)); - if (!targetIsTypeParameter) - entities.AddRange(type.GetNestedTypes(options: GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions).Select(t => t.GetDefinition())); + if (!targetIsTypeParameter) { + var nestedTypes = type.GetNestedTypes(options: GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions); + // GetDefinition() might return null if some IType has a strange implementation of GetNestedTypes. + entities.AddRange(nestedTypes.Select(t => t.GetDefinition()).Where(td => td != null)); + } foreach (var entityGroup in entities.GroupBy(e => e.Name)) { From 875ee8d07947b04cabfd7134aed76a863651dfa7 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Fri, 14 Sep 2012 23:36:29 +0200 Subject: [PATCH 37/37] Add test: implicit long constant expression conversion to short --- ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs index d95d91605d..18b73ae011 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/ConversionsTest.cs @@ -397,6 +397,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver public void ImplicitLongConstantExpressionConversion() { Assert.IsFalse(IntegerLiteralConversion(0L, typeof(int))); + Assert.IsFalse(IntegerLiteralConversion(0L, typeof(short))); Assert.IsTrue(IntegerLiteralConversion(0L, typeof(long))); Assert.IsTrue(IntegerLiteralConversion(0L, typeof(ulong)));