From f74bf908bc7e3cdbd85315f118836688135eb344 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Thu, 15 Mar 2012 16:03:35 +0100 Subject: [PATCH] Make AstNode freezable. --- ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs | 87 +++++++++++++++---- .../Ast/CSharpModifierToken.cs | 6 +- .../Ast/CSharpTokenNode.cs | 1 + .../Ast/CompilationUnit.cs | 16 +++- .../Ast/DocumentationReference.cs | 30 ++++++- .../Expressions/AnonymousMethodExpression.cs | 12 ++- .../Ast/Expressions/EmptyExpression.cs | 1 + .../Ast/Expressions/LambdaExpression.cs | 7 +- .../Ast/Expressions/PrimitiveExpression.cs | 15 +++- .../Ast/GeneralScope/Comment.cs | 19 ++-- .../Ast/GeneralScope/PreProcessorDirective.cs | 1 + .../Ast/GeneralScope/TypeDeclaration.cs | 14 +-- .../GeneralScope/TypeParameterDeclaration.cs | 5 +- .../Ast/Identifier.cs | 49 +++++------ .../Ast/MemberType.cs | 12 ++- .../Ast/PrimitiveType.cs | 25 ++++-- ICSharpCode.NRefactory.CSharp/Ast/Roles.cs | 5 +- .../Ast/Statements/EmptyStatement.cs | 1 + .../Ast/TypeMembers/OperatorDeclaration.cs | 9 +- .../Ast/TypeMembers/ParameterDeclaration.cs | 9 +- .../ContextAction/TestRefactoringContext.cs | 1 + .../CSharp/Resolver/FindReferencesTest.cs | 18 ++++ ICSharpCode.NRefactory/Role.cs | 31 ++++++- 23 files changed, 280 insertions(+), 94 deletions(-) diff --git a/ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs b/ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs index 141dc67795..c4cb486df3 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/AstNode.cs @@ -33,12 +33,10 @@ using System.Threading; namespace ICSharpCode.NRefactory.CSharp { - public abstract class AstNode : AbstractAnnotatable, PatternMatching.INode + public abstract class AstNode : AbstractAnnotatable, ICSharpCode.NRefactory.TypeSystem.IFreezable, PatternMatching.INode { - /// - /// Root of an abstract syntax tree. - /// - public static readonly Role Root = new Role ("Root"); + // the Root role must be available when creating the null nodes, so we can't put it in the Roles class + internal static readonly Role RootRole = new Role ("Root"); #region Null public static readonly AstNode Null = new NullAstNode (); @@ -129,7 +127,42 @@ namespace ICSharpCode.NRefactory.CSharp AstNode nextSibling; AstNode firstChild; AstNode lastChild; - Role role = Root; + + // Flags, from least significant to most significant bits: + // - Role.RoleIndexBits: role index + // - 1 bit: IsFrozen + protected uint flags = RootRole.Index; + // Derived classes may also use a few bits, + // for example Identifier uses 1 bit for IsVerbatim + + const uint roleIndexMask = (1u << Role.RoleIndexBits) - 1; + const uint frozenBit = 1u << Role.RoleIndexBits; + protected const int AstNodeFlagsUsedBits = Role.RoleIndexBits + 1; + + protected AstNode() + { + if (IsNull) + Freeze(); + } + + public bool IsFrozen { + get { return (flags & frozenBit) != 0; } + } + + public void Freeze() + { + if (!IsFrozen) { + for (AstNode child = firstChild; child != null; child = child.nextSibling) + child.Freeze(); + flags |= frozenBit; + } + } + + protected void ThrowIfFrozen() + { + if (IsFrozen) + throw new InvalidOperationException("Cannot mutate frozen " + GetType().Name); + } public abstract NodeType NodeType { get; @@ -176,16 +209,24 @@ namespace ICSharpCode.NRefactory.CSharp } public Role Role { - get { return role; } + get { + return Role.GetByIndex(flags & roleIndexMask); + } set { if (value == null) throw new ArgumentNullException("value"); if (!value.IsValid(this)) throw new ArgumentException("This node is not valid in the new role."); - role = value; + ThrowIfFrozen(); + SetRole(value); } } + void SetRole(Role role) + { + flags = (flags & ~roleIndexMask) | role.Index; + } + public AstNode NextSibling { get { return nextSibling; } } @@ -258,8 +299,9 @@ namespace ICSharpCode.NRefactory.CSharp { if (role == null) throw new ArgumentNullException ("role"); + uint roleIndex = role.Index; for (var cur = firstChild; cur != null; cur = cur.nextSibling) { - if (cur.role == role) + if ((cur.flags & roleIndexMask) == roleIndex) return (T)cur; } return role.NullObject; @@ -285,10 +327,11 @@ namespace ICSharpCode.NRefactory.CSharp throw new ArgumentNullException ("role"); if (child == null || child.IsNull) return; - if (this.IsNull) - throw new InvalidOperationException ("Cannot add children to null nodes"); + ThrowIfFrozen(); if (child.parent != null) throw new ArgumentException ("Node is already used in another tree.", "child"); + if (child.IsFrozen) + throw new ArgumentException ("Cannot add a frozen node.", "child"); AddChildUnsafe (child, role); } @@ -298,7 +341,7 @@ namespace ICSharpCode.NRefactory.CSharp void AddChildUnsafe (AstNode child, Role role) { child.parent = this; - child.role = role; + child.SetRole(role); if (firstChild == null) { lastChild = firstChild = child; } else { @@ -319,8 +362,11 @@ namespace ICSharpCode.NRefactory.CSharp if (child == null || child.IsNull) return; + ThrowIfFrozen(); if (child.parent != null) throw new ArgumentException ("Node is already used in another tree.", "child"); + if (child.IsFrozen) + throw new ArgumentException ("Cannot add a frozen node.", "child"); if (nextSibling.parent != this) throw new ArgumentException ("NextSibling is not a child of this node.", "nextSibling"); // No need to test for "Cannot add children to null nodes", @@ -331,7 +377,7 @@ namespace ICSharpCode.NRefactory.CSharp void InsertChildBeforeUnsafe (AstNode nextSibling, AstNode child, Role role) { child.parent = this; - child.role = role; + child.SetRole(role); child.nextSibling = nextSibling; child.prevSibling = nextSibling.prevSibling; @@ -356,6 +402,7 @@ namespace ICSharpCode.NRefactory.CSharp public void Remove () { if (parent != null) { + ThrowIfFrozen(); if (prevSibling != null) { Debug.Assert (prevSibling.nextSibling == this); prevSibling.nextSibling = nextSibling; @@ -390,10 +437,11 @@ namespace ICSharpCode.NRefactory.CSharp if (parent == null) { throw new InvalidOperationException (this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node"); } + ThrowIfFrozen(); // Because this method doesn't statically check the new node's type with the role, // we perform a runtime test: - if (!role.IsValid (newNode)) { - throw new ArgumentException (string.Format ("The new node '{0}' is not valid in the role {1}", newNode.GetType ().Name, role.ToString ()), "newNode"); + if (!this.Role.IsValid (newNode)) { + throw new ArgumentException (string.Format ("The new node '{0}' is not valid in the role {1}", newNode.GetType ().Name, this.Role.ToString ()), "newNode"); } if (newNode.parent != null) { // newNode is used within this tree? @@ -405,9 +453,11 @@ namespace ICSharpCode.NRefactory.CSharp throw new ArgumentException ("Node is already used in another tree.", "newNode"); } } + if (newNode.IsFrozen) + throw new ArgumentException ("Cannot add a frozen node.", "newNode"); newNode.parent = parent; - newNode.role = role; + newNode.SetRole(this.Role); newNode.prevSibling = prevSibling; newNode.nextSibling = nextSibling; if (parent != null) { @@ -440,7 +490,7 @@ namespace ICSharpCode.NRefactory.CSharp } AstNode oldParent = parent; AstNode oldSuccessor = nextSibling; - Role oldRole = role; + Role oldRole = this.Role; Remove (); AstNode replacement = replaceFunction (this); if (oldSuccessor != null && oldSuccessor.parent != oldParent) @@ -473,10 +523,11 @@ namespace ICSharpCode.NRefactory.CSharp copy.lastChild = null; copy.prevSibling = null; copy.nextSibling = null; + copy.flags &= ~frozenBit; // unfreeze the copy // Then perform a deep copy: for (AstNode cur = firstChild; cur != null; cur = cur.nextSibling) { - copy.AddChildUnsafe (cur.Clone (), cur.role); + copy.AddChildUnsafe (cur.Clone (), cur.Role); } // Finally, clone the annotation, if necessary diff --git a/ICSharpCode.NRefactory.CSharp/Ast/CSharpModifierToken.cs b/ICSharpCode.NRefactory.CSharp/Ast/CSharpModifierToken.cs index 01bc7100ac..ac59c72460 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/CSharpModifierToken.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/CSharpModifierToken.cs @@ -29,14 +29,16 @@ using System.Linq; namespace ICSharpCode.NRefactory.CSharp { - public class CSharpModifierToken : CSharpTokenNode { Modifiers modifier; public Modifiers Modifier { get { return modifier; } - set { this.modifier = value; } + set { + ThrowIfFrozen(); + this.modifier = value; + } } protected override int TokenLength { diff --git a/ICSharpCode.NRefactory.CSharp/Ast/CSharpTokenNode.cs b/ICSharpCode.NRefactory.CSharp/Ast/CSharpTokenNode.cs index 504acfd272..0544b29280 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/CSharpTokenNode.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/CSharpTokenNode.cs @@ -103,6 +103,7 @@ namespace ICSharpCode.NRefactory.CSharp #region IRelocationable implementation void IRelocatable.SetStartLocation (TextLocation startLocation) { + ThrowIfFrozen(); this.startLocation = startLocation; } #endregion diff --git a/ICSharpCode.NRefactory.CSharp/Ast/CompilationUnit.cs b/ICSharpCode.NRefactory.CSharp/Ast/CompilationUnit.cs index a58eb16212..55ab8a30ec 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/CompilationUnit.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/CompilationUnit.cs @@ -1,4 +1,4 @@ -// +// // CompilationUnit.cs // // Author: @@ -45,10 +45,18 @@ namespace ICSharpCode.NRefactory.CSharp } } + string fileName; + /// /// Gets/Sets the file name of this compilation unit. /// - public string FileName { get; set; } + public string FileName { + get { return fileName; } + set { + ThrowIfFrozen(); + fileName = value; + } + } List errors = new List (); @@ -83,7 +91,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 && includeInnerTypes))) nodeStack.Push (child); } } @@ -99,7 +107,7 @@ namespace ICSharpCode.NRefactory.CSharp { visitor.VisitCompilationUnit (this); } - + public override T AcceptVisitor (IAstVisitor visitor) { return visitor.VisitCompilationUnit (this); diff --git a/ICSharpCode.NRefactory.CSharp/Ast/DocumentationReference.cs b/ICSharpCode.NRefactory.CSharp/Ast/DocumentationReference.cs index 93fcd38dbc..9c599ce6b3 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/DocumentationReference.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/DocumentationReference.cs @@ -29,6 +29,10 @@ namespace ICSharpCode.NRefactory.CSharp public static readonly Role DeclaringTypeRole = new Role("DeclaringType", AstType.Null); public static readonly Role ConversionOperatorReturnTypeRole = new Role("ConversionOperatorReturnType", AstType.Null); + EntityType entityType; + OperatorType operatorType; + bool hasParameterList; + /// /// Gets/Sets the entity type. /// Possible values are: @@ -37,18 +41,36 @@ namespace ICSharpCode.NRefactory.CSharp /// EntityType.TypeDefinition for references to primitive types, /// and EntityType.None for everything else. /// - public EntityType EntityType { get; set; } + public EntityType EntityType { + get { return entityType; } + set { + ThrowIfFrozen(); + entityType = value; + } + } /// /// Gets/Sets the operator type. /// This property is only used when EntityType==Operator. /// - public OperatorType OperatorType { get; set; } + public OperatorType OperatorType { + get { return operatorType; } + set { + ThrowIfFrozen(); + operatorType = value; + } + } /// /// Gets/Sets whether a parameter list was provided. /// - public bool HasParameterList { get; set; } + public bool HasParameterList { + get { return hasParameterList; } + set { + ThrowIfFrozen(); + hasParameterList = value; + } + } public override NodeType NodeType { get { return NodeType.Unknown; } @@ -113,7 +135,7 @@ namespace ICSharpCode.NRefactory.CSharp { visitor.VisitDocumentationReference (this); } - + public override T AcceptVisitor (IAstVisitor visitor) { return visitor.VisitDocumentationReference (this); diff --git a/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AnonymousMethodExpression.cs b/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AnonymousMethodExpression.cs index df7340dbea..07de16197a 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AnonymousMethodExpression.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/Expressions/AnonymousMethodExpression.cs @@ -37,11 +37,19 @@ namespace ICSharpCode.NRefactory.CSharp public readonly static TokenRole DelegateKeywordRole = new TokenRole ("delegate"); public readonly static TokenRole AsyncModifierRole = LambdaExpression.AsyncModifierRole; - public bool IsAsync { get; set; } + bool isAsync; + + public bool IsAsync { + get { return isAsync; } + set { ThrowIfFrozen(); isAsync = value; } + } // used to tell the difference between delegate {} and delegate () {} + bool hasParameterList; + public bool HasParameterList { - get; set; + get { return hasParameterList; } + set { ThrowIfFrozen(); hasParameterList = value; } } public CSharpTokenNode DelegateToken { diff --git a/ICSharpCode.NRefactory.CSharp/Ast/Expressions/EmptyExpression.cs b/ICSharpCode.NRefactory.CSharp/Ast/Expressions/EmptyExpression.cs index 6230164ca9..20c5135345 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/Expressions/EmptyExpression.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/Expressions/EmptyExpression.cs @@ -58,6 +58,7 @@ namespace ICSharpCode.NRefactory.CSharp #region IRelocationable implementation void IRelocatable.SetStartLocation (TextLocation startLocation) { + ThrowIfFrozen(); this.location = startLocation; } #endregion diff --git a/ICSharpCode.NRefactory.CSharp/Ast/Expressions/LambdaExpression.cs b/ICSharpCode.NRefactory.CSharp/Ast/Expressions/LambdaExpression.cs index e9779d7525..001ffcb323 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/Expressions/LambdaExpression.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/Expressions/LambdaExpression.cs @@ -37,7 +37,12 @@ namespace ICSharpCode.NRefactory.CSharp public readonly static TokenRole ArrowRole = new TokenRole ("=>"); public static readonly Role BodyRole = new Role("Body", AstNode.Null); - public bool IsAsync { get; set; } + bool isAsync; + + public bool IsAsync { + get { return isAsync; } + set { ThrowIfFrozen(); isAsync = value; } + } public AstNodeCollection Parameters { get { return GetChildrenByRole (Roles.Parameter); } diff --git a/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PrimitiveExpression.cs b/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PrimitiveExpression.cs index 5672ad8a63..97bd084365 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PrimitiveExpression.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/Expressions/PrimitiveExpression.cs @@ -47,14 +47,21 @@ namespace ICSharpCode.NRefactory.CSharp } } + object value; + public object Value { - get; - set; + get { return this.value; } + set { + ThrowIfFrozen(); + this.value = value; + } } public string LiteralValue { - get { - return literalValue; + get { return literalValue; } + set { + ThrowIfFrozen(); + literalValue = value; } } diff --git a/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Comment.cs b/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Comment.cs index 98b886cda6..d9c786d2af 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Comment.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/Comment.cs @@ -58,19 +58,25 @@ namespace ICSharpCode.NRefactory.CSharp } } + CommentType commentType; + public CommentType CommentType { - get; - set; + get { return commentType; } + set { ThrowIfFrozen(); commentType = value; } } + bool startsLine; + public bool StartsLine { - get; - set; + get { return startsLine; } + set { ThrowIfFrozen(); startsLine = value; } } + string content; + public string Content { - get; - set; + get { return content; } + set { ThrowIfFrozen(); content = value; } } TextLocation startLocation; @@ -103,6 +109,7 @@ namespace ICSharpCode.NRefactory.CSharp #region IRelocationable implementation void IRelocatable.SetStartLocation (TextLocation startLocation) { + ThrowIfFrozen(); int lineDelta = startLocation.Line - this.startLocation.Line; endLocation = new TextLocation (endLocation.Line + lineDelta, lineDelta != 0 ? endLocation.Column : endLocation.Column + startLocation.Column - this.startLocation.Column); this.startLocation = startLocation; diff --git a/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/PreProcessorDirective.cs b/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/PreProcessorDirective.cs index f2c2073e0f..81959aa9b7 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/PreProcessorDirective.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/PreProcessorDirective.cs @@ -96,6 +96,7 @@ namespace ICSharpCode.NRefactory.CSharp #region IRelocationable implementation void IRelocatable.SetStartLocation (TextLocation startLocation) { + ThrowIfFrozen(); int lineDelta = startLocation.Line - this.startLocation.Line; endLocation = new TextLocation (endLocation.Line + lineDelta, lineDelta != 0 ? endLocation.Column : endLocation.Column + startLocation.Column - this.startLocation.Column); this.startLocation = startLocation; diff --git a/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeDeclaration.cs b/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeDeclaration.cs index 73a203d88a..c6c25f5e25 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeDeclaration.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeDeclaration.cs @@ -114,17 +114,17 @@ namespace ICSharpCode.NRefactory.CSharp public static TypeDeclaration Create(ClassType type) { - switch (type) { - case ICSharpCode.NRefactory.CSharp.ClassType.Class: + switch (type) { + case ICSharpCode.NRefactory.CSharp.ClassType.Class: return new Class (); - case ICSharpCode.NRefactory.CSharp.ClassType.Struct: + case ICSharpCode.NRefactory.CSharp.ClassType.Struct: return new Struct (); - case ICSharpCode.NRefactory.CSharp.ClassType.Interface: + case ICSharpCode.NRefactory.CSharp.ClassType.Interface: return new Interface (); - case ICSharpCode.NRefactory.CSharp.ClassType.Enum: + case ICSharpCode.NRefactory.CSharp.ClassType.Enum: return new Enum (); - default: - throw new System.ArgumentOutOfRangeException(); + default: + throw new System.ArgumentOutOfRangeException(); } } diff --git a/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeParameterDeclaration.cs b/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeParameterDeclaration.cs index c0493976d9..a064dcda80 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeParameterDeclaration.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/GeneralScope/TypeParameterDeclaration.cs @@ -42,8 +42,11 @@ namespace ICSharpCode.NRefactory.CSharp get { return GetChildrenByRole (AttributeRole); } } + VarianceModifier variance; + public VarianceModifier Variance { - get; set; + get { return variance; } + set { ThrowIfFrozen(); variance = value; } } public CSharpTokenNode VarianceToken { diff --git a/ICSharpCode.NRefactory.CSharp/Ast/Identifier.cs b/ICSharpCode.NRefactory.CSharp/Ast/Identifier.cs index 47547fee13..fe63a8c8f8 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/Identifier.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/Identifier.cs @@ -1,6 +1,6 @@ // // Identifier.cs -// +// // Author: // Mike Krüger // @@ -42,7 +42,7 @@ namespace ICSharpCode.NRefactory.CSharp public override void AcceptVisitor (IAstVisitor visitor) { } - + public override T AcceptVisitor (IAstVisitor visitor) { return default (T); @@ -68,9 +68,10 @@ namespace ICSharpCode.NRefactory.CSharp string name; public string Name { get { return this.name; } - set { + set { if (value == null) throw new ArgumentNullException("value"); + ThrowIfFrozen(); this.name = value; } } @@ -80,25 +81,34 @@ namespace ICSharpCode.NRefactory.CSharp get { return startLocation; } - } - public virtual bool IsVerbatim { + const uint verbatimBit = 1u << AstNodeFlagsUsedBits; + + public bool IsVerbatim { get { - return false; + return (flags & verbatimBit) != 0; + } + set { + ThrowIfFrozen(); + if (value) + flags |= verbatimBit; + else + flags &= ~verbatimBit; } } #region IRelocationable implementation void IRelocatable.SetStartLocation (TextLocation startLocation) { + ThrowIfFrozen(); this.startLocation = startLocation; } #endregion public override TextLocation EndLocation { get { - return new TextLocation (StartLocation.Line, StartLocation.Column + (Name ?? "").Length); + return new TextLocation (StartLocation.Line, StartLocation.Column + (Name ?? "").Length + (IsVerbatim ? 1 : 0)); } } @@ -125,7 +135,7 @@ namespace ICSharpCode.NRefactory.CSharp if (string.IsNullOrEmpty(name)) return Identifier.Null; if (name[0] == '@') - return new VerbatimIdentifier(name.Substring (1), location); + return new Identifier (name.Substring (1), location) { IsVerbatim = true }; else return new Identifier (name, location); } @@ -136,7 +146,7 @@ namespace ICSharpCode.NRefactory.CSharp return Identifier.Null; if (isVerbatim) - return new VerbatimIdentifier (name, location); + return new Identifier (name, location) { IsVerbatim = true }; return new Identifier (name, location); } @@ -144,7 +154,7 @@ namespace ICSharpCode.NRefactory.CSharp { visitor.VisitIdentifier (this); } - + public override T AcceptVisitor (IAstVisitor visitor) { return visitor.VisitIdentifier (this); @@ -160,24 +170,5 @@ namespace ICSharpCode.NRefactory.CSharp Identifier o = other as Identifier; return o != null && !o.IsNull && MatchString(this.Name, o.Name); } - - class VerbatimIdentifier : Identifier - { - public override TextLocation EndLocation { - get { - return new TextLocation (StartLocation.Line, StartLocation.Column + (Name ?? "").Length + 1); // @"..." - } - } - - public override bool IsVerbatim { - get { - return true; - } - } - - public VerbatimIdentifier(string name, TextLocation location) : base (name, location) - { - } - } } } \ No newline at end of file diff --git a/ICSharpCode.NRefactory.CSharp/Ast/MemberType.cs b/ICSharpCode.NRefactory.CSharp/Ast/MemberType.cs index 9309bc6b7e..b017d45ba7 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/MemberType.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/MemberType.cs @@ -37,7 +37,15 @@ namespace ICSharpCode.NRefactory.CSharp { public static readonly Role TargetRole = new Role("Target", AstType.Null); - public bool IsDoubleColon { get; set; } + bool isDoubleColon; + + public bool IsDoubleColon { + get { return isDoubleColon; } + set { + ThrowIfFrozen(); + isDoubleColon = value; + } + } public AstType Target { get { return GetChildByRole(TargetRole); } @@ -93,7 +101,7 @@ namespace ICSharpCode.NRefactory.CSharp { visitor.VisitMemberType (this); } - + public override T AcceptVisitor (IAstVisitor visitor) { return visitor.VisitMemberType (this); diff --git a/ICSharpCode.NRefactory.CSharp/Ast/PrimitiveType.cs b/ICSharpCode.NRefactory.CSharp/Ast/PrimitiveType.cs index ed067b2ac5..18cebf77b4 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/PrimitiveType.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/PrimitiveType.cs @@ -34,8 +34,18 @@ namespace ICSharpCode.NRefactory.CSharp { public class PrimitiveType : AstType, IRelocatable { - public string Keyword { get; set; } - public TextLocation Location { get; set; } + TextLocation location; + string keyword = string.Empty; + + public string Keyword { + get { return keyword; } + set { + if (value == null) + throw new ArgumentNullException(); + ThrowIfFrozen(); + keyword = value; + } + } public KnownTypeCode KnownTypeCode { get { return GetTypeCodeForPrimitiveType(this.Keyword); } @@ -53,17 +63,17 @@ namespace ICSharpCode.NRefactory.CSharp public PrimitiveType(string keyword, TextLocation location) { this.Keyword = keyword; - this.Location = location; + this.location = location; } public override TextLocation StartLocation { get { - return Location; + return location; } } public override TextLocation EndLocation { get { - return new TextLocation (Location.Line, Location.Column + (Keyword != null ? Keyword.Length : 0)); + return new TextLocation (location.Line, location.Column + keyword.Length); } } @@ -71,7 +81,8 @@ namespace ICSharpCode.NRefactory.CSharp #region IRelocationable implementation void IRelocatable.SetStartLocation (TextLocation startLocation) { - this.Location = startLocation; + ThrowIfFrozen(); + this.location = startLocation; } #endregion @@ -98,7 +109,7 @@ namespace ICSharpCode.NRefactory.CSharp public override string ToString() { - return Keyword ?? base.ToString(); + return Keyword; } public override ITypeReference ToTypeReference(SimpleNameLookupMode lookupMode = SimpleNameLookupMode.Type) diff --git a/ICSharpCode.NRefactory.CSharp/Ast/Roles.cs b/ICSharpCode.NRefactory.CSharp/Ast/Roles.cs index 98be008b56..f958e24659 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/Roles.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/Roles.cs @@ -1,4 +1,4 @@ -// +// // Roles.cs // // Author: @@ -30,7 +30,8 @@ namespace ICSharpCode.NRefactory.CSharp { public static class Roles { - + public static readonly Role Root = AstNode.RootRole; + // some pre defined constants for common roles public static readonly Role Identifier = new Role ("Identifier", CSharp.Identifier.Null); public static readonly Role Body = new Role ("Body", CSharp.BlockStatement.Null); diff --git a/ICSharpCode.NRefactory.CSharp/Ast/Statements/EmptyStatement.cs b/ICSharpCode.NRefactory.CSharp/Ast/Statements/EmptyStatement.cs index 623d851c3e..e85833953d 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/Statements/EmptyStatement.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/Statements/EmptyStatement.cs @@ -51,6 +51,7 @@ namespace ICSharpCode.NRefactory.CSharp #region IRelocationable implementation void IRelocatable.SetStartLocation (TextLocation startLocation) { + ThrowIfFrozen(); this.Location = startLocation; } #endregion diff --git a/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/OperatorDeclaration.cs b/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/OperatorDeclaration.cs index abfa1e937a..8e09673577 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/OperatorDeclaration.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/OperatorDeclaration.cs @@ -109,9 +109,14 @@ namespace ICSharpCode.NRefactory.CSharp get { return EntityType.Operator; } } + OperatorType operatorType; + public OperatorType OperatorType { - get; - set; + get { return operatorType; } + set { + ThrowIfFrozen(); + operatorType = value; + } } public CSharpTokenNode OperatorToken { diff --git a/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/ParameterDeclaration.cs b/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/ParameterDeclaration.cs index 5be4528283..0942c6dc34 100644 --- a/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/ParameterDeclaration.cs +++ b/ICSharpCode.NRefactory.CSharp/Ast/TypeMembers/ParameterDeclaration.cs @@ -56,9 +56,14 @@ namespace ICSharpCode.NRefactory.CSharp get { return GetChildrenByRole (AttributeRole); } } + ParameterModifier parameterModifier; + public ParameterModifier ParameterModifier { - get; - set; + get { return parameterModifier; } + set { + ThrowIfFrozen(); + parameterModifier = value; + } } public AstType Type { diff --git a/ICSharpCode.NRefactory.Tests/CSharp/ContextAction/TestRefactoringContext.cs b/ICSharpCode.NRefactory.Tests/CSharp/ContextAction/TestRefactoringContext.cs index fd76c209b6..d8d68a3bf3 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/ContextAction/TestRefactoringContext.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/ContextAction/TestRefactoringContext.cs @@ -137,6 +137,7 @@ namespace ICSharpCode.NRefactory.CSharp.ContextActions if (parser.HasErrors) parser.ErrorPrinter.Errors.ForEach (e => Console.WriteLine (e.Message)); Assert.IsFalse (parser.HasErrors, "File contains parsing errors."); + unit.Freeze(); var parsedFile = unit.ToTypeSystem(); IProjectContent pc = new CSharpProjectContent(); diff --git a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/FindReferencesTest.cs b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/FindReferencesTest.cs index ef10098665..30b76d561e 100644 --- a/ICSharpCode.NRefactory.Tests/CSharp/Resolver/FindReferencesTest.cs +++ b/ICSharpCode.NRefactory.Tests/CSharp/Resolver/FindReferencesTest.cs @@ -129,5 +129,23 @@ class Test { Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 3 && r is MethodDeclaration)); Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 8 && r is ForeachStatement)); } + + [Test] + public void FindReferencesForOpImplicitInLocalVariableInitialization() + { + Init(@"using System; +class Test { + static void T() { + int x = new Test(); + } + public static implicit operator int(Test x) { return 0; } +}"); + var test = compilation.MainAssembly.TopLevelTypeDefinitions.Single(t => t.Name == "Test"); + var opImplicit = test.Methods.Single(m => m.Name == "op_Implicit"); + var actual = FindReferences(opImplicit).ToList(); + Assert.AreEqual(2, actual.Count); + Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 4 && r is ObjectCreateExpression)); + Assert.IsTrue(actual.Any(r => r.StartLocation.Line == 6 && r is OperatorDeclaration)); + } } } diff --git a/ICSharpCode.NRefactory/Role.cs b/ICSharpCode.NRefactory/Role.cs index cde1462e7a..da923f3629 100644 --- a/ICSharpCode.NRefactory/Role.cs +++ b/ICSharpCode.NRefactory/Role.cs @@ -17,6 +17,7 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Threading; namespace ICSharpCode.NRefactory { @@ -25,12 +26,40 @@ namespace ICSharpCode.NRefactory /// public abstract class Role { - internal Role() {} // don't allow NRefactory consumers to derive from Role + public const int RoleIndexBits = 9; + + static readonly Role[] roles = new Role[1 << RoleIndexBits]; + static int nextRoleIndex = 0; + + readonly uint index; + + [CLSCompliant(false)] + public uint Index { + get { return index; } + } + + // don't allow NRefactory consumers to derive from Role + internal Role() + { + this.index = (uint)Interlocked.Increment(ref nextRoleIndex); + if (this.index >= roles.Length) + throw new InvalidOperationException(""); + roles[this.index] = this; + } /// /// Gets whether the specified node is valid in this role. /// public abstract bool IsValid(object node); + + /// + /// Gets the role with the specified index. + /// + [CLSCompliant(false)] + public static Role GetByIndex(uint index) + { + return roles[index]; + } } ///