From a5865bdd8e678de40e8b8a51424676da0b9f781f Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 27 Aug 2011 14:48:51 +0200 Subject: [PATCH] Implemented "Find References". --- .../CSDemo.Designer.cs | 21 +- ICSharpCode.NRefactory.Demo/CSDemo.cs | 52 +- .../CompositeResolveVisitorNavigator.cs | 54 ++ .../CSharp/Resolver/FindReferences.cs | 675 ++++++++++++++++++ .../CSharp/Resolver/ResolveVisitor.cs | 17 +- .../ICSharpCode.NRefactory.csproj | 2 + .../Utils/ReferenceComparer.cs | 2 +- 7 files changed, 814 insertions(+), 9 deletions(-) create mode 100644 ICSharpCode.NRefactory/CSharp/Resolver/CompositeResolveVisitorNavigator.cs create mode 100644 ICSharpCode.NRefactory/CSharp/Resolver/FindReferences.cs diff --git a/ICSharpCode.NRefactory.Demo/CSDemo.Designer.cs b/ICSharpCode.NRefactory.Demo/CSDemo.Designer.cs index c4315cc48f..079e531e11 100644 --- a/ICSharpCode.NRefactory.Demo/CSDemo.Designer.cs +++ b/ICSharpCode.NRefactory.Demo/CSDemo.Designer.cs @@ -48,6 +48,7 @@ namespace ICSharpCode.NRefactory.Demo { this.splitContainer1 = new System.Windows.Forms.SplitContainer(); this.csharpCodeTextBox = new System.Windows.Forms.TextBox(); + this.findReferencesButton = new System.Windows.Forms.Button(); this.resolveButton = new System.Windows.Forms.Button(); this.csharpTreeView = new System.Windows.Forms.TreeView(); this.csharpGenerateCodeButton = new System.Windows.Forms.Button(); @@ -71,6 +72,7 @@ namespace ICSharpCode.NRefactory.Demo // // splitContainer1.Panel2 // + this.splitContainer1.Panel2.Controls.Add(this.findReferencesButton); this.splitContainer1.Panel2.Controls.Add(this.resolveButton); this.splitContainer1.Panel2.Controls.Add(this.csharpTreeView); this.splitContainer1.Panel2.Controls.Add(this.csharpGenerateCodeButton); @@ -97,10 +99,22 @@ namespace ICSharpCode.NRefactory.Demo this.csharpCodeTextBox.WordWrap = false; this.csharpCodeTextBox.TextChanged += new System.EventHandler(this.CsharpCodeTextBoxTextChanged); // + // findReferencesButton + // + this.findReferencesButton.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.findReferencesButton.Enabled = false; + this.findReferencesButton.Location = new System.Drawing.Point(344, 4); + this.findReferencesButton.Name = "findReferencesButton"; + this.findReferencesButton.Size = new System.Drawing.Size(100, 23); + this.findReferencesButton.TabIndex = 4; + this.findReferencesButton.Text = "Find References"; + this.findReferencesButton.UseVisualStyleBackColor = true; + this.findReferencesButton.Click += new System.EventHandler(this.FindReferencesButtonClick); + // // resolveButton // this.resolveButton.Anchor = System.Windows.Forms.AnchorStyles.Top; - this.resolveButton.Location = new System.Drawing.Point(187, 3); + this.resolveButton.Location = new System.Drawing.Point(132, 4); this.resolveButton.Name = "resolveButton"; this.resolveButton.Size = new System.Drawing.Size(100, 23); this.resolveButton.TabIndex = 3; @@ -123,7 +137,7 @@ namespace ICSharpCode.NRefactory.Demo // csharpGenerateCodeButton // this.csharpGenerateCodeButton.Anchor = System.Windows.Forms.AnchorStyles.Top; - this.csharpGenerateCodeButton.Location = new System.Drawing.Point(293, 2); + this.csharpGenerateCodeButton.Location = new System.Drawing.Point(238, 4); this.csharpGenerateCodeButton.Name = "csharpGenerateCodeButton"; this.csharpGenerateCodeButton.Size = new System.Drawing.Size(100, 23); this.csharpGenerateCodeButton.TabIndex = 1; @@ -134,7 +148,7 @@ namespace ICSharpCode.NRefactory.Demo // csharpParseButton // this.csharpParseButton.Anchor = System.Windows.Forms.AnchorStyles.Top; - this.csharpParseButton.Location = new System.Drawing.Point(81, 3); + this.csharpParseButton.Location = new System.Drawing.Point(26, 4); this.csharpParseButton.Name = "csharpParseButton"; this.csharpParseButton.Size = new System.Drawing.Size(100, 23); this.csharpParseButton.TabIndex = 0; @@ -156,6 +170,7 @@ namespace ICSharpCode.NRefactory.Demo this.splitContainer1.ResumeLayout(false); this.ResumeLayout(false); } + private System.Windows.Forms.Button findReferencesButton; private System.Windows.Forms.Button csharpParseButton; private System.Windows.Forms.Button csharpGenerateCodeButton; private System.Windows.Forms.TreeView csharpTreeView; diff --git a/ICSharpCode.NRefactory.Demo/CSDemo.cs b/ICSharpCode.NRefactory.Demo/CSDemo.cs index 317da7977d..1ccb5de511 100644 --- a/ICSharpCode.NRefactory.Demo/CSDemo.cs +++ b/ICSharpCode.NRefactory.Demo/CSDemo.cs @@ -22,12 +22,12 @@ using System.ComponentModel; using System.Diagnostics; using System.Drawing; using System.IO; +using System.Linq; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; - using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp.Resolver; using ICSharpCode.NRefactory.TypeSystem; @@ -71,6 +71,7 @@ namespace ICSharpCode.NRefactory.Demo } SelectCurrentNode(csharpTreeView.Nodes); resolveButton.Enabled = true; + findReferencesButton.Enabled = true; } TreeNode MakeTreeNode(AstNode node) @@ -245,6 +246,55 @@ namespace ICSharpCode.NRefactory.Demo void CsharpCodeTextBoxTextChanged(object sender, EventArgs e) { resolveButton.Enabled = false; + findReferencesButton.Enabled = false; + } + + void FindReferencesButtonClick(object sender, EventArgs e) + { + if (csharpTreeView.SelectedNode == null) + return; + + SimpleProjectContent project = new SimpleProjectContent(); + var parsedFile = new TypeSystemConvertVisitor(project, "dummy.cs").Convert(compilationUnit); + project.UpdateProjectContent(null, parsedFile); + + List projects = new List(); + projects.Add(project); + projects.AddRange(builtInLibs.Value); + + using (var context = new CompositeTypeResolveContext(projects).Synchronize()) { + CSharpResolver resolver = new CSharpResolver(context); + + AstNode node = (AstNode)csharpTreeView.SelectedNode.Tag; + IResolveVisitorNavigator navigator = new NodeListResolveVisitorNavigator(new[] { node }); + ResolveVisitor visitor = new ResolveVisitor(resolver, parsedFile, navigator); + visitor.Scan(compilationUnit); + IEntity entity; + MemberResolveResult mrr = visitor.GetResolveResult(node) as MemberResolveResult; + TypeResolveResult trr = visitor.GetResolveResult(node) as TypeResolveResult; + if (mrr != null) { + entity = mrr.Member; + } else if (trr != null) { + entity = trr.Type.GetDefinition(); + } else { + return; + } + + FindReferences fr = new FindReferences(); + int referenceCount = 0; + fr.ReferenceFound += delegate { referenceCount++; }; + + var searchScopes = fr.GetSearchScopes(entity); + navigator = new CompositeResolveVisitorNavigator(searchScopes.ToArray()); + visitor = new ResolveVisitor(resolver, parsedFile, navigator); + visitor.Scan(compilationUnit); + + csharpTreeView.BeginUpdate(); + ShowResolveResultsInTree(csharpTreeView.Nodes, visitor); + csharpTreeView.EndUpdate(); + + MessageBox.Show("Found " + referenceCount + " references to " + entity.FullName); + } } } } diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/CompositeResolveVisitorNavigator.cs b/ICSharpCode.NRefactory/CSharp/Resolver/CompositeResolveVisitorNavigator.cs new file mode 100644 index 0000000000..18f5692e5e --- /dev/null +++ b/ICSharpCode.NRefactory/CSharp/Resolver/CompositeResolveVisitorNavigator.cs @@ -0,0 +1,54 @@ +// 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; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + public sealed class CompositeResolveVisitorNavigator : IResolveVisitorNavigator + { + IResolveVisitorNavigator[] navigators; + + public CompositeResolveVisitorNavigator(IResolveVisitorNavigator[] navigators) + { + this.navigators = navigators; + } + + public ResolveVisitorNavigationMode Scan(AstNode node) + { + ResolveVisitorNavigationMode mode = ResolveVisitorNavigationMode.Skip; + foreach (var navigator in navigators) { + ResolveVisitorNavigationMode newMode = navigator.Scan(node); + if (newMode == ResolveVisitorNavigationMode.ResolveAll) + return newMode; // ResolveAll has highest priority + if (newMode == ResolveVisitorNavigationMode.Resolve) + mode = newMode; // resolve has high priority and replaces the previous mode + else if (mode == ResolveVisitorNavigationMode.Skip) + mode = newMode; // skip has lowest priority and always gets replaced + } + return mode; + } + + public void Resolved(AstNode node, ResolveResult result) + { + foreach (var navigator in navigators) { + navigator.Resolved(node, result); + } + } + } +} diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/FindReferences.cs b/ICSharpCode.NRefactory/CSharp/Resolver/FindReferences.cs new file mode 100644 index 0000000000..149e283b15 --- /dev/null +++ b/ICSharpCode.NRefactory/CSharp/Resolver/FindReferences.cs @@ -0,0 +1,675 @@ +// 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.Linq; +using System.Threading; +using ICSharpCode.NRefactory.TypeSystem; + +namespace ICSharpCode.NRefactory.CSharp.Resolver +{ + /// + /// 'Find references' implementation. + /// + public class FindReferences + { + CancellationToken cancellationToken; + + /// + /// Callback that is invoked whenever a reference is found. + /// + public event Action ReferenceFound; + + #region Constructor + public FindReferences(CancellationToken cancellationToken = default(CancellationToken)) + { + this.cancellationToken = cancellationToken; + } + #endregion + + #region GetEffectiveAccessibility + /// + /// Gets the effective accessibility of the specified entity - + /// that is, the accessibility viewed from the top level. + /// + /// + /// internal member in public class -> internal + /// public member in internal class -> internal + /// protected member in public class -> protected + /// protected member in internal class -> protected and internal + /// + public static Accessibility GetEffectiveAccessibility(IEntity entity) + { + if (entity == null) + throw new ArgumentNullException("entity"); + Accessibility a = entity.Accessibility; + for (ITypeDefinition declType = entity.DeclaringTypeDefinition; declType != null; declType = declType.DeclaringTypeDefinition) { + a = MergeAccessibility(declType.Accessibility, a); + } + return a; + } + + static Accessibility MergeAccessibility(Accessibility outer, Accessibility inner) + { + if (outer == inner) + return inner; + if (outer == Accessibility.None || inner == Accessibility.None) + return Accessibility.None; + if (outer == Accessibility.Private || inner == Accessibility.Private) + return Accessibility.Private; + if (outer == Accessibility.Public) + return inner; + if (inner == Accessibility.Public) + return outer; + // Inner and outer are both in { protected, internal, protected and internal, protected or internal } + // (but they aren't both the same) + if (outer == Accessibility.ProtectedOrInternal) + return inner; + if (inner == Accessibility.ProtectedOrInternal) + return outer; + // Inner and outer are both in { protected, internal, protected and internal }, + // but aren't both the same, so the result is protected and internal. + return Accessibility.ProtectedAndInternal; + } + #endregion + + #region class SearchScope + public abstract class SearchScope : IResolveVisitorNavigator + { + protected string searchTerm; + internal Accessibility accessibility; + internal IEntity topLevelEntity; + internal FindReferences findReferences; + + /// + /// Gets the search term. Only files that contain this identifier need to be parsed. + /// Can return null if all files need to be parsed. + /// + public string SearchTerm { get { return searchTerm; } } + + /// + /// Gets the accessibility that defines the search scope. + /// + public Accessibility Accessibility { get { return accessibility; } } + + /// + /// Gets the top-level entity that defines the search scope. + /// + public IEntity TopLevelEntity { get { return topLevelEntity; } } + + internal abstract bool CanMatch(AstNode node); + internal abstract bool IsMatch(ResolveResult rr); + + ResolveVisitorNavigationMode IResolveVisitorNavigator.Scan(AstNode node) + { + if (CanMatch(node)) + return ResolveVisitorNavigationMode.Resolve; + else + return ResolveVisitorNavigationMode.Scan; + } + + void IResolveVisitorNavigator.Resolved(AstNode node, ResolveResult result) + { + if (CanMatch(node) && IsMatch(result)) { + var referenceFound = findReferences.ReferenceFound; + if (referenceFound != null) + referenceFound(node, result); + } + } + } + #endregion + + #region GetSearchScopes + public IList GetSearchScopes(IEntity entity) + { + if (entity == null) + throw new ArgumentNullException("entity"); + Accessibility effectiveAccessibility = GetEffectiveAccessibility(entity); + IEntity topLevelEntity = entity; + while (topLevelEntity.DeclaringTypeDefinition != null) + topLevelEntity = topLevelEntity.DeclaringTypeDefinition; + SearchScope scope; + SearchScope additionalScope = null; + switch (entity.EntityType) { + case EntityType.TypeDefinition: + scope = new FindTypeDefinitionReferences((ITypeDefinition)entity); + break; + case EntityType.Field: + scope = new FindFieldReferences((IField)entity); + break; + case EntityType.Property: + scope = new FindPropertyReferences((IProperty)entity); + break; + case EntityType.Event: + scope = new FindEventReferences((IEvent)entity); + break; + case EntityType.Method: + scope = GetSearchScopeForMethod((IMethod)entity); + break; + case EntityType.Indexer: + scope = new FindIndexerReferences((IProperty)entity); + break; + case EntityType.Operator: + scope = GetSearchScopeForOperator((IMethod)entity); + break; + case EntityType.Constructor: + IMethod ctor = (IMethod)entity; + scope = new FindObjectCreateReferences(ctor); + additionalScope = new FindChainedConstructorReferences(ctor); + break; + case EntityType.Destructor: + return EmptyList.Instance; + default: + throw new ArgumentException("Unknown entity type " + entity.EntityType); + } + if (scope.accessibility == Accessibility.None) + scope.accessibility = effectiveAccessibility; + scope.topLevelEntity = topLevelEntity; + scope.findReferences = this; + if (additionalScope != null) { + if (additionalScope.accessibility == Accessibility.None) + additionalScope.accessibility = effectiveAccessibility; + additionalScope.topLevelEntity = topLevelEntity; + additionalScope.findReferences = this; + return new[] { scope, additionalScope }; + } else { + return new[] { scope }; + } + } + #endregion + + #region FindReferencesInFile + /// + /// Finds all references in the given file. + /// + /// The search scopes for which to look. + /// The type system representation of the file being searched. + /// The compilation unit of the file being searched. + /// The type resolve context to use for resolving the file. + public void FindReferencesInFile(IList searchScopes, ParsedFile parsedFile, CompilationUnit compilationUnit, ITypeResolveContext context) + { + if (searchScopes == null) + throw new ArgumentNullException("searchScopes"); + if (parsedFile == null) + throw new ArgumentNullException("parsedFile"); + if (compilationUnit == null) + throw new ArgumentNullException("compilationUnit"); + if (context == null) + throw new ArgumentNullException("context"); + cancellationToken.ThrowIfCancellationRequested(); + if (searchScopes.Count == 0) + return; + foreach (SearchScope scope in searchScopes) { + if (scope.findReferences != this) + throw new ArgumentException("Cannot use a search scope that was created by another FindReferences instance"); + } + using (var ctx = context.Synchronize()) { + IResolveVisitorNavigator navigator; + if (searchScopes.Count == 1) + navigator = searchScopes[0]; + else + navigator = new CompositeResolveVisitorNavigator(searchScopes.ToArray()); + navigator = new DetectSkippableNodesNavigator(navigator, compilationUnit); + CSharpResolver resolver = new CSharpResolver(ctx, cancellationToken); + ResolveVisitor v = new ResolveVisitor(resolver, parsedFile, navigator); + v.Scan(compilationUnit); + } + } + #endregion + + #region Find TypeDefinition References + sealed class FindTypeDefinitionReferences : SearchScope + { + ITypeDefinition typeDefinition; + + public FindTypeDefinitionReferences(ITypeDefinition typeDefinition) + { + this.typeDefinition = typeDefinition; + if (ReflectionHelper.GetTypeCode(typeDefinition) == TypeCode.Empty) { + // not a built-in type + this.searchTerm = typeDefinition.Name; + } + } + + internal override bool CanMatch(AstNode node) + { + IdentifierExpression ident = node as IdentifierExpression; + if (ident != null) + return searchTerm == null || ident.Identifier == searchTerm; + + MemberReferenceExpression mre = node as MemberReferenceExpression; + if (mre != null) + return searchTerm == null || mre.MemberName == searchTerm; + + SimpleType st = node as SimpleType; + if (st != null) + return searchTerm == null || st.Identifier == searchTerm; + + MemberType mt = node as MemberType; + if (mt != null) + return searchTerm == null || mt.MemberName == searchTerm; + + if (searchTerm == null && node is PrimitiveType) + return true; + + return false; + } + + internal override bool IsMatch(ResolveResult rr) + { + TypeResolveResult trr = rr as TypeResolveResult; + return trr != null && typeDefinition.Equals(trr.Type.GetDefinition()); + } + } + #endregion + + #region Find Member References + class FindMemberReferences : SearchScope + { + readonly IMember member; + + public FindMemberReferences(IMember member) + { + this.member = member.MemberDefinition; + this.searchTerm = member.Name; + } + + internal override bool CanMatch(AstNode node) + { + IdentifierExpression ident = node as IdentifierExpression; + if (ident != null) + return ident.Identifier == searchTerm; + + MemberReferenceExpression mre = node as MemberReferenceExpression; + if (mre != null) + return mre.MemberName == searchTerm; + + PointerReferenceExpression pre = node as PointerReferenceExpression; + if (pre != null) + return pre.MemberName == searchTerm; + + NamedExpression ne = node as NamedExpression; + if (ne != null) + return ne.Identifier == searchTerm; + + return false; + } + + internal override bool IsMatch(ResolveResult rr) + { + MemberResolveResult mrr = rr as MemberResolveResult; + return mrr != null && member == mrr.Member.MemberDefinition; + } + } + + sealed class FindFieldReferences : FindMemberReferences + { + public FindFieldReferences(IField field) : base(field) + { + } + + internal override bool CanMatch(AstNode node) + { + return node is FieldDeclaration || base.CanMatch(node); + } + } + + sealed class FindPropertyReferences : FindMemberReferences + { + public FindPropertyReferences(IProperty property) : base(property) + { + } + + internal override bool CanMatch(AstNode node) + { + return node is PropertyDeclaration || base.CanMatch(node); + } + } + + sealed class FindEventReferences : FindMemberReferences + { + public FindEventReferences(IEvent ev) : base(ev) + { + } + + internal override bool CanMatch(AstNode node) + { + return node is EventDeclaration || base.CanMatch(node); + } + } + #endregion + + #region Find Method References + SearchScope GetSearchScopeForMethod(IMethod method) + { + switch (method.Name) { + case "Add": + return new FindMethodReferences(method, typeof(ArrayInitializerExpression)); + case "Where": + return new FindMethodReferences(method, typeof(QueryWhereClause)); + case "Select": + return new FindMethodReferences(method, typeof(QuerySelectClause)); + case "SelectMany": + return new FindMethodReferences(method, typeof(QueryFromClause)); + case "Join": + case "GroupJoin": + return new FindMethodReferences(method, typeof(QueryJoinClause)); + case "OrderBy": + case "OrderByDescending": + case "ThenBy": + case "ThenByDescending": + return new FindMethodReferences(method, typeof(QueryOrdering)); + case "GroupBy": + return new FindMethodReferences(method, typeof(QueryGroupClause)); + default: + return new FindMethodReferences(method); + } + } + + sealed class FindMethodReferences : SearchScope + { + readonly IMethod method; + readonly Type specialNodeType; + + public FindMethodReferences(IMethod method, Type specialNodeType = null) + { + this.method = (IMethod)method.MemberDefinition; + this.specialNodeType = specialNodeType; + if (specialNodeType == null) + this.searchTerm = method.Name; + } + + internal override bool CanMatch(AstNode node) + { + InvocationExpression ie = node as InvocationExpression; + if (ie != null) { + Expression target = ResolveVisitor.UnpackParenthesizedExpression(ie.Target); + + IdentifierExpression ident = target as IdentifierExpression; + if (ident != null) + return ident.Identifier == method.Name; + + MemberReferenceExpression mre = target as MemberReferenceExpression; + if (mre != null) + return mre.MemberName == method.Name; + + PointerReferenceExpression pre = target as PointerReferenceExpression; + if (pre != null) + return pre.MemberName == method.Name; + } + if (node is MethodDeclaration) + return true; + if (specialNodeType != null) + return specialNodeType.IsInstanceOfType(node); + else + return false; + } + + internal override bool IsMatch(ResolveResult rr) + { + MemberResolveResult mrr = rr as MemberResolveResult; + return mrr != null && method == mrr.Member.MemberDefinition; + } + } + #endregion + + #region Find Indexer References + sealed class FindIndexerReferences : SearchScope + { + readonly IProperty indexer; + + public FindIndexerReferences(IProperty indexer) + { + this.indexer = (IProperty)indexer.MemberDefinition; + } + + internal override bool CanMatch(AstNode node) + { + return node is IndexerExpression || node is IndexerDeclaration; + } + + internal override bool IsMatch(ResolveResult rr) + { + MemberResolveResult mrr = rr as MemberResolveResult; + return mrr != null && indexer == mrr.Member.MemberDefinition; + } + } + #endregion + + #region Find Operator References + SearchScope GetSearchScopeForOperator(IMethod op) + { + OperatorType? opType = OperatorDeclaration.GetOperatorType(op.Name); + if (opType == null) + return new FindMethodReferences(op); + switch (opType.Value) { + case OperatorType.LogicalNot: + return new FindUnaryOperator(op, UnaryOperatorType.Not); + case OperatorType.OnesComplement: + return new FindUnaryOperator(op, UnaryOperatorType.BitNot); + case OperatorType.UnaryPlus: + return new FindUnaryOperator(op, UnaryOperatorType.Plus); + case OperatorType.UnaryNegation: + return new FindUnaryOperator(op, UnaryOperatorType.Minus); + case OperatorType.Increment: + return new FindUnaryOperator(op, UnaryOperatorType.Increment); + case OperatorType.Decrement: + return new FindUnaryOperator(op, UnaryOperatorType.Decrement); + case OperatorType.True: + case OperatorType.False: + // TODO: implement search for op_True/op_False correctly + return new FindMethodReferences(op); + case OperatorType.Addition: + return new FindBinaryOperator(op, BinaryOperatorType.Add); + case OperatorType.Subtraction: + return new FindBinaryOperator(op, BinaryOperatorType.Subtract); + case OperatorType.Multiply: + return new FindBinaryOperator(op, BinaryOperatorType.Multiply); + case OperatorType.Division: + return new FindBinaryOperator(op, BinaryOperatorType.Divide); + case OperatorType.Modulus: + return new FindBinaryOperator(op, BinaryOperatorType.Modulus); + case OperatorType.BitwiseAnd: + // TODO: an overloaded bitwise operator can also be called using the corresponding logical operator + // (if op_True/op_False is defined) + return new FindBinaryOperator(op, BinaryOperatorType.BitwiseAnd); + case OperatorType.BitwiseOr: + return new FindBinaryOperator(op, BinaryOperatorType.BitwiseOr); + case OperatorType.ExclusiveOr: + return new FindBinaryOperator(op, BinaryOperatorType.ExclusiveOr); + case OperatorType.LeftShift: + return new FindBinaryOperator(op, BinaryOperatorType.ShiftLeft); + case OperatorType.RightShift: + return new FindBinaryOperator(op, BinaryOperatorType.ShiftRight); + case OperatorType.Equality: + return new FindBinaryOperator(op, BinaryOperatorType.Equality); + case OperatorType.Inequality: + return new FindBinaryOperator(op, BinaryOperatorType.InEquality); + case OperatorType.GreaterThan: + return new FindBinaryOperator(op, BinaryOperatorType.GreaterThan); + case OperatorType.LessThan: + return new FindBinaryOperator(op, BinaryOperatorType.LessThan); + case OperatorType.GreaterThanOrEqual: + return new FindBinaryOperator(op, BinaryOperatorType.GreaterThanOrEqual); + case OperatorType.LessThanOrEqual: + return new FindBinaryOperator(op, BinaryOperatorType.LessThanOrEqual); + case OperatorType.Implicit: + return new FindImplicitOperator(op); + case OperatorType.Explicit: + return new FindExplicitOperator(op); + default: + throw new InvalidOperationException("Invalid value for OperatorType"); + } + } + + sealed class FindUnaryOperator : SearchScope + { + readonly IMethod op; + readonly UnaryOperatorType operatorType; + + public FindUnaryOperator(IMethod op, UnaryOperatorType operatorType) + { + this.op = op; + this.operatorType = operatorType; + } + + internal override bool CanMatch(AstNode node) + { + UnaryOperatorExpression uoe = node as UnaryOperatorExpression; + if (uoe != null) { + if (operatorType == UnaryOperatorType.Increment) + return uoe.Operator == UnaryOperatorType.Increment || uoe.Operator == UnaryOperatorType.PostIncrement; + else if (operatorType == UnaryOperatorType.Decrement) + return uoe.Operator == UnaryOperatorType.Decrement || uoe.Operator == UnaryOperatorType.PostDecrement; + else + return uoe.Operator == operatorType; + } + return node is OperatorDeclaration; + } + + internal override bool IsMatch(ResolveResult rr) + { + MemberResolveResult mrr = rr as MemberResolveResult; + return mrr != null && op == mrr.Member.MemberDefinition; + } + } + + sealed class FindBinaryOperator : SearchScope + { + readonly IMethod op; + readonly BinaryOperatorType operatorType; + + public FindBinaryOperator(IMethod op, BinaryOperatorType operatorType) + { + this.op = op; + this.operatorType = operatorType; + } + + internal override bool CanMatch(AstNode node) + { + BinaryOperatorExpression boe = node as BinaryOperatorExpression; + if (boe != null) { + return boe.Operator == operatorType; + } + return node is OperatorDeclaration; + } + + internal override bool IsMatch(ResolveResult rr) + { + MemberResolveResult mrr = rr as MemberResolveResult; + return mrr != null && op == mrr.Member.MemberDefinition; + } + } + + sealed class FindImplicitOperator : SearchScope + { + readonly IMethod op; + + public FindImplicitOperator(IMethod op) + { + this.op = op; + } + + internal override bool CanMatch(AstNode node) + { + return true; + } + + internal override bool IsMatch(ResolveResult rr) + { + throw new NotImplementedException(); + } + } + + sealed class FindExplicitOperator : SearchScope + { + readonly IMethod op; + + public FindExplicitOperator(IMethod op) + { + this.op = op; + } + + internal override bool CanMatch(AstNode node) + { + return node is CastExpression; + } + + internal override bool IsMatch(ResolveResult rr) + { + ConversionResolveResult crr = rr as ConversionResolveResult; + return crr != null && crr.Conversion.IsUserDefined && crr.Conversion.Method.MemberDefinition == op; + } + } + #endregion + + #region Find Constructor References + sealed class FindObjectCreateReferences : SearchScope + { + readonly IMethod ctor; + + public FindObjectCreateReferences(IMethod ctor) + { + this.ctor = (IMethod)ctor.MemberDefinition; + if (ReflectionHelper.GetTypeCode(ctor.DeclaringTypeDefinition) == TypeCode.Empty) { + // not a built-in type + this.searchTerm = ctor.DeclaringTypeDefinition.Name; + } + } + + internal override bool CanMatch(AstNode node) + { + return node is ObjectCreateExpression || node is ConstructorDeclaration; + } + + internal override bool IsMatch(ResolveResult rr) + { + MemberResolveResult mrr = rr as MemberResolveResult; + return mrr != null && ctor == mrr.Member.MemberDefinition; + } + } + + sealed class FindChainedConstructorReferences : SearchScope + { + readonly IMethod ctor; + + public FindChainedConstructorReferences(IMethod ctor) + { + this.ctor = (IMethod)ctor.MemberDefinition; + if (ctor.DeclaringTypeDefinition.IsSealed) + this.accessibility = Accessibility.Private; + else + this.accessibility = Accessibility.Protected; + this.accessibility = MergeAccessibility(GetEffectiveAccessibility(ctor), this.accessibility); + } + + internal override bool CanMatch(AstNode node) + { + return node is ConstructorInitializer; + } + + internal override bool IsMatch(ResolveResult rr) + { + MemberResolveResult mrr = rr as MemberResolveResult; + return mrr != null && ctor == mrr.Member.MemberDefinition; + } + } + #endregion + } +} diff --git a/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs b/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs index f026784125..ccea11e630 100644 --- a/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs +++ b/ICSharpCode.NRefactory/CSharp/Resolver/ResolveVisitor.cs @@ -1881,10 +1881,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver AstNode parent = lambda.LambdaExpression.Parent; // Continue going upwards until we find a node that can be resolved and provides // an expected type. - while (parent is ParenthesizedExpression - || parent is CheckedExpression || parent is UncheckedExpression - || parent is NamedArgumentExpression || parent is ArrayInitializerExpression) - { + while (ActsAsParenthesizedExpression(parent) || parent is NamedArgumentExpression || parent is ArrayInitializerExpression) { parent = parent.Parent; } CSharpResolver storedResolver; @@ -1905,6 +1902,18 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver Log.Unindent(); Log.WriteLine("MergeUndecidedLambdas() finished."); } + + internal static bool ActsAsParenthesizedExpression(AstNode expression) + { + return expression is ParenthesizedExpression || expression is CheckedExpression || expression is UncheckedExpression; + } + + internal static Expression UnpackParenthesizedExpression(Expression expr) + { + while (ActsAsParenthesizedExpression(expr)) + expr = expr.GetChildByRole(ParenthesizedExpression.Roles.Expression); + return expr; + } #endregion #region AnalyzeLambda diff --git a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj index 3ac91f5a60..0e63ab8ae5 100644 --- a/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj +++ b/ICSharpCode.NRefactory/ICSharpCode.NRefactory.csproj @@ -101,7 +101,9 @@ + + diff --git a/ICSharpCode.NRefactory/Utils/ReferenceComparer.cs b/ICSharpCode.NRefactory/Utils/ReferenceComparer.cs index 079e837bf2..9e0e3b0df5 100644 --- a/ICSharpCode.NRefactory/Utils/ReferenceComparer.cs +++ b/ICSharpCode.NRefactory/Utils/ReferenceComparer.cs @@ -28,7 +28,7 @@ namespace ICSharpCode.NRefactory.Utils public new bool Equals(object a, object b) { - return ReferenceEquals(a, b); + return a == b; } public int GetHashCode(object obj)